github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/object_goreflect_test.go (about) 1 package goja 2 3 import ( 4 "errors" 5 "fmt" 6 "math" 7 "reflect" 8 "strings" 9 "testing" 10 "time" 11 ) 12 13 func TestGoReflectGet(t *testing.T) { 14 const SCRIPT = ` 15 o.X + o.Y; 16 ` 17 type O struct { 18 X int 19 Y string 20 } 21 r := New() 22 o := O{X: 4, Y: "2"} 23 r.Set("o", o) 24 25 v, err := r.RunString(SCRIPT) 26 if err != nil { 27 t.Fatal(err) 28 } 29 30 if s, ok := v.(String); ok { 31 if s.String() != "42" { 32 t.Fatalf("Unexpected string: %s", s) 33 } 34 } else { 35 t.Fatalf("Unexpected type: %s", v) 36 } 37 } 38 39 func TestGoReflectSet(t *testing.T) { 40 const SCRIPT = ` 41 o.X++; 42 o.Y += "P"; 43 ` 44 type O struct { 45 X int 46 Y string 47 } 48 r := New() 49 o := O{X: 4, Y: "2"} 50 r.Set("o", &o) 51 52 _, err := r.RunString(SCRIPT) 53 if err != nil { 54 t.Fatal(err) 55 } 56 57 if o.X != 5 { 58 t.Fatalf("Unexpected X: %d", o.X) 59 } 60 61 if o.Y != "2P" { 62 t.Fatalf("Unexpected Y: %s", o.Y) 63 } 64 65 r.Set("o", o) 66 _, err = r.RunString(SCRIPT) 67 if err != nil { 68 t.Fatal(err) 69 } 70 71 if res, ok := r.Get("o").Export().(O); ok { 72 if res.X != 6 { 73 t.Fatalf("Unexpected res.X: %d", res.X) 74 } 75 76 if res.Y != "2PP" { 77 t.Fatalf("Unexpected res.Y: %s", res.Y) 78 } 79 } 80 } 81 82 func TestGoReflectEnumerate(t *testing.T) { 83 const SCRIPT = ` 84 var hasX = false; 85 var hasY = false; 86 for (var key in o) { 87 switch (key) { 88 case "X": 89 if (hasX) { 90 throw "Already have X"; 91 } 92 hasX = true; 93 break; 94 case "Y": 95 if (hasY) { 96 throw "Already have Y"; 97 } 98 hasY = true; 99 break; 100 default: 101 throw "Unexpected property: " + key; 102 } 103 } 104 hasX && hasY; 105 ` 106 107 type S struct { 108 X, Y int 109 } 110 111 r := New() 112 r.Set("o", S{X: 40, Y: 2}) 113 v, err := r.RunString(SCRIPT) 114 if err != nil { 115 t.Fatal(err) 116 } 117 118 if !v.StrictEquals(valueTrue) { 119 t.Fatalf("Expected true, got %v", v) 120 } 121 122 } 123 124 func TestGoReflectCustomIntUnbox(t *testing.T) { 125 const SCRIPT = ` 126 i + 2; 127 ` 128 129 type CustomInt int 130 var i CustomInt = 40 131 132 r := New() 133 r.Set("i", i) 134 v, err := r.RunString(SCRIPT) 135 if err != nil { 136 t.Fatal(err) 137 } 138 139 if !v.StrictEquals(intToValue(42)) { 140 t.Fatalf("Expected int 42, got %v", v) 141 } 142 } 143 144 func TestGoReflectPreserveCustomType(t *testing.T) { 145 const SCRIPT = ` 146 i; 147 ` 148 149 type CustomInt int 150 var i CustomInt = 42 151 152 r := New() 153 r.Set("i", i) 154 v, err := r.RunString(SCRIPT) 155 if err != nil { 156 t.Fatal(err) 157 } 158 159 ve := v.Export() 160 161 if ii, ok := ve.(CustomInt); ok { 162 if ii != i { 163 t.Fatalf("Wrong value: %v", ii) 164 } 165 } else { 166 t.Fatalf("Wrong type: %v", ve) 167 } 168 } 169 170 func TestGoReflectCustomIntValueOf(t *testing.T) { 171 const SCRIPT = ` 172 if (i instanceof Number) { 173 i.valueOf(); 174 } else { 175 throw new Error("Value is not a number"); 176 } 177 ` 178 179 type CustomInt int 180 var i CustomInt = 42 181 182 r := New() 183 r.Set("i", i) 184 v, err := r.RunString(SCRIPT) 185 if err != nil { 186 t.Fatal(err) 187 } 188 189 if !v.StrictEquals(intToValue(42)) { 190 t.Fatalf("Expected int 42, got %v", v) 191 } 192 } 193 194 func TestGoReflectEqual(t *testing.T) { 195 const SCRIPT = ` 196 x === y; 197 ` 198 199 type CustomInt int 200 var x CustomInt = 42 201 var y CustomInt = 42 202 203 r := New() 204 r.Set("x", x) 205 r.Set("y", y) 206 v, err := r.RunString(SCRIPT) 207 if err != nil { 208 t.Fatal(err) 209 } 210 211 if !v.StrictEquals(valueTrue) { 212 t.Fatalf("Expected true, got %v", v) 213 } 214 } 215 216 type testGoReflectMethod_O struct { 217 field string 218 Test string 219 } 220 221 func (o testGoReflectMethod_O) Method(s string) string { 222 return o.field + s 223 } 224 225 func TestGoReflectMethod(t *testing.T) { 226 const SCRIPT = ` 227 o.Method(" 123") 228 ` 229 230 o := testGoReflectMethod_O{ 231 field: "test", 232 } 233 234 r := New() 235 r.Set("o", &o) 236 v, err := r.RunString(SCRIPT) 237 if err != nil { 238 t.Fatal(err) 239 } 240 241 if !v.StrictEquals(asciiString("test 123")) { 242 t.Fatalf("Expected 'test 123', got %v", v) 243 } 244 } 245 246 func (o *testGoReflectMethod_O) Set(s string) { 247 o.field = s 248 } 249 250 func (o *testGoReflectMethod_O) Get() string { 251 return o.field 252 } 253 254 func TestGoReflectMethodPtr(t *testing.T) { 255 const SCRIPT = ` 256 o.Set("42") 257 o.Get() 258 ` 259 260 o := testGoReflectMethod_O{ 261 field: "test", 262 } 263 264 r := New() 265 r.Set("o", &o) 266 v, err := r.RunString(SCRIPT) 267 if err != nil { 268 t.Fatal(err) 269 } 270 271 if !v.StrictEquals(asciiString("42")) { 272 t.Fatalf("Expected '42', got %v", v) 273 } 274 } 275 276 func (b *testBoolS) Method() bool { 277 return bool(*b) 278 } 279 280 func TestGoReflectPtrMethodOnNonPtrValue(t *testing.T) { 281 var o testGoReflectMethod_O 282 o.Get() 283 vm := New() 284 vm.Set("o", o) 285 _, err := vm.RunString(`o.Get()`) 286 if err != nil { 287 t.Fatal(err) 288 } 289 _, err = vm.RunString(`o.Method()`) 290 if err != nil { 291 t.Fatal(err) 292 } 293 294 var b testBoolS 295 vm.Set("b", b) 296 _, err = vm.RunString(`b.Method()`) 297 if err != nil { 298 t.Fatal(err) 299 } 300 } 301 302 func TestGoReflectStructField(t *testing.T) { 303 type S struct { 304 F testGoReflectMethod_O 305 B testBoolS 306 } 307 var s S 308 vm := New() 309 vm.Set("s", &s) 310 311 const SCRIPT = ` 312 s.F.Set("Test"); 313 assert.sameValue(s.F.Method(""), "Test", "1"); 314 315 s.B = true; 316 assert.sameValue(s.B.Method(), true, "2"); 317 318 assert.sameValue(s.B.toString(), "B", "3"); 319 ` 320 321 vm.testScriptWithTestLib(SCRIPT, _undefined, t) 322 } 323 324 func TestGoReflectProp(t *testing.T) { 325 const SCRIPT = ` 326 var d1 = Object.getOwnPropertyDescriptor(o, "Get"); 327 var d2 = Object.getOwnPropertyDescriptor(o, "Test"); 328 !d1.writable && !d1.configurable && d2.writable && !d2.configurable; 329 ` 330 331 o := testGoReflectMethod_O{ 332 field: "test", 333 } 334 335 r := New() 336 r.Set("o", &o) 337 v, err := r.RunString(SCRIPT) 338 if err != nil { 339 t.Fatal(err) 340 } 341 342 if !v.StrictEquals(valueTrue) { 343 t.Fatalf("Expected true, got %v", v) 344 } 345 } 346 347 func TestGoReflectRedefineFieldSuccess(t *testing.T) { 348 const SCRIPT = ` 349 Object.defineProperty(o, "Test", {value: "AAA"}) === o; 350 ` 351 352 o := testGoReflectMethod_O{} 353 354 r := New() 355 r.Set("o", &o) 356 v, err := r.RunString(SCRIPT) 357 if err != nil { 358 t.Fatal(err) 359 } 360 361 if !v.StrictEquals(valueTrue) { 362 t.Fatalf("Expected true, got %v", v) 363 } 364 365 if o.Test != "AAA" { 366 t.Fatalf("Expected 'AAA', got '%s'", o.Test) 367 } 368 369 } 370 371 func TestGoReflectRedefineFieldNonWritable(t *testing.T) { 372 const SCRIPT = ` 373 var thrown = false; 374 try { 375 Object.defineProperty(o, "Test", {value: "AAA", writable: false}); 376 } catch (e) { 377 if (e instanceof TypeError) { 378 thrown = true; 379 } else { 380 throw e; 381 } 382 } 383 thrown; 384 ` 385 386 o := testGoReflectMethod_O{Test: "Test"} 387 388 r := New() 389 r.Set("o", &o) 390 v, err := r.RunString(SCRIPT) 391 if err != nil { 392 t.Fatal(err) 393 } 394 395 if !v.StrictEquals(valueTrue) { 396 t.Fatalf("Expected true, got %v", v) 397 } 398 399 if o.Test != "Test" { 400 t.Fatalf("Expected 'Test', got: '%s'", o.Test) 401 } 402 } 403 404 func TestGoReflectRedefineFieldConfigurable(t *testing.T) { 405 const SCRIPT = ` 406 var thrown = false; 407 try { 408 Object.defineProperty(o, "Test", {value: "AAA", configurable: true}); 409 } catch (e) { 410 if (e instanceof TypeError) { 411 thrown = true; 412 } else { 413 throw e; 414 } 415 } 416 thrown; 417 ` 418 419 o := testGoReflectMethod_O{Test: "Test"} 420 421 r := New() 422 r.Set("o", &o) 423 v, err := r.RunString(SCRIPT) 424 if err != nil { 425 t.Fatal(err) 426 } 427 428 if !v.StrictEquals(valueTrue) { 429 t.Fatalf("Expected true, got %v", v) 430 } 431 432 if o.Test != "Test" { 433 t.Fatalf("Expected 'Test', got: '%s'", o.Test) 434 } 435 } 436 437 func TestGoReflectRedefineMethod(t *testing.T) { 438 const SCRIPT = ` 439 var thrown = false; 440 try { 441 Object.defineProperty(o, "Method", {value: "AAA", configurable: true}); 442 } catch (e) { 443 if (e instanceof TypeError) { 444 thrown = true; 445 } else { 446 throw e; 447 } 448 } 449 thrown; 450 ` 451 452 o := testGoReflectMethod_O{Test: "Test"} 453 454 r := New() 455 r.Set("o", &o) 456 v, err := r.RunString(SCRIPT) 457 if err != nil { 458 t.Fatal(err) 459 } 460 461 if !v.StrictEquals(valueTrue) { 462 t.Fatalf("Expected true, got %v", v) 463 } 464 } 465 466 func TestGoReflectEmbeddedStruct(t *testing.T) { 467 const SCRIPT = ` 468 if (o.ParentField2 !== "ParentField2") { 469 throw new Error("ParentField2 = " + o.ParentField2); 470 } 471 472 if (o.Parent.ParentField2 !== 2) { 473 throw new Error("o.Parent.ParentField2 = " + o.Parent.ParentField2); 474 } 475 476 if (o.ParentField1 !== 1) { 477 throw new Error("o.ParentField1 = " + o.ParentField1); 478 479 } 480 481 if (o.ChildField !== 3) { 482 throw new Error("o.ChildField = " + o.ChildField); 483 } 484 485 var keys = {}; 486 for (var k in o) { 487 if (keys[k]) { 488 throw new Error("Duplicate key: " + k); 489 } 490 keys[k] = true; 491 } 492 493 var expectedKeys = ["ParentField2", "ParentField1", "Parent", "ChildField"]; 494 for (var i in expectedKeys) { 495 if (!keys[expectedKeys[i]]) { 496 throw new Error("Missing key in enumeration: " + expectedKeys[i]); 497 } 498 delete keys[expectedKeys[i]]; 499 } 500 501 var remainingKeys = Object.keys(keys); 502 if (remainingKeys.length > 0) { 503 throw new Error("Unexpected keys: " + remainingKeys); 504 } 505 506 o.ParentField2 = "ParentField22"; 507 o.Parent.ParentField2 = 22; 508 o.ParentField1 = 11; 509 o.ChildField = 33; 510 ` 511 512 type Parent struct { 513 ParentField1 int 514 ParentField2 int 515 } 516 517 type Child struct { 518 ParentField2 string 519 Parent 520 ChildField int 521 } 522 523 vm := New() 524 o := Child{ 525 Parent: Parent{ 526 ParentField1: 1, 527 ParentField2: 2, 528 }, 529 ParentField2: "ParentField2", 530 ChildField: 3, 531 } 532 vm.Set("o", &o) 533 534 _, err := vm.RunString(SCRIPT) 535 if err != nil { 536 t.Fatal(err) 537 } 538 539 if o.ParentField2 != "ParentField22" { 540 t.Fatalf("ParentField2 = %q", o.ParentField2) 541 } 542 543 if o.Parent.ParentField2 != 22 { 544 t.Fatalf("Parent.ParentField2 = %d", o.Parent.ParentField2) 545 } 546 547 if o.ParentField1 != 11 { 548 t.Fatalf("ParentField1 = %d", o.ParentField1) 549 } 550 551 if o.ChildField != 33 { 552 t.Fatalf("ChildField = %d", o.ChildField) 553 } 554 } 555 556 type jsonTagNamer struct{} 557 558 func (jsonTagNamer) FieldName(_ reflect.Type, field reflect.StructField) string { 559 if jsonTag := field.Tag.Get("json"); jsonTag != "" { 560 return jsonTag 561 } 562 return field.Name 563 } 564 565 func (jsonTagNamer) MethodName(_ reflect.Type, method reflect.Method) string { 566 return method.Name 567 } 568 569 func TestGoReflectCustomNaming(t *testing.T) { 570 571 type testStructWithJsonTags struct { 572 A string `json:"b"` // <-- script sees field "A" as property "b" 573 } 574 575 o := &testStructWithJsonTags{"Hello world"} 576 r := New() 577 r.SetFieldNameMapper(&jsonTagNamer{}) 578 r.Set("fn", func() *testStructWithJsonTags { return o }) 579 580 t.Run("get property", func(t *testing.T) { 581 v, err := r.RunString(`fn().b`) 582 if err != nil { 583 t.Fatal(err) 584 } 585 if !v.StrictEquals(newStringValue(o.A)) { 586 t.Fatalf("Expected %q, got %v", o.A, v) 587 } 588 }) 589 590 t.Run("set property", func(t *testing.T) { 591 _, err := r.RunString(`fn().b = "Hello universe"`) 592 if err != nil { 593 t.Fatal(err) 594 } 595 if o.A != "Hello universe" { 596 t.Fatalf("Expected \"Hello universe\", got %q", o.A) 597 } 598 }) 599 600 t.Run("enumerate properties", func(t *testing.T) { 601 v, err := r.RunString(`Object.keys(fn())`) 602 if err != nil { 603 t.Fatal(err) 604 } 605 if !reflect.DeepEqual(v.Export(), []interface{}{"b"}) { 606 t.Fatalf("Expected [\"b\"], got %v", v.Export()) 607 } 608 }) 609 } 610 611 func TestGoReflectCustomObjNaming(t *testing.T) { 612 613 type testStructWithJsonTags struct { 614 A string `json:"b"` // <-- script sees field "A" as property "b" 615 } 616 617 r := New() 618 r.SetFieldNameMapper(&jsonTagNamer{}) 619 620 t.Run("Set object in slice", func(t *testing.T) { 621 testSlice := &[]testStructWithJsonTags{{"Hello world"}} 622 r.Set("testslice", testSlice) 623 _, err := r.RunString(`testslice[0] = {b:"setted"}`) 624 if err != nil { 625 t.Fatal(err) 626 } 627 if (*testSlice)[0].A != "setted" { 628 t.Fatalf("Expected \"setted\", got %q", (*testSlice)[0]) 629 } 630 }) 631 632 t.Run("Set object in map", func(t *testing.T) { 633 testMap := map[string]testStructWithJsonTags{"key": {"Hello world"}} 634 r.Set("testmap", testMap) 635 _, err := r.RunString(`testmap["key"] = {b:"setted"}`) 636 if err != nil { 637 t.Fatal(err) 638 } 639 if testMap["key"].A != "setted" { 640 t.Fatalf("Expected \"setted\", got %q", testMap["key"]) 641 } 642 }) 643 644 t.Run("Add object to map", func(t *testing.T) { 645 testMap := map[string]testStructWithJsonTags{} 646 r.Set("testmap", testMap) 647 _, err := r.RunString(`testmap["newkey"] = {b:"setted"}`) 648 if err != nil { 649 t.Fatal(err) 650 } 651 if testMap["newkey"].A != "setted" { 652 t.Fatalf("Expected \"setted\", got %q", testMap["newkey"]) 653 } 654 }) 655 } 656 657 type fieldNameMapper1 struct{} 658 659 func (fieldNameMapper1) FieldName(_ reflect.Type, f reflect.StructField) string { 660 return strings.ToLower(f.Name) 661 } 662 663 func (fieldNameMapper1) MethodName(_ reflect.Type, m reflect.Method) string { 664 return m.Name 665 } 666 667 func TestNonStructAnonFields(t *testing.T) { 668 type Test1 struct { 669 M bool 670 } 671 type test3 []int 672 type Test4 []int 673 type Test2 struct { 674 test3 675 Test4 676 *Test1 677 } 678 679 const SCRIPT = ` 680 JSON.stringify(a); 681 a.m && a.test3 === undefined && a.test4.length === 2 682 ` 683 vm := New() 684 vm.SetFieldNameMapper(fieldNameMapper1{}) 685 vm.Set("a", &Test2{Test1: &Test1{M: true}, Test4: []int{1, 2}, test3: nil}) 686 v, err := vm.RunString(SCRIPT) 687 if err != nil { 688 t.Fatal(err) 689 } 690 if !v.StrictEquals(valueTrue) { 691 t.Fatalf("Unexepected result: %v", v) 692 } 693 } 694 695 func TestStructNonAddressable(t *testing.T) { 696 type S struct { 697 Field int 698 } 699 700 const SCRIPT = ` 701 "use strict"; 702 703 if (!Object.getOwnPropertyDescriptor(s, "Field").writable) { 704 throw new Error("s.Field is non-writable"); 705 } 706 707 if (!Object.getOwnPropertyDescriptor(s1, "Field").writable) { 708 throw new Error("s1.Field is non-writable"); 709 } 710 711 s1.Field = 42; 712 s.Field = 43; 713 s; 714 ` 715 716 var s S 717 vm := New() 718 vm.Set("s", s) 719 vm.Set("s1", &s) 720 v, err := vm.RunString(SCRIPT) 721 if err != nil { 722 t.Fatal(err) 723 } 724 exp := v.Export() 725 if s1, ok := exp.(S); ok { 726 if s1.Field != 43 { 727 t.Fatal(s1) 728 } 729 } else { 730 t.Fatalf("Wrong type: %T", exp) 731 } 732 if s.Field != 42 { 733 t.Fatalf("Unexpected s.Field value: %d", s.Field) 734 } 735 } 736 737 type testFieldMapper struct { 738 } 739 740 func (testFieldMapper) FieldName(_ reflect.Type, f reflect.StructField) string { 741 if tag := f.Tag.Get("js"); tag != "" { 742 if tag == "-" { 743 return "" 744 } 745 return tag 746 } 747 748 return f.Name 749 } 750 751 func (testFieldMapper) MethodName(_ reflect.Type, m reflect.Method) string { 752 return m.Name 753 } 754 755 func TestHidingAnonField(t *testing.T) { 756 type InnerType struct { 757 AnotherField string 758 } 759 760 type OuterType struct { 761 InnerType `js:"-"` 762 SomeField string 763 } 764 765 const SCRIPT = ` 766 var a = Object.getOwnPropertyNames(o); 767 if (a.length !== 2) { 768 throw new Error("unexpected length: " + a.length); 769 } 770 771 if (a.indexOf("SomeField") === -1) { 772 throw new Error("no SomeField"); 773 } 774 775 if (a.indexOf("AnotherField") === -1) { 776 throw new Error("no SomeField"); 777 } 778 ` 779 780 var o OuterType 781 782 vm := New() 783 vm.SetFieldNameMapper(testFieldMapper{}) 784 vm.Set("o", &o) 785 786 _, err := vm.RunString(SCRIPT) 787 if err != nil { 788 t.Fatal(err) 789 } 790 } 791 792 func TestFieldOverriding(t *testing.T) { 793 type InnerType struct { 794 AnotherField string 795 AnotherField1 string 796 } 797 798 type OuterType struct { 799 InnerType `js:"-"` 800 SomeField string 801 AnotherField string `js:"-"` 802 AnotherField1 string 803 } 804 805 const SCRIPT = ` 806 if (o.SomeField !== "SomeField") { 807 throw new Error("SomeField"); 808 } 809 810 if (o.AnotherField !== "AnotherField inner") { 811 throw new Error("AnotherField"); 812 } 813 814 if (o.AnotherField1 !== "AnotherField1 outer") { 815 throw new Error("AnotherField1"); 816 } 817 818 if (o.InnerType) { 819 throw new Error("InnerType is present"); 820 } 821 ` 822 823 o := OuterType{ 824 InnerType: InnerType{ 825 AnotherField: "AnotherField inner", 826 AnotherField1: "AnotherField1 inner", 827 }, 828 SomeField: "SomeField", 829 AnotherField: "AnotherField outer", 830 AnotherField1: "AnotherField1 outer", 831 } 832 833 vm := New() 834 vm.SetFieldNameMapper(testFieldMapper{}) 835 vm.Set("o", &o) 836 837 _, err := vm.RunString(SCRIPT) 838 if err != nil { 839 t.Fatal(err) 840 } 841 } 842 843 func TestDefinePropertyUnexportedJsName(t *testing.T) { 844 type T struct { 845 Field int 846 unexported int 847 } 848 849 vm := New() 850 vm.SetFieldNameMapper(fieldNameMapper1{}) 851 vm.Set("f", &T{unexported: 0}) 852 853 _, err := vm.RunString(` 854 "use strict"; 855 Object.defineProperty(f, "field", {value: 42}); 856 if (f.field !== 42) { 857 throw new Error("Unexpected value: " + f.field); 858 } 859 if (f.hasOwnProperty("unexported")) { 860 throw new Error("hasOwnProperty('unexported') is true"); 861 } 862 var thrown; 863 try { 864 Object.defineProperty(f, "unexported", {value: 1}); 865 } catch (e) { 866 thrown = e; 867 } 868 if (!(thrown instanceof TypeError)) { 869 throw new Error("Unexpected error: ", thrown); 870 } 871 `) 872 if err != nil { 873 t.Fatal(err) 874 } 875 } 876 877 type fieldNameMapperToLower struct{} 878 879 func (fieldNameMapperToLower) FieldName(_ reflect.Type, f reflect.StructField) string { 880 return strings.ToLower(f.Name) 881 } 882 883 func (fieldNameMapperToLower) MethodName(_ reflect.Type, m reflect.Method) string { 884 return strings.ToLower(m.Name) 885 } 886 887 func TestHasOwnPropertyUnexportedJsName(t *testing.T) { 888 vm := New() 889 vm.SetFieldNameMapper(fieldNameMapperToLower{}) 890 vm.Set("f", &testGoReflectMethod_O{}) 891 892 _, err := vm.RunString(` 893 "use strict"; 894 if (!f.hasOwnProperty("test")) { 895 throw new Error("hasOwnProperty('test') returned false"); 896 } 897 if (!f.hasOwnProperty("method")) { 898 throw new Error("hasOwnProperty('method') returned false"); 899 } 900 `) 901 if err != nil { 902 t.Fatal(err) 903 } 904 } 905 906 func BenchmarkGoReflectGet(b *testing.B) { 907 type parent struct { 908 field, Test1, Test2, Test3, Test4, Test5, Test string 909 } 910 911 type child struct { 912 parent 913 Test6 string 914 } 915 916 b.StopTimer() 917 vm := New() 918 919 b.StartTimer() 920 for i := 0; i < b.N; i++ { 921 v := vm.ToValue(child{parent: parent{Test: "Test", field: ""}}).(*Object) 922 v.Get("Test") 923 } 924 } 925 926 func TestNestedStructSet(t *testing.T) { 927 type B struct { 928 Field int 929 } 930 type A struct { 931 B B 932 } 933 934 const SCRIPT = ` 935 'use strict'; 936 a.B.Field++; 937 if (a1.B.Field != 1) { 938 throw new Error("a1.B.Field = " + a1.B.Field); 939 } 940 var d = Object.getOwnPropertyDescriptor(a1.B, "Field"); 941 if (!d.writable) { 942 throw new Error("a1.B is not writable"); 943 } 944 a1.B.Field = 42; 945 a1; 946 ` 947 a := A{ 948 B: B{ 949 Field: 1, 950 }, 951 } 952 vm := New() 953 vm.Set("a", &a) 954 vm.Set("a1", a) 955 v, err := vm.RunString(SCRIPT) 956 if err != nil { 957 t.Fatal(err) 958 } 959 exp := v.Export() 960 if v, ok := exp.(A); ok { 961 if v.B.Field != 42 { 962 t.Fatal(v) 963 } 964 } else { 965 t.Fatalf("Wrong type: %T", exp) 966 } 967 968 if v := a.B.Field; v != 2 { 969 t.Fatalf("Unexpected a.B.Field: %d", v) 970 } 971 } 972 973 func TestStructNonAddressableAnonStruct(t *testing.T) { 974 975 type C struct { 976 Z int64 977 X string 978 } 979 980 type B struct { 981 C 982 Y string 983 } 984 985 type A struct { 986 B B 987 } 988 989 a := A{ 990 B: B{ 991 C: C{ 992 Z: 1, 993 X: "X2", 994 }, 995 Y: "Y3", 996 }, 997 } 998 const SCRIPT = ` 999 "use strict"; 1000 var s = JSON.stringify(a); 1001 s; 1002 ` 1003 1004 vm := New() 1005 vm.Set("a", &a) 1006 v, err := vm.RunString(SCRIPT) 1007 if err != nil { 1008 t.Fatal(err) 1009 } 1010 1011 expected := `{"B":{"C":{"Z":1,"X":"X2"},"Z":1,"X":"X2","Y":"Y3"}}` 1012 if expected != v.String() { 1013 t.Fatalf("Expected '%s', got '%s'", expected, v.String()) 1014 } 1015 1016 } 1017 1018 func TestTagFieldNameMapperInvalidId(t *testing.T) { 1019 vm := New() 1020 vm.SetFieldNameMapper(TagFieldNameMapper("json", true)) 1021 type S struct { 1022 Field int `json:"-"` 1023 } 1024 vm.Set("s", S{Field: 42}) 1025 res, err := vm.RunString(`s.hasOwnProperty("field") || s.hasOwnProperty("Field")`) 1026 if err != nil { 1027 t.Fatal(err) 1028 } 1029 if res != valueFalse { 1030 t.Fatalf("Unexpected result: %v", res) 1031 } 1032 } 1033 1034 func TestPrimitivePtr(t *testing.T) { 1035 vm := New() 1036 s := "test" 1037 vm.Set("s", &s) 1038 res, err := vm.RunString(`s instanceof String && s == "test"`) // note non-strict equality 1039 if err != nil { 1040 t.Fatal(err) 1041 } 1042 if v := res.ToBoolean(); !v { 1043 t.Fatalf("value: %#v", res) 1044 } 1045 s = "test1" 1046 res, err = vm.RunString(`s == "test1"`) 1047 if err != nil { 1048 t.Fatal(err) 1049 } 1050 if v := res.ToBoolean(); !v { 1051 t.Fatalf("value: %#v", res) 1052 } 1053 } 1054 1055 func TestStringer(t *testing.T) { 1056 vm := New() 1057 vm.Set("e", errors.New("test")) 1058 res, err := vm.RunString("e.toString()") 1059 if err != nil { 1060 t.Fatal(err) 1061 } 1062 if v := res.Export(); v != "test" { 1063 t.Fatalf("v: %v", v) 1064 } 1065 } 1066 1067 func ExampleTagFieldNameMapper() { 1068 vm := New() 1069 vm.SetFieldNameMapper(TagFieldNameMapper("json", true)) 1070 type S struct { 1071 Field int `json:"field"` 1072 } 1073 vm.Set("s", S{Field: 42}) 1074 res, _ := vm.RunString(`s.field`) 1075 fmt.Println(res.Export()) 1076 // Output: 42 1077 } 1078 1079 func ExampleUncapFieldNameMapper() { 1080 vm := New() 1081 s := testGoReflectMethod_O{ 1082 Test: "passed", 1083 } 1084 vm.SetFieldNameMapper(UncapFieldNameMapper()) 1085 vm.Set("s", s) 1086 res, _ := vm.RunString(`s.test + " and " + s.method("passed too")`) 1087 fmt.Println(res.Export()) 1088 // Output: passed and passed too 1089 } 1090 1091 func TestGoReflectWithProto(t *testing.T) { 1092 type S struct { 1093 Field int 1094 } 1095 var s S 1096 vm := New() 1097 vm.Set("s", &s) 1098 vm.testScriptWithTestLib(` 1099 (function() { 1100 'use strict'; 1101 var proto = { 1102 Field: "protoField", 1103 test: 42 1104 }; 1105 var test1Holder; 1106 Object.defineProperty(proto, "test1", { 1107 set: function(v) { 1108 test1Holder = v; 1109 }, 1110 get: function() { 1111 return test1Holder; 1112 } 1113 }); 1114 Object.setPrototypeOf(s, proto); 1115 assert.sameValue(s.Field, 0, "s.Field"); 1116 s.Field = 2; 1117 assert.sameValue(s.Field, 2, "s.Field"); 1118 assert.sameValue(s.test, 42, "s.test"); 1119 assert.throws(TypeError, function() { 1120 Object.defineProperty(s, "test", {value: 43}); 1121 }); 1122 test1Holder = 1; 1123 assert.sameValue(s.test1, 1, "s.test1"); 1124 s.test1 = 2; 1125 assert.sameValue(test1Holder, 2, "test1Holder"); 1126 })(); 1127 `, _undefined, t) 1128 } 1129 1130 func TestGoReflectSymbols(t *testing.T) { 1131 type S struct { 1132 Field int 1133 } 1134 var s S 1135 vm := New() 1136 vm.Set("s", &s) 1137 _, err := vm.RunString(` 1138 'use strict'; 1139 var sym = Symbol(66); 1140 s[sym] = "Test"; 1141 if (s[sym] !== "Test") { 1142 throw new Error("s[sym]=" + s[sym]); 1143 } 1144 `) 1145 if err != nil { 1146 t.Fatal(err) 1147 } 1148 } 1149 1150 func TestGoReflectSymbolEqualityQuirk(t *testing.T) { 1151 type Field struct { 1152 } 1153 type S struct { 1154 Field *Field 1155 } 1156 var s = S{ 1157 Field: &Field{}, 1158 } 1159 vm := New() 1160 vm.Set("s", &s) 1161 res, err := vm.RunString(` 1162 var sym = Symbol(66); 1163 var field1 = s.Field; 1164 field1[sym] = true; 1165 var field2 = s.Field; 1166 // Because a wrapper is created every time the property is accessed 1167 // field1 and field2 will be different instances of the wrapper. 1168 // Symbol properties only exist in the wrapper, they cannot be placed into the original Go value, 1169 // hence the following: 1170 field1 === field2 && field1[sym] === true && field2[sym] === undefined; 1171 `) 1172 if err != nil { 1173 t.Fatal(err) 1174 } 1175 if res != valueTrue { 1176 t.Fatal(res) 1177 } 1178 } 1179 1180 func TestGoObj__Proto__(t *testing.T) { 1181 type S struct { 1182 Field int 1183 } 1184 vm := New() 1185 vm.Set("s", S{}) 1186 vm.Set("m", map[string]interface{}{}) 1187 vm.Set("mr", map[int]string{}) 1188 vm.Set("a", []interface{}{}) 1189 vm.Set("ar", []string{}) 1190 _, err := vm.RunString(` 1191 function f(s, expectedCtor, prefix) { 1192 if (s.__proto__ !== expectedCtor.prototype) { 1193 throw new Error(prefix + ": __proto__: " + s.__proto__); 1194 } 1195 s.__proto__ = null; 1196 if (s.__proto__ !== undefined) { // as there is no longer a prototype, there is no longer the __proto__ property 1197 throw new Error(prefix + ": __proto__ is not undefined: " + s.__proto__); 1198 } 1199 var proto = Object.getPrototypeOf(s); 1200 if (proto !== null) { 1201 throw new Error(prefix + ": proto is not null: " + proto); 1202 } 1203 } 1204 f(s, Object, "struct"); 1205 f(m, Object, "simple map"); 1206 f(mr, Object, "reflect map"); 1207 f(a, Array, "slice"); 1208 f(ar, Array, "reflect slice"); 1209 `) 1210 if err != nil { 1211 t.Fatal(err) 1212 } 1213 } 1214 1215 func TestGoReflectUnicodeProps(t *testing.T) { 1216 type S struct { 1217 Тест string 1218 } 1219 vm := New() 1220 var s S 1221 vm.Set("s", &s) 1222 _, err := vm.RunString(` 1223 if (!s.hasOwnProperty("Тест")) { 1224 throw new Error("hasOwnProperty"); 1225 } 1226 `) 1227 if err != nil { 1228 t.Fatal(err) 1229 } 1230 } 1231 1232 func TestGoReflectPreserveType(t *testing.T) { 1233 vm := New() 1234 var expect = time.Duration(math.MaxInt64) 1235 vm.Set(`make`, func() time.Duration { 1236 return expect 1237 }) 1238 vm.Set(`handle`, func(d time.Duration) { 1239 if d.String() != expect.String() { 1240 t.Fatal(`expect`, expect, `, but get`, d) 1241 } 1242 }) 1243 _, e := vm.RunString(` 1244 var d=make() 1245 handle(d) 1246 `) 1247 if e != nil { 1248 t.Fatal(e) 1249 } 1250 } 1251 1252 func TestGoReflectCopyOnWrite(t *testing.T) { 1253 type Inner struct { 1254 Field int 1255 } 1256 type S struct { 1257 I Inner 1258 } 1259 var s S 1260 s.I.Field = 1 1261 1262 vm := New() 1263 vm.Set("s", &s) 1264 _, err := vm.RunString(` 1265 if (s.I.Field !== 1) { 1266 throw new Error("s.I.Field: " + s.I.Field); 1267 } 1268 1269 let tmp = s.I; // tmp becomes a reference to s.I 1270 if (tmp.Field !== 1) { 1271 throw new Error("tmp.Field: " + tmp.Field); 1272 } 1273 1274 s.I.Field = 2; 1275 if (s.I.Field !== 2) { 1276 throw new Error("s.I.Field (1): " + s.I.Field); 1277 } 1278 if (tmp.Field !== 2) { 1279 throw new Error("tmp.Field (1): " + tmp.Field); 1280 } 1281 1282 s.I = {Field: 3}; // at this point tmp is changed to a copy 1283 if (s.I.Field !== 3) { 1284 throw new Error("s.I.Field (2): " + s.I.Field); 1285 } 1286 if (tmp.Field !== 2) { 1287 throw new Error("tmp.Field (2): " + tmp.Field); 1288 } 1289 `) 1290 1291 if err != nil { 1292 t.Fatal(err) 1293 } 1294 } 1295 1296 func TestReflectSetReflectValue(t *testing.T) { 1297 o := []testGoReflectMethod_O{{}} 1298 vm := New() 1299 vm.Set("o", o) 1300 _, err := vm.RunString(` 1301 const t = o[0]; 1302 t.Set("a"); 1303 o[0] = {}; 1304 o[0].Set("b"); 1305 if (t.Get() !== "a") { 1306 throw new Error(); 1307 } 1308 `) 1309 1310 if err != nil { 1311 t.Fatal(err) 1312 } 1313 } 1314 1315 func TestReflectOverwriteReflectMap(t *testing.T) { 1316 vm := New() 1317 type S struct { 1318 M map[int]interface{} 1319 } 1320 var s S 1321 s.M = map[int]interface{}{ 1322 0: true, 1323 } 1324 vm.Set("s", &s) 1325 _, err := vm.RunString(` 1326 s.M = {1: false}; 1327 `) 1328 if err != nil { 1329 t.Fatal(err) 1330 } 1331 if _, exists := s.M[0]; exists { 1332 t.Fatal(s) 1333 } 1334 } 1335 1336 type testBoolS bool 1337 1338 func (testBoolS) String() string { 1339 return "B" 1340 } 1341 1342 type testIntS int 1343 1344 func (testIntS) String() string { 1345 return "I" 1346 } 1347 1348 type testStringS string 1349 1350 func (testStringS) String() string { 1351 return "S" 1352 } 1353 1354 func TestGoReflectToPrimitive(t *testing.T) { 1355 vm := New() 1356 1357 f := func(expr string, expected Value, t *testing.T) { 1358 v, err := vm.RunString(expr) 1359 if err != nil { 1360 t.Fatal(err) 1361 } 1362 if IsNaN(expected) { 1363 if IsNaN(v) { 1364 return 1365 } 1366 } else { 1367 if v.StrictEquals(expected) { 1368 return 1369 } 1370 } 1371 t.Fatalf("%s: expected: %v, actual: %v", expr, expected, v) 1372 } 1373 1374 t.Run("Not Stringers", func(t *testing.T) { 1375 type Bool bool 1376 var b Bool = true 1377 1378 t.Run("Bool", func(t *testing.T) { 1379 vm.Set("b", b) 1380 f("+b", intToValue(1), t) 1381 f("`${b}`", asciiString("true"), t) 1382 f("b.toString()", asciiString("true"), t) 1383 f("b.valueOf()", valueTrue, t) 1384 }) 1385 1386 t.Run("*Bool", func(t *testing.T) { 1387 vm.Set("b", &b) 1388 f("+b", intToValue(1), t) 1389 f("`${b}`", asciiString("true"), t) 1390 f("b.toString()", asciiString("true"), t) 1391 f("b.valueOf()", valueTrue, t) 1392 }) 1393 1394 type Int int 1395 var i Int = 1 1396 1397 t.Run("Int", func(t *testing.T) { 1398 vm.Set("i", i) 1399 f("+i", intToValue(1), t) 1400 f("`${i}`", asciiString("1"), t) 1401 f("i.toString()", asciiString("1"), t) 1402 f("i.valueOf()", intToValue(1), t) 1403 }) 1404 1405 t.Run("*Int", func(t *testing.T) { 1406 vm.Set("i", &i) 1407 f("+i", intToValue(1), t) 1408 f("`${i}`", asciiString("1"), t) 1409 f("i.toString()", asciiString("1"), t) 1410 f("i.valueOf()", intToValue(1), t) 1411 }) 1412 1413 type Uint uint 1414 var ui Uint = 1 1415 1416 t.Run("Uint", func(t *testing.T) { 1417 vm.Set("ui", ui) 1418 f("+ui", intToValue(1), t) 1419 f("`${ui}`", asciiString("1"), t) 1420 f("ui.toString()", asciiString("1"), t) 1421 f("ui.valueOf()", intToValue(1), t) 1422 }) 1423 1424 t.Run("*Uint", func(t *testing.T) { 1425 vm.Set("ui", &i) 1426 f("+ui", intToValue(1), t) 1427 f("`${ui}`", asciiString("1"), t) 1428 f("ui.toString()", asciiString("1"), t) 1429 f("ui.valueOf()", intToValue(1), t) 1430 }) 1431 1432 type Float float64 1433 var fl Float = 1.1 1434 1435 t.Run("Float", func(t *testing.T) { 1436 vm.Set("fl", fl) 1437 f("+fl", floatToValue(1.1), t) 1438 f("`${fl}`", asciiString("1.1"), t) 1439 f("fl.toString()", asciiString("1.1"), t) 1440 f("fl.valueOf()", floatToValue(1.1), t) 1441 }) 1442 1443 t.Run("*Float", func(t *testing.T) { 1444 vm.Set("fl", &fl) 1445 f("+fl", floatToValue(1.1), t) 1446 f("`${fl}`", asciiString("1.1"), t) 1447 f("fl.toString()", asciiString("1.1"), t) 1448 f("fl.valueOf()", floatToValue(1.1), t) 1449 }) 1450 1451 fl = Float(math.Inf(1)) 1452 t.Run("FloatInf", func(t *testing.T) { 1453 vm.Set("fl", fl) 1454 f("+fl", _positiveInf, t) 1455 f("fl.toString()", asciiString("Infinity"), t) 1456 }) 1457 1458 type Empty struct{} 1459 1460 var e Empty 1461 t.Run("Empty", func(t *testing.T) { 1462 vm.Set("e", &e) 1463 f("+e", _NaN, t) 1464 f("`${e}`", asciiString("[object Object]"), t) 1465 f("e.toString()", asciiString("[object Object]"), t) 1466 f("e.valueOf()", vm.ToValue(&e), t) 1467 }) 1468 }) 1469 1470 t.Run("Stringers", func(t *testing.T) { 1471 var b testBoolS = true 1472 t.Run("Bool", func(t *testing.T) { 1473 vm.Set("b", b) 1474 f("`${b}`", asciiString("B"), t) 1475 f("b.toString()", asciiString("B"), t) 1476 f("b.valueOf()", valueTrue, t) 1477 f("+b", intToValue(1), t) 1478 }) 1479 1480 t.Run("*Bool", func(t *testing.T) { 1481 vm.Set("b", &b) 1482 f("`${b}`", asciiString("B"), t) 1483 f("b.toString()", asciiString("B"), t) 1484 f("b.valueOf()", valueTrue, t) 1485 f("+b", intToValue(1), t) 1486 }) 1487 1488 var i testIntS = 1 1489 t.Run("Int", func(t *testing.T) { 1490 vm.Set("i", i) 1491 f("`${i}`", asciiString("I"), t) 1492 f("i.toString()", asciiString("I"), t) 1493 f("i.valueOf()", intToValue(1), t) 1494 f("+i", intToValue(1), t) 1495 }) 1496 1497 t.Run("*Int", func(t *testing.T) { 1498 vm.Set("i", &i) 1499 f("`${i}`", asciiString("I"), t) 1500 f("i.toString()", asciiString("I"), t) 1501 f("i.valueOf()", intToValue(1), t) 1502 f("+i", intToValue(1), t) 1503 }) 1504 1505 var s testStringS 1506 t.Run("String", func(t *testing.T) { 1507 vm.Set("s", s) 1508 f("`${s}`", asciiString("S"), t) 1509 f("s.toString()", asciiString("S"), t) 1510 f("s.valueOf()", asciiString("S"), t) 1511 f("+s", _NaN, t) 1512 }) 1513 1514 t.Run("*String", func(t *testing.T) { 1515 vm.Set("s", &s) 1516 f("`${s}`", asciiString("S"), t) 1517 f("s.toString()", asciiString("S"), t) 1518 f("s.valueOf()", asciiString("S"), t) 1519 f("+s", _NaN, t) 1520 }) 1521 }) 1522 } 1523 1524 type testGoReflectFuncRt struct { 1525 } 1526 1527 func (*testGoReflectFuncRt) M(call FunctionCall, r *Runtime) Value { 1528 if r == nil { 1529 panic(typeError("Runtime is nil")) 1530 } 1531 return call.Argument(0) 1532 } 1533 1534 func (*testGoReflectFuncRt) C(call ConstructorCall, r *Runtime) *Object { 1535 if r == nil { 1536 panic(typeError("Runtime is nil in constructor")) 1537 } 1538 call.This.Set("r", call.Argument(0)) 1539 return nil 1540 } 1541 1542 func TestGoReflectFuncWithRuntime(t *testing.T) { 1543 vm := New() 1544 var s testGoReflectFuncRt 1545 vm.Set("s", &s) 1546 res, err := vm.RunString("s.M(true)") 1547 if err != nil { 1548 t.Fatal(err) 1549 } 1550 if res != valueTrue { 1551 t.Fatal(res) 1552 } 1553 1554 res, err = vm.RunString("new s.C(true).r") 1555 if err != nil { 1556 t.Fatal(err) 1557 } 1558 if res != valueTrue { 1559 t.Fatal(res) 1560 } 1561 } 1562 1563 func TestGoReflectDefaultToString(t *testing.T) { 1564 var s testStringS 1565 vm := New() 1566 v := vm.ToValue(s).(*Object) 1567 v.Delete("toString") 1568 v.Delete("valueOf") 1569 vm.Set("s", v) 1570 _, err := vm.RunString(` 1571 class S { 1572 toString() { 1573 return "X"; 1574 } 1575 } 1576 1577 if (s.toString() !== "S") { 1578 throw new Error(s.toString()); 1579 } 1580 if (("" + s) !== "S") { 1581 throw new Error("" + s); 1582 } 1583 1584 Object.setPrototypeOf(s, S.prototype); 1585 if (s.toString() !== "X") { 1586 throw new Error(s.toString()); 1587 } 1588 if (("" + s) !== "X") { 1589 throw new Error("" + s); 1590 } 1591 `) 1592 if err != nil { 1593 t.Fatal(err) 1594 } 1595 }