1 package goja
2
3 import (
4 "testing"
5 )
6
7 func TestSparseArraySetLengthWithPropItems(t *testing.T) {
8 const SCRIPT = `
9 var a = [1,2,3,4];
10 a[100000] = 5;
11 var thrown = false;
12
13 Object.defineProperty(a, "2", {value: 42, configurable: false, writable: false});
14 try {
15 Object.defineProperty(a, "length", {value: 0, writable: false});
16 } catch (e) {
17 thrown = e instanceof TypeError;
18 }
19 thrown && a.length === 3;
20 `
21
22 testScript(SCRIPT, valueTrue, t)
23 }
24
25 func TestSparseArraySwitch(t *testing.T) {
26 vm := New()
27 _, err := vm.RunString(`
28 var a = [];
29 a[20470] = 5; // switch to sparse`)
30 if err != nil {
31 t.Fatal(err)
32 }
33 a := vm.Get("a").(*Object)
34 if _, ok := a.self.(*sparseArrayObject); !ok {
35 t.Fatal("1: array is not sparse")
36 }
37 _, err = vm.RunString(`
38 var cutoffIdx = Math.round(20470 - 20470/8);
39 for (var i = a.length - 1; i >= cutoffIdx; i--) {
40 a[i] = i;
41 }
42
43 // At this point it will have switched to a normal array
44 if (a.length != 20471) {
45 throw new Error("Invalid length: " + a.length);
46 }
47
48 for (var i = 0; i < cutoffIdx; i++) {
49 if (a[i] !== undefined) {
50 throw new Error("Invalid value at " + i + ": " + a[i]);
51 }
52 }
53
54 for (var i = cutoffIdx; i < a.length; i++) {
55 if (a[i] !== i) {
56 throw new Error("Invalid value at " + i + ": " + a[i]);
57 }
58 }`)
59 if err != nil {
60 t.Fatal(err)
61 }
62 if _, ok := a.self.(*arrayObject); !ok {
63 t.Fatal("2: array is not normal")
64 }
65 _, err = vm.RunString(`
66 // Now try to expand. Should stay a normal array
67 a[20471] = 20471;
68 if (a.length != 20472) {
69 throw new Error("Invalid length: " + a.length);
70 }
71
72 for (var i = 0; i < cutoffIdx; i++) {
73 if (a[i] !== undefined) {
74 throw new Error("Invalid value at " + i + ": " + a[i]);
75 }
76 }
77
78 for (var i = cutoffIdx; i < a.length; i++) {
79 if (a[i] !== i) {
80 throw new Error("Invalid value at " + i + ": " + a[i]);
81 }
82 }`)
83 if err != nil {
84 t.Fatal(err)
85 }
86 if _, ok := a.self.(*arrayObject); !ok {
87 t.Fatal("3: array is not normal")
88 }
89 _, err = vm.RunString(`
90 // Delete enough elements for it to become sparse again.
91 var cutoffIdx1 = Math.round(20472 - 20472/10);
92 for (var i = cutoffIdx; i < cutoffIdx1; i++) {
93 delete a[i];
94 }
95
96 // This should switch it back to sparse.
97 a[25590] = 25590;
98 if (a.length != 25591) {
99 throw new Error("Invalid length: " + a.length);
100 }
101
102 for (var i = 0; i < cutoffIdx1; i++) {
103 if (a[i] !== undefined) {
104 throw new Error("Invalid value at " + i + ": " + a[i]);
105 }
106 }
107
108 for (var i = cutoffIdx1; i < 20472; i++) {
109 if (a[i] !== i) {
110 throw new Error("Invalid value at " + i + ": " + a[i]);
111 }
112 }
113
114 for (var i = 20472; i < 25590; i++) {
115 if (a[i] !== undefined) {
116 throw new Error("Invalid value at " + i + ": " + a[i]);
117 }
118 }
119
120 if (a[25590] !== 25590) {
121 throw new Error("Invalid value at 25590: " + a[25590]);
122 }
123 `)
124 if err != nil {
125 t.Fatal(err)
126 }
127 if _, ok := a.self.(*sparseArrayObject); !ok {
128 t.Fatal("4: array is not sparse")
129 }
130 }
131
132 func TestSparseArrayOwnKeys(t *testing.T) {
133 const SCRIPT = `
134 var a1 = [];
135 a1[500000] = 1;
136 var seen = false;
137 var count = 0;
138 var keys = Object.keys(a1);
139 keys.length === 1 && keys[0] === "500000";
140 `
141
142 testScript(SCRIPT, valueTrue, t)
143 }
144
145 func TestSparseArrayEnumerate(t *testing.T) {
146 const SCRIPT = `
147 var a1 = [];
148 a1[500000] = 1;
149 var seen = false;
150 var count = 0;
151 for (var i in a1) {
152 if (i === "500000") {
153 if (seen) {
154 throw new Error("seen twice");
155 }
156 seen = true;
157 }
158 count++;
159 }
160 seen && count === 1;
161 `
162
163 testScript(SCRIPT, valueTrue, t)
164 }
165
166 func TestArraySparseMaxLength(t *testing.T) {
167 const SCRIPT = `
168 var a = [];
169 a[4294967294]=1;
170 a.length === 4294967295 && a[4294967294] === 1;
171 `
172
173 testScript(SCRIPT, valueTrue, t)
174 }
175
176 func TestArraySparseExportProps(t *testing.T) {
177 vm := New()
178 proto := vm.NewArray()
179 for _, idx := range []string{"0", "500", "9999", "10001", "20471"} {
180 err := proto.Set(idx, true)
181 if err != nil {
182 t.Fatal(err)
183 }
184 }
185
186 arr := vm.NewArray()
187 err := arr.SetPrototype(proto)
188 if err != nil {
189 t.Fatal(err)
190 }
191 err = arr.DefineDataProperty("20470", vm.ToValue(true), FLAG_TRUE, FLAG_FALSE, FLAG_TRUE)
192 if err != nil {
193 t.Fatal(err)
194 }
195 err = arr.DefineDataProperty("10000", vm.ToValue(true), FLAG_TRUE, FLAG_FALSE, FLAG_TRUE)
196 if err != nil {
197 t.Fatal(err)
198 }
199 err = arr.Set("length", 20472)
200 if err != nil {
201 t.Fatal(err)
202 }
203 actual := arr.Export()
204 if actualArr, ok := actual.([]interface{}); ok {
205 if len(actualArr) == 20472 {
206 expectedIdx := map[int]struct{}{
207 0: {},
208 500: {},
209 9999: {},
210 10000: {},
211 10001: {},
212 20470: {},
213 20471: {},
214 }
215 for i, v := range actualArr {
216 if _, exists := expectedIdx[i]; exists {
217 if v != true {
218 t.Fatalf("Expected true at %d, got %v", i, v)
219 }
220 } else {
221 if v != nil {
222 t.Fatalf("Expected nil at %d, got %v", i, v)
223 }
224 }
225 }
226 } else {
227 t.Fatalf("Expected len 20471, actual: %d", len(actualArr))
228 }
229 } else {
230 t.Fatalf("Invalid export type: %T", actual)
231 }
232 }
233
234 func TestSparseArrayExportToSlice(t *testing.T) {
235 vm := New()
236 arr := vm.NewArray()
237 err := arr.Set("20470", 120470)
238 if err != nil {
239 t.Fatal(err)
240 }
241 err = arr.DefineDataProperty("20471", vm.ToValue(220471), FLAG_TRUE, FLAG_FALSE, FLAG_TRUE)
242 if err != nil {
243 t.Fatal(err)
244 }
245 var exp []int
246 err = vm.ExportTo(arr, &exp)
247 if err != nil {
248 t.Fatal(err)
249 }
250 if len(exp) != 20472 {
251 t.Fatalf("len: %d", len(exp))
252 }
253 if e := exp[20470]; e != 120470 {
254 t.Fatalf("20470: %d", e)
255 }
256 if e := exp[20471]; e != 220471 {
257 t.Fatalf("20471: %d", e)
258 }
259 for i := 0; i < 20470; i++ {
260 if exp[i] != 0 {
261 t.Fatalf("at %d: %d", i, exp[i])
262 }
263 }
264 }
265
View as plain text