github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/object_dynamic_test.go (about) 1 package goja 2 3 import ( 4 "sync" 5 "testing" 6 ) 7 8 type testDynObject struct { 9 r *Runtime 10 m map[string]Value 11 } 12 13 func (t *testDynObject) Get(key string) Value { 14 return t.m[key] 15 } 16 17 func (t *testDynObject) Set(key string, val Value) bool { 18 t.m[key] = val 19 return true 20 } 21 22 func (t *testDynObject) Has(key string) bool { 23 _, exists := t.m[key] 24 return exists 25 } 26 27 func (t *testDynObject) Delete(key string) bool { 28 delete(t.m, key) 29 return true 30 } 31 32 func (t *testDynObject) Keys() []string { 33 keys := make([]string, 0, len(t.m)) 34 for k := range t.m { 35 keys = append(keys, k) 36 } 37 return keys 38 } 39 40 type testDynArray struct { 41 r *Runtime 42 a []Value 43 } 44 45 func (t *testDynArray) Len() int { 46 return len(t.a) 47 } 48 49 func (t *testDynArray) Get(idx int) Value { 50 if idx < 0 { 51 idx += len(t.a) 52 } 53 if idx >= 0 && idx < len(t.a) { 54 return t.a[idx] 55 } 56 return nil 57 } 58 59 func (t *testDynArray) expand(newLen int) { 60 if newLen > cap(t.a) { 61 a := make([]Value, newLen) 62 copy(a, t.a) 63 t.a = a 64 } else { 65 t.a = t.a[:newLen] 66 } 67 } 68 69 func (t *testDynArray) Set(idx int, val Value) bool { 70 if idx < 0 { 71 idx += len(t.a) 72 } 73 if idx < 0 { 74 return false 75 } 76 if idx >= len(t.a) { 77 t.expand(idx + 1) 78 } 79 t.a[idx] = val 80 return true 81 } 82 83 func (t *testDynArray) SetLen(i int) bool { 84 if i > len(t.a) { 85 t.expand(i) 86 return true 87 } 88 if i < 0 { 89 return false 90 } 91 if i < len(t.a) { 92 tail := t.a[i:len(t.a)] 93 for j := range tail { 94 tail[j] = nil 95 } 96 t.a = t.a[:i] 97 } 98 return true 99 } 100 101 func TestDynamicObject(t *testing.T) { 102 vm := New() 103 dynObj := &testDynObject{ 104 r: vm, 105 m: make(map[string]Value), 106 } 107 o := vm.NewDynamicObject(dynObj) 108 vm.Set("o", o) 109 vm.testScriptWithTestLibX(` 110 assert(o instanceof Object, "instanceof Object"); 111 assert(o === o, "self equality"); 112 assert(o !== {}, "non-equality"); 113 114 o.test = 42; 115 assert("test" in o, "'test' in o"); 116 assert(deepEqual(Object.getOwnPropertyDescriptor(o, "test"), {value: 42, writable: true, enumerable: true, configurable: true}), "prop desc"); 117 118 assert.throws(TypeError, function() { 119 "use strict"; 120 Object.defineProperty(o, "test1", {value: 0, writable: false, enumerable: false, configurable: true}); 121 }, "define prop"); 122 123 var keys = []; 124 for (var key in o) { 125 keys.push(key); 126 } 127 assert(compareArray(keys, ["test"]), "for-in"); 128 129 assert(delete o.test, "delete"); 130 assert(!("test" in o), "'test' in o after delete"); 131 132 assert("__proto__" in o, "__proto__ in o"); 133 assert.sameValue(o.__proto__, Object.prototype, "__proto__"); 134 o.__proto__ = null; 135 assert(!("__proto__" in o), "__proto__ in o after setting to null"); 136 `, _undefined, t) 137 } 138 139 func TestDynamicObjectCustomProto(t *testing.T) { 140 vm := New() 141 m := make(map[string]Value) 142 dynObj := &testDynObject{ 143 r: vm, 144 m: m, 145 } 146 o := vm.NewDynamicObject(dynObj) 147 vm.Set("o", o) 148 vm.testScriptWithTestLib(` 149 var proto = { 150 valueOf: function() { 151 return this.num; 152 } 153 }; 154 proto[Symbol.toStringTag] = "GoObject"; 155 Object.setPrototypeOf(o, proto); 156 o.num = 41; 157 assert(o instanceof Object, "instanceof"); 158 assert.sameValue(o+1, 42); 159 assert.sameValue(o.toString(), "[object GoObject]"); 160 `, _undefined, t) 161 162 if v := m["num"]; v.Export() != int64(41) { 163 t.Fatal(v) 164 } 165 } 166 167 func TestDynamicArray(t *testing.T) { 168 vm := New() 169 dynObj := &testDynArray{ 170 r: vm, 171 } 172 a := vm.NewDynamicArray(dynObj) 173 vm.Set("a", a) 174 vm.testScriptWithTestLibX(` 175 assert(a instanceof Array, "instanceof Array"); 176 assert(a instanceof Object, "instanceof Object"); 177 assert(a === a, "self equality"); 178 assert(a !== [], "non-equality"); 179 assert(Array.isArray(a), "isArray()"); 180 assert("length" in a, "length in a"); 181 assert.sameValue(a.length, 0, "len == 0"); 182 assert.sameValue(a[0], undefined, "a[0] (1)"); 183 184 a[0] = 0; 185 assert.sameValue(a[0], 0, "a[0] (2)"); 186 assert.sameValue(a.length, 1, "length"); 187 assert(deepEqual(Object.getOwnPropertyDescriptor(a, 0), {value: 0, writable: true, enumerable: true, configurable: true}), "prop desc"); 188 assert(deepEqual(Object.getOwnPropertyDescriptor(a, "length"), {value: 1, writable: true, enumerable: false, configurable: false}), "length prop desc"); 189 190 assert("__proto__" in a, "__proto__ in a"); 191 assert.sameValue(a.__proto__, Array.prototype, "__proto__"); 192 193 assert(compareArray(Object.keys(a), ["0"]), "Object.keys()"); 194 assert(compareArray(Reflect.ownKeys(a), ["0", "length"]), "Reflect.ownKeys()"); 195 196 a.length = 2; 197 assert.sameValue(a.length, 2, "length after grow"); 198 assert.sameValue(a[1], undefined, "a[1]"); 199 200 a[1] = 1; 201 assert.sameValue(a[1], 1, "a[1] after set"); 202 a.length = 1; 203 assert.sameValue(a.length, 1, "length after shrink"); 204 assert.sameValue(a[1], undefined, "a[1] after shrink"); 205 a.length = 2; 206 assert.sameValue(a.length, 2, "length after shrink and grow"); 207 assert.sameValue(a[1], undefined, "a[1] after grow"); 208 209 a[0] = 3; a[1] = 1; a[2] = 2; 210 assert.sameValue(a.length, 3); 211 var keys = []; 212 for (var key in a) { 213 keys.push(key); 214 } 215 assert(compareArray(keys, ["0","1","2"]), "for-in"); 216 217 var vals = []; 218 for (var val of a) { 219 vals.push(val); 220 } 221 assert(compareArray(vals, [3,1,2]), "for-of"); 222 223 a.sort(); 224 assert(compareArray(a, [1,2,3]), "sort: "+a); 225 226 assert.sameValue(a[-1], 3); 227 assert.sameValue(a[-4], undefined); 228 229 assert.throws(TypeError, function() { 230 "use strict"; 231 delete a.length; 232 }, "delete length"); 233 234 assert.throws(TypeError, function() { 235 "use strict"; 236 a.test = true; 237 }, "set string prop"); 238 239 assert.throws(TypeError, function() { 240 "use strict"; 241 Object.defineProperty(a, 0, {value: 0, writable: false, enumerable: false, configurable: true}); 242 }, "define prop"); 243 244 `, _undefined, t) 245 } 246 247 type testSharedDynObject struct { 248 sync.RWMutex 249 m map[string]Value 250 } 251 252 func (t *testSharedDynObject) Get(key string) Value { 253 t.RLock() 254 val := t.m[key] 255 t.RUnlock() 256 return val 257 } 258 259 func (t *testSharedDynObject) Set(key string, val Value) bool { 260 t.Lock() 261 t.m[key] = val 262 t.Unlock() 263 return true 264 } 265 266 func (t *testSharedDynObject) Has(key string) bool { 267 t.RLock() 268 _, exists := t.m[key] 269 t.RUnlock() 270 return exists 271 } 272 273 func (t *testSharedDynObject) Delete(key string) bool { 274 t.Lock() 275 delete(t.m, key) 276 t.Unlock() 277 return true 278 } 279 280 func (t *testSharedDynObject) Keys() []string { 281 t.RLock() 282 keys := make([]string, 0, len(t.m)) 283 for k := range t.m { 284 keys = append(keys, k) 285 } 286 t.RUnlock() 287 return keys 288 } 289 290 func TestSharedDynamicObject(t *testing.T) { 291 dynObj := &testSharedDynObject{m: make(map[string]Value, 10000)} 292 o := NewSharedDynamicObject(dynObj) 293 ch := make(chan error, 1) 294 go func() { 295 vm := New() 296 vm.Set("o", o) 297 _, err := vm.RunString(` 298 for (let i = 0; i < 10000; i++) { 299 o[i] = i; 300 } 301 `) 302 ch <- err 303 }() 304 vm := New() 305 vm.Set("o", o) 306 _, err := vm.RunString(` 307 for (let i = 0; i < 10000; i++) { 308 o[i] = i+1; 309 } 310 `) 311 if err != nil { 312 t.Fatal(err) 313 } 314 315 err = <-ch 316 if err != nil { 317 t.Fatal(err) 318 } 319 } 320 321 type testSharedDynArray struct { 322 sync.RWMutex 323 a []Value 324 } 325 326 func (t *testSharedDynArray) Len() int { 327 t.RLock() 328 l := len(t.a) 329 t.RUnlock() 330 return l 331 } 332 333 func (t *testSharedDynArray) Get(idx int) Value { 334 t.RLock() 335 defer t.RUnlock() 336 if idx < 0 { 337 idx += len(t.a) 338 } 339 if idx >= 0 && idx < len(t.a) { 340 return t.a[idx] 341 } 342 return nil 343 } 344 345 func (t *testSharedDynArray) expand(newLen int) { 346 if newLen > cap(t.a) { 347 a := make([]Value, newLen) 348 copy(a, t.a) 349 t.a = a 350 } else { 351 t.a = t.a[:newLen] 352 } 353 } 354 355 func (t *testSharedDynArray) Set(idx int, val Value) bool { 356 t.Lock() 357 defer t.Unlock() 358 if idx < 0 { 359 idx += len(t.a) 360 } 361 if idx < 0 { 362 return false 363 } 364 if idx >= len(t.a) { 365 t.expand(idx + 1) 366 } 367 t.a[idx] = val 368 return true 369 } 370 371 func (t *testSharedDynArray) SetLen(i int) bool { 372 t.Lock() 373 defer t.Unlock() 374 if i > len(t.a) { 375 t.expand(i) 376 return true 377 } 378 if i < 0 { 379 return false 380 } 381 if i < len(t.a) { 382 tail := t.a[i:len(t.a)] 383 for j := range tail { 384 tail[j] = nil 385 } 386 t.a = t.a[:i] 387 } 388 return true 389 } 390 391 func TestSharedDynamicArray(t *testing.T) { 392 dynObj := &testSharedDynArray{a: make([]Value, 10000)} 393 o := NewSharedDynamicArray(dynObj) 394 ch := make(chan error, 1) 395 go func() { 396 vm := New() 397 vm.Set("o", o) 398 _, err := vm.RunString(` 399 for (let i = 0; i < 10000; i++) { 400 o[i] = i; 401 } 402 `) 403 ch <- err 404 }() 405 vm := New() 406 vm.Set("o", o) 407 _, err := vm.RunString(` 408 for (let i = 0; i < 10000; i++) { 409 o[i] = i+1; 410 } 411 `) 412 if err != nil { 413 t.Fatal(err) 414 } 415 416 err = <-ch 417 if err != nil { 418 t.Fatal(err) 419 } 420 }