github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/array_sparse_test.go (about) 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 }