1 package goja
2
3 import (
4 "fmt"
5 "reflect"
6 "strings"
7 "testing"
8 )
9
10 func TestDefineProperty(t *testing.T) {
11 r := New()
12 o := r.NewObject()
13
14 err := o.DefineDataProperty("data", r.ToValue(42), FLAG_TRUE, FLAG_TRUE, FLAG_TRUE)
15 if err != nil {
16 t.Fatal(err)
17 }
18
19 err = o.DefineAccessorProperty("accessor_ro", r.ToValue(func() int {
20 return 1
21 }), nil, FLAG_TRUE, FLAG_TRUE)
22 if err != nil {
23 t.Fatal(err)
24 }
25
26 err = o.DefineAccessorProperty("accessor_rw",
27 r.ToValue(func(call FunctionCall) Value {
28 return o.Get("__hidden")
29 }),
30 r.ToValue(func(call FunctionCall) (ret Value) {
31 o.Set("__hidden", call.Argument(0))
32 return
33 }),
34 FLAG_TRUE, FLAG_TRUE)
35
36 if err != nil {
37 t.Fatal(err)
38 }
39
40 if v := o.Get("accessor_ro"); v.ToInteger() != 1 {
41 t.Fatalf("Unexpected accessor value: %v", v)
42 }
43
44 err = o.Set("accessor_ro", r.ToValue(2))
45 if err == nil {
46 t.Fatal("Expected an error")
47 }
48 if ex, ok := err.(*Exception); ok {
49 if msg := ex.Error(); msg != "TypeError: Cannot assign to read only property 'accessor_ro'" {
50 t.Fatalf("Unexpected error: '%s'", msg)
51 }
52 } else {
53 t.Fatalf("Unexected error type: %T", err)
54 }
55
56 err = o.Set("accessor_rw", 42)
57 if err != nil {
58 t.Fatal(err)
59 }
60
61 if v := o.Get("accessor_rw"); v.ToInteger() != 42 {
62 t.Fatalf("Unexpected value: %v", v)
63 }
64 }
65
66 func TestPropertyOrder(t *testing.T) {
67 const SCRIPT = `
68 var o = {};
69 var sym1 = Symbol(1);
70 var sym2 = Symbol(2);
71 o[sym2] = 1;
72 o[4294967294] = 1;
73 o[2] = 1;
74 o[1] = 1;
75 o[0] = 1;
76 o["02"] = 1;
77 o[4294967295] = 1;
78 o["01"] = 1;
79 o["00"] = 1;
80 o[sym1] = 1;
81 var expected = ["0", "1", "2", "4294967294", "02", "4294967295", "01", "00", sym2, sym1];
82 var actual = Reflect.ownKeys(o);
83 if (actual.length !== expected.length) {
84 throw new Error("Unexpected length: "+actual.length);
85 }
86 for (var i = 0; i < actual.length; i++) {
87 if (actual[i] !== expected[i]) {
88 throw new Error("Unexpected list: " + actual);
89 }
90 }
91 `
92
93 testScript(SCRIPT, _undefined, t)
94 }
95
96 func TestDefinePropertiesSymbol(t *testing.T) {
97 const SCRIPT = `
98 var desc = {};
99 desc[Symbol.toStringTag] = {value: "Test"};
100 var o = {};
101 Object.defineProperties(o, desc);
102 o[Symbol.toStringTag] === "Test";
103 `
104
105 testScript(SCRIPT, valueTrue, t)
106 }
107
108 func TestObjectShorthandProperties(t *testing.T) {
109 const SCRIPT = `
110 var b = 1;
111 var a = {b, get() {return "c"}};
112
113 assert.sameValue(a.b, b, "#1");
114 assert.sameValue(a.get(), "c", "#2");
115
116 var obj = {
117 w\u0069th() { return 42; }
118 };
119
120 assert.sameValue(obj['with'](), 42, 'property exists');
121 `
122 testScriptWithTestLib(SCRIPT, _undefined, t)
123 }
124
125 func TestObjectAssign(t *testing.T) {
126 const SCRIPT = `
127 assert.sameValue(Object.assign({ b: 1 }, { get a() {
128 Object.defineProperty(this, "b", {
129 value: 3,
130 enumerable: false
131 });
132 }, b: 2 }).b, 1, "#1");
133
134 assert.sameValue(Object.assign({ b: 1 }, { get a() {
135 delete this.b;
136 }, b: 2 }).b, 1, "#2");
137 `
138 testScriptWithTestLib(SCRIPT, _undefined, t)
139 }
140
141 func TestExportCircular(t *testing.T) {
142 vm := New()
143 o := vm.NewObject()
144 o.Set("o", o)
145 v := o.Export()
146 if m, ok := v.(map[string]interface{}); ok {
147 if reflect.ValueOf(m["o"]).Pointer() != reflect.ValueOf(v).Pointer() {
148 t.Fatal("Unexpected value")
149 }
150 } else {
151 t.Fatal("Unexpected type")
152 }
153
154 res, err := vm.RunString(`var a = []; a[0] = a;`)
155 if err != nil {
156 t.Fatal(err)
157 }
158 v = res.Export()
159 if a, ok := v.([]interface{}); ok {
160 if reflect.ValueOf(a[0]).Pointer() != reflect.ValueOf(v).Pointer() {
161 t.Fatal("Unexpected value")
162 }
163 } else {
164 t.Fatal("Unexpected type")
165 }
166 }
167
168 type test_s struct {
169 S *test_s1
170 }
171 type test_s1 struct {
172 S *test_s
173 }
174
175 func TestExportToCircular(t *testing.T) {
176 vm := New()
177 o := vm.NewObject()
178 o.Set("o", o)
179 var m map[string]interface{}
180 err := vm.ExportTo(o, &m)
181 if err != nil {
182 t.Fatal(err)
183 }
184
185 type K string
186 type T map[K]T
187 var m1 T
188 err = vm.ExportTo(o, &m1)
189 if err != nil {
190 t.Fatal(err)
191 }
192
193 type A []A
194 var a A
195 res, err := vm.RunString("var a = []; a[0] = a;")
196 if err != nil {
197 t.Fatal(err)
198 }
199 err = vm.ExportTo(res, &a)
200 if err != nil {
201 t.Fatal(err)
202 }
203 if &a[0] != &a[0][0] {
204 t.Fatal("values do not match")
205 }
206
207 o = vm.NewObject()
208 o.Set("S", o)
209 var s test_s
210 err = vm.ExportTo(o, &s)
211 if err != nil {
212 t.Fatal(err)
213 }
214 if s.S.S != &s {
215 t.Fatalf("values do not match: %v, %v", s.S.S, &s)
216 }
217
218 type test_s2 struct {
219 S interface{}
220 S1 *test_s2
221 }
222
223 var s2 test_s2
224 o.Set("S1", o)
225
226 err = vm.ExportTo(o, &s2)
227 if err != nil {
228 t.Fatal(err)
229 }
230
231 if m, ok := s2.S.(map[string]interface{}); ok {
232 if reflect.ValueOf(m["S"]).Pointer() != reflect.ValueOf(m).Pointer() {
233 t.Fatal("Unexpected m.S")
234 }
235 } else {
236 t.Fatalf("Unexpected s2.S type: %T", s2.S)
237 }
238 if s2.S1 != &s2 {
239 t.Fatal("Unexpected s2.S1")
240 }
241
242 o1 := vm.NewObject()
243 o1.Set("S", o)
244 o1.Set("S1", o)
245 err = vm.ExportTo(o1, &s2)
246 if err != nil {
247 t.Fatal(err)
248 }
249 if s2.S1.S1 != s2.S1 {
250 t.Fatal("Unexpected s2.S1.S1")
251 }
252 }
253
254 func TestExportWrappedMap(t *testing.T) {
255 vm := New()
256 m := map[string]interface{}{
257 "test": "failed",
258 }
259 exported := vm.ToValue(m).Export()
260 if exportedMap, ok := exported.(map[string]interface{}); ok {
261 exportedMap["test"] = "passed"
262 if v := m["test"]; v != "passed" {
263 t.Fatalf("Unexpected m[\"test\"]: %v", v)
264 }
265 } else {
266 t.Fatalf("Unexpected export type: %T", exported)
267 }
268 }
269
270 func TestExportToWrappedMap(t *testing.T) {
271 vm := New()
272 m := map[string]interface{}{
273 "test": "failed",
274 }
275 var exported map[string]interface{}
276 err := vm.ExportTo(vm.ToValue(m), &exported)
277 if err != nil {
278 t.Fatal(err)
279 }
280 exported["test"] = "passed"
281 if v := m["test"]; v != "passed" {
282 t.Fatalf("Unexpected m[\"test\"]: %v", v)
283 }
284 }
285
286 func TestExportToWrappedMapCustom(t *testing.T) {
287 type CustomMap map[string]bool
288 vm := New()
289 m := CustomMap{}
290 var exported CustomMap
291 err := vm.ExportTo(vm.ToValue(m), &exported)
292 if err != nil {
293 t.Fatal(err)
294 }
295 exported["test"] = true
296 if v := m["test"]; v != true {
297 t.Fatalf("Unexpected m[\"test\"]: %v", v)
298 }
299 }
300
301 func TestExportToSliceNonIterable(t *testing.T) {
302 vm := New()
303 o := vm.NewObject()
304 var a []interface{}
305 err := vm.ExportTo(o, &a)
306 if err == nil {
307 t.Fatal("Expected an error")
308 }
309 if len(a) != 0 {
310 t.Fatalf("a: %v", a)
311 }
312 if msg := err.Error(); msg != "cannot convert [object Object] to []interface {}: not an array or iterable" {
313 t.Fatalf("Unexpected error: %v", err)
314 }
315 }
316
317 func ExampleRuntime_ExportTo_iterableToSlice() {
318 vm := New()
319 v, err := vm.RunString(`
320 function reverseIterator() {
321 const arr = this;
322 let idx = arr.length;
323 return {
324 next: () => idx > 0 ? {value: arr[--idx]} : {done: true}
325 }
326 }
327 const arr = [1,2,3];
328 arr[Symbol.iterator] = reverseIterator;
329 arr;
330 `)
331 if err != nil {
332 panic(err)
333 }
334
335 var arr []int
336 err = vm.ExportTo(v, &arr)
337 if err != nil {
338 panic(err)
339 }
340
341 fmt.Println(arr)
342
343 }
344
345 func TestRuntime_ExportTo_proxiedIterableToSlice(t *testing.T) {
346 vm := New()
347 v, err := vm.RunString(`
348 function reverseIterator() {
349 const arr = this;
350 let idx = arr.length;
351 return {
352 next: () => idx > 0 ? {value: arr[--idx]} : {done: true}
353 }
354 }
355 const arr = [1,2,3];
356 arr[Symbol.iterator] = reverseIterator;
357 new Proxy(arr, {});
358 `)
359 if err != nil {
360 t.Fatal(err)
361 }
362
363 var arr []int
364 err = vm.ExportTo(v, &arr)
365 if err != nil {
366 t.Fatal(err)
367 }
368 if out := fmt.Sprint(arr); out != "[3 2 1]" {
369 t.Fatal(out)
370 }
371 }
372
373 func ExampleRuntime_ExportTo_arrayLikeToSlice() {
374 vm := New()
375 v, err := vm.RunString(`
376 ({
377 length: 3,
378 0: 1,
379 1: 2,
380 2: 3
381 });
382 `)
383 if err != nil {
384 panic(err)
385 }
386
387 var arr []int
388 err = vm.ExportTo(v, &arr)
389 if err != nil {
390 panic(err)
391 }
392
393 fmt.Println(arr)
394
395 }
396
397 func TestExportArrayToArrayMismatchedLengths(t *testing.T) {
398 vm := New()
399 a := vm.NewArray(1, 2)
400 var a1 [3]int
401 err := vm.ExportTo(a, &a1)
402 if err == nil {
403 t.Fatal("expected error")
404 }
405 if msg := err.Error(); !strings.Contains(msg, "lengths mismatch") {
406 t.Fatalf("unexpected error: %v", err)
407 }
408 }
409
410 func TestExportIterableToArrayMismatchedLengths(t *testing.T) {
411 vm := New()
412 a, err := vm.RunString(`
413 new Map([[1, true], [2, true]]);
414 `)
415 if err != nil {
416 t.Fatal(err)
417 }
418
419 var a1 [3]interface{}
420 err = vm.ExportTo(a, &a1)
421 if err == nil {
422 t.Fatal("expected error")
423 }
424 if msg := err.Error(); !strings.Contains(msg, "lengths mismatch") {
425 t.Fatalf("unexpected error: %v", err)
426 }
427 }
428
429 func TestExportArrayLikeToArrayMismatchedLengths(t *testing.T) {
430 vm := New()
431 a, err := vm.RunString(`
432 ({
433 length: 2,
434 0: true,
435 1: true
436 });
437 `)
438 if err != nil {
439 t.Fatal(err)
440 }
441
442 var a1 [3]interface{}
443 err = vm.ExportTo(a, &a1)
444 if err == nil {
445 t.Fatal("expected error")
446 }
447 if msg := err.Error(); !strings.Contains(msg, "lengths mismatch") {
448 t.Fatalf("unexpected error: %v", err)
449 }
450 }
451
452 func TestSetForeignReturnValue(t *testing.T) {
453 const SCRIPT = `
454 var array = [1, 2, 3];
455 var arrayTarget = new Proxy(array, {});
456
457 Object.preventExtensions(array);
458
459 !Reflect.set(arrayTarget, "foo", 2);
460 `
461
462 testScript(SCRIPT, valueTrue, t)
463 }
464
465 func TestDefinePropertiesUndefinedVal(t *testing.T) {
466 const SCRIPT = `
467 var target = {};
468 var sym = Symbol();
469 target[sym] = 1;
470 target.foo = 2;
471 target[0] = 3;
472
473 var getOwnKeys = [];
474 var proxy = new Proxy(target, {
475 getOwnPropertyDescriptor: function(_target, key) {
476 getOwnKeys.push(key);
477 },
478 });
479
480 Object.defineProperties({}, proxy);
481 true;
482 `
483
484 testScript(SCRIPT, valueTrue, t)
485 }
486
487 func ExampleObject_Delete() {
488 vm := New()
489 obj := vm.NewObject()
490 _ = obj.Set("test", true)
491 before := obj.Get("test")
492 _ = obj.Delete("test")
493 after := obj.Get("test")
494 fmt.Printf("before: %v, after: %v", before, after)
495
496 }
497
498 func TestObjectEquality(t *testing.T) {
499 type CustomInt int
500 type S struct {
501 F CustomInt
502 }
503 vm := New()
504 vm.Set("s", S{})
505
506
507 vm.testScriptWithTestLib(`
508 assert.sameValue([s.F].indexOf(s.F), 0, "indexOf");
509 assert([s.F].includes(s.F));
510 `, _undefined, t)
511 }
512
513 func BenchmarkPut(b *testing.B) {
514 v := &Object{}
515
516 o := &baseObject{
517 val: v,
518 extensible: true,
519 }
520 v.self = o
521
522 o.init()
523
524 var key Value = asciiString("test")
525 var val Value = valueInt(123)
526
527 for i := 0; i < b.N; i++ {
528 v.setOwn(key, val, false)
529 }
530 }
531
532 func BenchmarkPutStr(b *testing.B) {
533 v := &Object{}
534
535 o := &baseObject{
536 val: v,
537 extensible: true,
538 }
539
540 o.init()
541
542 v.self = o
543
544 var val Value = valueInt(123)
545
546 for i := 0; i < b.N; i++ {
547 o.setOwnStr("test", val, false)
548 }
549 }
550
551 func BenchmarkGet(b *testing.B) {
552 v := &Object{}
553
554 o := &baseObject{
555 val: v,
556 extensible: true,
557 }
558
559 o.init()
560
561 v.self = o
562 var n Value = asciiString("test")
563
564 for i := 0; i < b.N; i++ {
565 v.get(n, nil)
566 }
567
568 }
569
570 func BenchmarkGetStr(b *testing.B) {
571 v := &Object{}
572
573 o := &baseObject{
574 val: v,
575 extensible: true,
576 }
577 v.self = o
578
579 o.init()
580
581 for i := 0; i < b.N; i++ {
582 o.getStr("test", nil)
583 }
584 }
585
586 func __toString(v Value) string {
587 switch v := v.(type) {
588 case asciiString:
589 return string(v)
590 default:
591 return ""
592 }
593 }
594
595 func BenchmarkToString1(b *testing.B) {
596 v := asciiString("test")
597
598 for i := 0; i < b.N; i++ {
599 v.toString()
600 }
601 }
602
603 func BenchmarkToString2(b *testing.B) {
604 v := asciiString("test")
605
606 for i := 0; i < b.N; i++ {
607 __toString(v)
608 }
609 }
610
611 func BenchmarkConv(b *testing.B) {
612 count := int64(0)
613 for i := 0; i < b.N; i++ {
614 count += valueInt(123).ToInteger()
615 }
616 if count == 0 {
617 b.Fatal("zero")
618 }
619 }
620
621 func BenchmarkToUTF8String(b *testing.B) {
622 var s String = asciiString("test")
623 for i := 0; i < b.N; i++ {
624 _ = s.String()
625 }
626 }
627
628 func BenchmarkAdd(b *testing.B) {
629 var x, y Value
630 x = valueInt(2)
631 y = valueInt(2)
632
633 for i := 0; i < b.N; i++ {
634 if xi, ok := x.(valueInt); ok {
635 if yi, ok := y.(valueInt); ok {
636 x = xi + yi
637 }
638 }
639 }
640 }
641
642 func BenchmarkAddString(b *testing.B) {
643 var x, y Value
644
645 tst := asciiString("22")
646 x = asciiString("2")
647 y = asciiString("2")
648
649 for i := 0; i < b.N; i++ {
650 var z Value
651 if xi, ok := x.(String); ok {
652 if yi, ok := y.(String); ok {
653 z = xi.Concat(yi)
654 }
655 }
656 if !z.StrictEquals(tst) {
657 b.Fatalf("Unexpected result %v", x)
658 }
659 }
660 }
661
View as plain text