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 }
421
View as plain text