123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- package goja
- import (
- "reflect"
- "strings"
- "testing"
- )
- func TestGoReflectGet(t *testing.T) {
- const SCRIPT = `
- o.X + o.Y;
- `
- type O struct {
- X int
- Y string
- }
- r := New()
- o := O{X: 4, Y: "2"}
- r.Set("o", o)
- v, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if s, ok := v.assertString(); ok {
- if s.String() != "42" {
- t.Fatalf("Unexpected string: %s", s)
- }
- } else {
- t.Fatalf("Unexpected type: %s", v)
- }
- }
- func TestGoReflectSet(t *testing.T) {
- const SCRIPT = `
- o.X++;
- o.Y += "P";
- `
- type O struct {
- X int
- Y string
- }
- r := New()
- o := O{X: 4, Y: "2"}
- r.Set("o", &o)
- _, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if o.X != 5 {
- t.Fatalf("Unexpected X: %d", o.X)
- }
- if o.Y != "2P" {
- t.Fatalf("Unexpected Y: %s", o.Y)
- }
- }
- type TestGoReflectMethod_Struct struct {
- }
- func (s *TestGoReflectMethod_Struct) M() int {
- return 42
- }
- func TestGoReflectEnumerate(t *testing.T) {
- const SCRIPT = `
- var hasX = false;
- var hasY = false;
- for (var key in o) {
- switch (key) {
- case "X":
- if (hasX) {
- throw "Already have X";
- }
- hasX = true;
- break;
- case "Y":
- if (hasY) {
- throw "Already have Y";
- }
- hasY = true;
- break;
- default:
- throw "Unexpected property: " + key;
- }
- }
- hasX && hasY;
- `
- type S struct {
- X, Y int
- }
- r := New()
- r.Set("o", S{X: 40, Y: 2})
- v, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(valueTrue) {
- t.Fatalf("Expected true, got %v", v)
- }
- }
- func TestGoReflectCustomIntUnbox(t *testing.T) {
- const SCRIPT = `
- i + 2;
- `
- type CustomInt int
- var i CustomInt = 40
- r := New()
- r.Set("i", i)
- v, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(intToValue(42)) {
- t.Fatalf("Expected int 42, got %v", v)
- }
- }
- func TestGoReflectPreserveCustomType(t *testing.T) {
- const SCRIPT = `
- i;
- `
- type CustomInt int
- var i CustomInt = 42
- r := New()
- r.Set("i", i)
- v, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- ve := v.Export()
- if ii, ok := ve.(CustomInt); ok {
- if ii != i {
- t.Fatalf("Wrong value: %v", ii)
- }
- } else {
- t.Fatalf("Wrong type: %v", ve)
- }
- }
- func TestGoReflectCustomIntValueOf(t *testing.T) {
- const SCRIPT = `
- if (i instanceof Number) {
- i.valueOf();
- } else {
- throw new Error("Value is not a number");
- }
- `
- type CustomInt int
- var i CustomInt = 42
- r := New()
- r.Set("i", i)
- v, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(intToValue(42)) {
- t.Fatalf("Expected int 42, got %v", v)
- }
- }
- func TestGoReflectEqual(t *testing.T) {
- const SCRIPT = `
- x === y;
- `
- type CustomInt int
- var x CustomInt = 42
- var y CustomInt = 42
- r := New()
- r.Set("x", x)
- r.Set("y", y)
- v, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(valueTrue) {
- t.Fatalf("Expected true, got %v", v)
- }
- }
- type testGoReflectMethod_O struct {
- field string
- Test string
- }
- func (o testGoReflectMethod_O) Method(s string) string {
- return o.field + s
- }
- func TestGoReflectMethod(t *testing.T) {
- const SCRIPT = `
- o.Method(" 123")
- `
- o := testGoReflectMethod_O{
- field: "test",
- }
- r := New()
- r.Set("o", &o)
- v, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(asciiString("test 123")) {
- t.Fatalf("Expected 'test 123', got %v", v)
- }
- }
- func (o *testGoReflectMethod_O) Set(s string) {
- o.field = s
- }
- func (o *testGoReflectMethod_O) Get() string {
- return o.field
- }
- func TestGoReflectMethodPtr(t *testing.T) {
- const SCRIPT = `
- o.Set("42")
- o.Get()
- `
- o := testGoReflectMethod_O{
- field: "test",
- }
- r := New()
- r.Set("o", &o)
- v, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(asciiString("42")) {
- t.Fatalf("Expected '42', got %v", v)
- }
- }
- func TestGoReflectProp(t *testing.T) {
- const SCRIPT = `
- var d1 = Object.getOwnPropertyDescriptor(o, "Get");
- var d2 = Object.getOwnPropertyDescriptor(o, "Test");
- !d1.writable && !d1.configurable && d2.writable && !d2.configurable;
- `
- o := testGoReflectMethod_O{
- field: "test",
- }
- r := New()
- r.Set("o", &o)
- v, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(valueTrue) {
- t.Fatalf("Expected true, got %v", v)
- }
- }
- func TestGoReflectRedefineFieldSuccess(t *testing.T) {
- const SCRIPT = `
- Object.defineProperty(o, "Test", {value: "AAA"}) === o;
- `
- o := testGoReflectMethod_O{}
- r := New()
- r.Set("o", &o)
- v, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(valueTrue) {
- t.Fatalf("Expected true, got %v", v)
- }
- if o.Test != "AAA" {
- t.Fatalf("Expected 'AAA', got '%s'", o.Test)
- }
- }
- func TestGoReflectRedefineFieldNonWritable(t *testing.T) {
- const SCRIPT = `
- var thrown = false;
- try {
- Object.defineProperty(o, "Test", {value: "AAA", writable: false});
- } catch (e) {
- if (e instanceof TypeError) {
- thrown = true;
- } else {
- throw e;
- }
- }
- thrown;
- `
- o := testGoReflectMethod_O{Test: "Test"}
- r := New()
- r.Set("o", &o)
- v, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(valueTrue) {
- t.Fatalf("Expected true, got %v", v)
- }
- if o.Test != "Test" {
- t.Fatalf("Expected 'Test', got: '%s'", o.Test)
- }
- }
- func TestGoReflectRedefineFieldConfigurable(t *testing.T) {
- const SCRIPT = `
- var thrown = false;
- try {
- Object.defineProperty(o, "Test", {value: "AAA", configurable: true});
- } catch (e) {
- if (e instanceof TypeError) {
- thrown = true;
- } else {
- throw e;
- }
- }
- thrown;
- `
- o := testGoReflectMethod_O{Test: "Test"}
- r := New()
- r.Set("o", &o)
- v, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(valueTrue) {
- t.Fatalf("Expected true, got %v", v)
- }
- if o.Test != "Test" {
- t.Fatalf("Expected 'Test', got: '%s'", o.Test)
- }
- }
- func TestGoReflectRedefineMethod(t *testing.T) {
- const SCRIPT = `
- var thrown = false;
- try {
- Object.defineProperty(o, "Method", {value: "AAA", configurable: true});
- } catch (e) {
- if (e instanceof TypeError) {
- thrown = true;
- } else {
- throw e;
- }
- }
- thrown;
- `
- o := testGoReflectMethod_O{Test: "Test"}
- r := New()
- r.Set("o", &o)
- v, err := r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(valueTrue) {
- t.Fatalf("Expected true, got %v", v)
- }
- }
- func TestGoReflectEmbeddedStruct(t *testing.T) {
- const SCRIPT = `
- if (o.ParentField2 !== "ParentField2") {
- throw new Error("ParentField2 = " + o.ParentField2);
- }
- if (o.Parent.ParentField2 !== 2) {
- throw new Error("o.Parent.ParentField2 = " + o.Parent.ParentField2);
- }
- if (o.ParentField1 !== 1) {
- throw new Error("o.ParentField1 = " + o.ParentField1);
- }
- if (o.ChildField !== 3) {
- throw new Error("o.ChildField = " + o.ChildField);
- }
- var keys = {};
- for (var k in o) {
- if (keys[k]) {
- throw new Error("Duplicate key: " + k);
- }
- keys[k] = true;
- }
- var expectedKeys = ["ParentField2", "ParentField1", "Parent", "ChildField"];
- for (var i in expectedKeys) {
- if (!keys[expectedKeys[i]]) {
- throw new Error("Missing key in enumeration: " + expectedKeys[i]);
- }
- delete keys[expectedKeys[i]];
- }
- var remainingKeys = Object.keys(keys);
- if (remainingKeys.length > 0) {
- throw new Error("Unexpected keys: " + remainingKeys);
- }
- o.ParentField2 = "ParentField22";
- o.Parent.ParentField2 = 22;
- o.ParentField1 = 11;
- o.ChildField = 33;
- `
- type Parent struct {
- ParentField1 int
- ParentField2 int
- }
- type Child struct {
- ParentField2 string
- Parent
- ChildField int
- }
- vm := New()
- o := Child{
- Parent: Parent{
- ParentField1: 1,
- ParentField2: 2,
- },
- ParentField2: "ParentField2",
- ChildField: 3,
- }
- vm.Set("o", &o)
- _, err := vm.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if o.ParentField2 != "ParentField22" {
- t.Fatalf("ParentField2 = %q", o.ParentField2)
- }
- if o.Parent.ParentField2 != 22 {
- t.Fatalf("Parent.ParentField2 = %d", o.Parent.ParentField2)
- }
- if o.ParentField1 != 11 {
- t.Fatalf("ParentField1 = %d", o.ParentField1)
- }
- if o.ChildField != 33 {
- t.Fatalf("ChildField = %d", o.ChildField)
- }
- }
- type jsonTagNamer struct{}
- func (*jsonTagNamer) FieldName(t reflect.Type, field reflect.StructField) string {
- if jsonTag := field.Tag.Get("json"); jsonTag != "" {
- return jsonTag
- }
- return field.Name
- }
- func (*jsonTagNamer) MethodName(t reflect.Type, method reflect.Method) string {
- return method.Name
- }
- func TestGoReflectCustomNaming(t *testing.T) {
- type testStructWithJsonTags struct {
- A string `json:"b"` // <-- script sees field "A" as property "b"
- }
- o := &testStructWithJsonTags{"Hello world"}
- r := New()
- r.SetFieldNameMapper(&jsonTagNamer{})
- r.Set("fn", func() *testStructWithJsonTags { return o })
- t.Run("get property", func(t *testing.T) {
- v, err := r.RunString(`fn().b`)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(newStringValue(o.A)) {
- t.Fatalf("Expected %q, got %v", o.A, v)
- }
- })
- t.Run("set property", func(t *testing.T) {
- _, err := r.RunString(`fn().b = "Hello universe"`)
- if err != nil {
- t.Fatal(err)
- }
- if o.A != "Hello universe" {
- t.Fatalf("Expected \"Hello universe\", got %q", o.A)
- }
- })
- t.Run("enumerate properties", func(t *testing.T) {
- v, err := r.RunString(`Object.keys(fn())`)
- if err != nil {
- t.Fatal(err)
- }
- if !reflect.DeepEqual(v.Export(), []interface{}{"b"}) {
- t.Fatalf("Expected [\"b\"], got %v", v.Export())
- }
- })
- }
- type fieldNameMapper1 struct{}
- func (fieldNameMapper1) FieldName(t reflect.Type, f reflect.StructField) string {
- return strings.ToLower(f.Name)
- }
- func (fieldNameMapper1) MethodName(t reflect.Type, m reflect.Method) string {
- return m.Name
- }
- func TestNonStructAnonFields(t *testing.T) {
- type Test1 struct {
- M bool
- }
- type test3 []int
- type Test4 []int
- type Test2 struct {
- test3
- Test4
- *Test1
- }
- const SCRIPT = `
- JSON.stringify(a);
- a.m && a.test3 === undefined && a.test4.length === 2
- `
- vm := New()
- vm.SetFieldNameMapper(fieldNameMapper1{})
- vm.Set("a", &Test2{Test1: &Test1{M: true}, Test4: []int{1, 2}})
- v, err := vm.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(valueTrue) {
- t.Fatalf("Unexepected result: %v", v)
- }
- }
- func BenchmarkGoReflectGet(b *testing.B) {
- type parent struct {
- field, Test1, Test2, Test3, Test4, Test5, Test string
- }
- type child struct {
- parent
- Test6 string
- }
- b.StopTimer()
- vm := New()
- b.StartTimer()
- for i := 0; i < b.N; i++ {
- v := vm.ToValue(child{parent: parent{Test: "Test"}}).(*Object)
- v.Get("Test")
- }
- }
|