1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687 |
- package goja
- import (
- "errors"
- "fmt"
- "math"
- "reflect"
- "strings"
- "testing"
- "time"
- )
- 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.(String); 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)
- }
- r.Set("o", o)
- _, err = r.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if res, ok := r.Get("o").Export().(O); ok {
- if res.X != 6 {
- t.Fatalf("Unexpected res.X: %d", res.X)
- }
- if res.Y != "2PP" {
- t.Fatalf("Unexpected res.Y: %s", res.Y)
- }
- }
- }
- 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 (b *testBoolS) Method() bool {
- return bool(*b)
- }
- func TestGoReflectPtrMethodOnNonPtrValue(t *testing.T) {
- var o testGoReflectMethod_O
- o.Get()
- vm := New()
- vm.Set("o", o)
- _, err := vm.RunString(`o.Get()`)
- if err != nil {
- t.Fatal(err)
- }
- _, err = vm.RunString(`o.Method()`)
- if err != nil {
- t.Fatal(err)
- }
- var b testBoolS
- vm.Set("b", b)
- _, err = vm.RunString(`b.Method()`)
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestGoReflectStructField(t *testing.T) {
- type S struct {
- F testGoReflectMethod_O
- B testBoolS
- }
- var s S
- vm := New()
- vm.Set("s", &s)
- const SCRIPT = `
- s.F.Set("Test");
- assert.sameValue(s.F.Method(""), "Test", "1");
- s.B = true;
- assert.sameValue(s.B.Method(), true, "2");
- assert.sameValue(s.B.toString(), "B", "3");
- `
- vm.testScriptWithTestLib(SCRIPT, _undefined, t)
- }
- 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(_ reflect.Type, field reflect.StructField) string {
- if jsonTag := field.Tag.Get("json"); jsonTag != "" {
- return jsonTag
- }
- return field.Name
- }
- func (jsonTagNamer) MethodName(_ 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())
- }
- })
- }
- func TestGoReflectCustomObjNaming(t *testing.T) {
- type testStructWithJsonTags struct {
- A string `json:"b"` // <-- script sees field "A" as property "b"
- }
- r := New()
- r.SetFieldNameMapper(&jsonTagNamer{})
- t.Run("Set object in slice", func(t *testing.T) {
- testSlice := &[]testStructWithJsonTags{{"Hello world"}}
- r.Set("testslice", testSlice)
- _, err := r.RunString(`testslice[0] = {b:"setted"}`)
- if err != nil {
- t.Fatal(err)
- }
- if (*testSlice)[0].A != "setted" {
- t.Fatalf("Expected \"setted\", got %q", (*testSlice)[0])
- }
- })
- t.Run("Set object in map", func(t *testing.T) {
- testMap := map[string]testStructWithJsonTags{"key": {"Hello world"}}
- r.Set("testmap", testMap)
- _, err := r.RunString(`testmap["key"] = {b:"setted"}`)
- if err != nil {
- t.Fatal(err)
- }
- if testMap["key"].A != "setted" {
- t.Fatalf("Expected \"setted\", got %q", testMap["key"])
- }
- })
- t.Run("Add object to map", func(t *testing.T) {
- testMap := map[string]testStructWithJsonTags{}
- r.Set("testmap", testMap)
- _, err := r.RunString(`testmap["newkey"] = {b:"setted"}`)
- if err != nil {
- t.Fatal(err)
- }
- if testMap["newkey"].A != "setted" {
- t.Fatalf("Expected \"setted\", got %q", testMap["newkey"])
- }
- })
- }
- type fieldNameMapper1 struct{}
- func (fieldNameMapper1) FieldName(_ reflect.Type, f reflect.StructField) string {
- return strings.ToLower(f.Name)
- }
- func (fieldNameMapper1) MethodName(_ 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}, test3: nil})
- v, err := vm.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- if !v.StrictEquals(valueTrue) {
- t.Fatalf("Unexepected result: %v", v)
- }
- }
- func TestStructNonAddressable(t *testing.T) {
- type S struct {
- Field int
- }
- const SCRIPT = `
- "use strict";
-
- if (!Object.getOwnPropertyDescriptor(s, "Field").writable) {
- throw new Error("s.Field is non-writable");
- }
- if (!Object.getOwnPropertyDescriptor(s1, "Field").writable) {
- throw new Error("s1.Field is non-writable");
- }
- s1.Field = 42;
- s.Field = 43;
- s;
- `
- var s S
- vm := New()
- vm.Set("s", s)
- vm.Set("s1", &s)
- v, err := vm.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- exp := v.Export()
- if s1, ok := exp.(S); ok {
- if s1.Field != 43 {
- t.Fatal(s1)
- }
- } else {
- t.Fatalf("Wrong type: %T", exp)
- }
- if s.Field != 42 {
- t.Fatalf("Unexpected s.Field value: %d", s.Field)
- }
- }
- type testFieldMapper struct {
- }
- func (testFieldMapper) FieldName(_ reflect.Type, f reflect.StructField) string {
- if tag := f.Tag.Get("js"); tag != "" {
- if tag == "-" {
- return ""
- }
- return tag
- }
- return f.Name
- }
- func (testFieldMapper) MethodName(_ reflect.Type, m reflect.Method) string {
- return m.Name
- }
- func TestHidingAnonField(t *testing.T) {
- type InnerType struct {
- AnotherField string
- }
- type OuterType struct {
- InnerType `js:"-"`
- SomeField string
- }
- const SCRIPT = `
- var a = Object.getOwnPropertyNames(o);
- if (a.length !== 2) {
- throw new Error("unexpected length: " + a.length);
- }
- if (a.indexOf("SomeField") === -1) {
- throw new Error("no SomeField");
- }
- if (a.indexOf("AnotherField") === -1) {
- throw new Error("no SomeField");
- }
- `
- var o OuterType
- vm := New()
- vm.SetFieldNameMapper(testFieldMapper{})
- vm.Set("o", &o)
- _, err := vm.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestFieldOverriding(t *testing.T) {
- type InnerType struct {
- AnotherField string
- AnotherField1 string
- }
- type OuterType struct {
- InnerType `js:"-"`
- SomeField string
- AnotherField string `js:"-"`
- AnotherField1 string
- }
- const SCRIPT = `
- if (o.SomeField !== "SomeField") {
- throw new Error("SomeField");
- }
- if (o.AnotherField !== "AnotherField inner") {
- throw new Error("AnotherField");
- }
- if (o.AnotherField1 !== "AnotherField1 outer") {
- throw new Error("AnotherField1");
- }
- if (o.InnerType) {
- throw new Error("InnerType is present");
- }
- `
- o := OuterType{
- InnerType: InnerType{
- AnotherField: "AnotherField inner",
- AnotherField1: "AnotherField1 inner",
- },
- SomeField: "SomeField",
- AnotherField: "AnotherField outer",
- AnotherField1: "AnotherField1 outer",
- }
- vm := New()
- vm.SetFieldNameMapper(testFieldMapper{})
- vm.Set("o", &o)
- _, err := vm.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestDefinePropertyUnexportedJsName(t *testing.T) {
- type T struct {
- Field int
- unexported int
- }
- vm := New()
- vm.SetFieldNameMapper(fieldNameMapper1{})
- vm.Set("f", &T{unexported: 0})
- _, err := vm.RunString(`
- "use strict";
- Object.defineProperty(f, "field", {value: 42});
- if (f.field !== 42) {
- throw new Error("Unexpected value: " + f.field);
- }
- if (f.hasOwnProperty("unexported")) {
- throw new Error("hasOwnProperty('unexported') is true");
- }
- var thrown;
- try {
- Object.defineProperty(f, "unexported", {value: 1});
- } catch (e) {
- thrown = e;
- }
- if (!(thrown instanceof TypeError)) {
- throw new Error("Unexpected error: ", thrown);
- }
- `)
- if err != nil {
- t.Fatal(err)
- }
- }
- type fieldNameMapperToLower struct{}
- func (fieldNameMapperToLower) FieldName(_ reflect.Type, f reflect.StructField) string {
- return strings.ToLower(f.Name)
- }
- func (fieldNameMapperToLower) MethodName(_ reflect.Type, m reflect.Method) string {
- return strings.ToLower(m.Name)
- }
- func TestHasOwnPropertyUnexportedJsName(t *testing.T) {
- vm := New()
- vm.SetFieldNameMapper(fieldNameMapperToLower{})
- vm.Set("f", &testGoReflectMethod_O{})
- _, err := vm.RunString(`
- "use strict";
- if (!f.hasOwnProperty("test")) {
- throw new Error("hasOwnProperty('test') returned false");
- }
- if (!f.hasOwnProperty("method")) {
- throw new Error("hasOwnProperty('method') returned false");
- }
- `)
- if err != nil {
- t.Fatal(err)
- }
- }
- 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", field: ""}}).(*Object)
- v.Get("Test")
- }
- }
- func TestNestedStructSet(t *testing.T) {
- type B struct {
- Field int
- }
- type A struct {
- B B
- }
- const SCRIPT = `
- 'use strict';
- a.B.Field++;
- if (a1.B.Field != 1) {
- throw new Error("a1.B.Field = " + a1.B.Field);
- }
- var d = Object.getOwnPropertyDescriptor(a1.B, "Field");
- if (!d.writable) {
- throw new Error("a1.B is not writable");
- }
- a1.B.Field = 42;
- a1;
- `
- a := A{
- B: B{
- Field: 1,
- },
- }
- vm := New()
- vm.Set("a", &a)
- vm.Set("a1", a)
- v, err := vm.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- exp := v.Export()
- if v, ok := exp.(A); ok {
- if v.B.Field != 42 {
- t.Fatal(v)
- }
- } else {
- t.Fatalf("Wrong type: %T", exp)
- }
- if v := a.B.Field; v != 2 {
- t.Fatalf("Unexpected a.B.Field: %d", v)
- }
- }
- func TestStructNonAddressableAnonStruct(t *testing.T) {
- type C struct {
- Z int64
- X string
- }
- type B struct {
- C
- Y string
- }
- type A struct {
- B B
- }
- a := A{
- B: B{
- C: C{
- Z: 1,
- X: "X2",
- },
- Y: "Y3",
- },
- }
- const SCRIPT = `
- "use strict";
- var s = JSON.stringify(a);
- s;
- `
- vm := New()
- vm.Set("a", &a)
- v, err := vm.RunString(SCRIPT)
- if err != nil {
- t.Fatal(err)
- }
- expected := `{"B":{"C":{"Z":1,"X":"X2"},"Z":1,"X":"X2","Y":"Y3"}}`
- if expected != v.String() {
- t.Fatalf("Expected '%s', got '%s'", expected, v.String())
- }
- }
- func TestTagFieldNameMapperInvalidId(t *testing.T) {
- vm := New()
- vm.SetFieldNameMapper(TagFieldNameMapper("json", true))
- type S struct {
- Field int `json:"-"`
- }
- vm.Set("s", S{Field: 42})
- res, err := vm.RunString(`s.hasOwnProperty("field") || s.hasOwnProperty("Field")`)
- if err != nil {
- t.Fatal(err)
- }
- if res != valueFalse {
- t.Fatalf("Unexpected result: %v", res)
- }
- }
- func TestPrimitivePtr(t *testing.T) {
- vm := New()
- s := "test"
- vm.Set("s", &s)
- res, err := vm.RunString(`s instanceof String && s == "test"`) // note non-strict equality
- if err != nil {
- t.Fatal(err)
- }
- if v := res.ToBoolean(); !v {
- t.Fatalf("value: %#v", res)
- }
- s = "test1"
- res, err = vm.RunString(`s == "test1"`)
- if err != nil {
- t.Fatal(err)
- }
- if v := res.ToBoolean(); !v {
- t.Fatalf("value: %#v", res)
- }
- }
- func TestStringer(t *testing.T) {
- vm := New()
- vm.Set("e", errors.New("test"))
- res, err := vm.RunString("e.toString()")
- if err != nil {
- t.Fatal(err)
- }
- if v := res.Export(); v != "test" {
- t.Fatalf("v: %v", v)
- }
- }
- func ExampleTagFieldNameMapper() {
- vm := New()
- vm.SetFieldNameMapper(TagFieldNameMapper("json", true))
- type S struct {
- Field int `json:"field"`
- }
- vm.Set("s", S{Field: 42})
- res, _ := vm.RunString(`s.field`)
- fmt.Println(res.Export())
- // Output: 42
- }
- func ExampleUncapFieldNameMapper() {
- vm := New()
- s := testGoReflectMethod_O{
- Test: "passed",
- }
- vm.SetFieldNameMapper(UncapFieldNameMapper())
- vm.Set("s", s)
- res, _ := vm.RunString(`s.test + " and " + s.method("passed too")`)
- fmt.Println(res.Export())
- // Output: passed and passed too
- }
- func TestGoReflectWithProto(t *testing.T) {
- type S struct {
- Field int
- }
- var s S
- vm := New()
- vm.Set("s", &s)
- vm.testScriptWithTestLib(`
- (function() {
- 'use strict';
- var proto = {
- Field: "protoField",
- test: 42
- };
- var test1Holder;
- Object.defineProperty(proto, "test1", {
- set: function(v) {
- test1Holder = v;
- },
- get: function() {
- return test1Holder;
- }
- });
- Object.setPrototypeOf(s, proto);
- assert.sameValue(s.Field, 0, "s.Field");
- s.Field = 2;
- assert.sameValue(s.Field, 2, "s.Field");
- assert.sameValue(s.test, 42, "s.test");
- assert.throws(TypeError, function() {
- Object.defineProperty(s, "test", {value: 43});
- });
- test1Holder = 1;
- assert.sameValue(s.test1, 1, "s.test1");
- s.test1 = 2;
- assert.sameValue(test1Holder, 2, "test1Holder");
- })();
- `, _undefined, t)
- }
- func TestGoReflectSymbols(t *testing.T) {
- type S struct {
- Field int
- }
- var s S
- vm := New()
- vm.Set("s", &s)
- _, err := vm.RunString(`
- 'use strict';
- var sym = Symbol(66);
- s[sym] = "Test";
- if (s[sym] !== "Test") {
- throw new Error("s[sym]=" + s[sym]);
- }
- `)
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestGoReflectSymbolEqualityQuirk(t *testing.T) {
- type Field struct {
- }
- type S struct {
- Field *Field
- }
- var s = S{
- Field: &Field{},
- }
- vm := New()
- vm.Set("s", &s)
- res, err := vm.RunString(`
- var sym = Symbol(66);
- var field1 = s.Field;
- field1[sym] = true;
- var field2 = s.Field;
- // Because a wrapper is created every time the property is accessed
- // field1 and field2 will be different instances of the wrapper.
- // Symbol properties only exist in the wrapper, they cannot be placed into the original Go value,
- // hence the following:
- field1 === field2 && field1[sym] === true && field2[sym] === undefined;
- `)
- if err != nil {
- t.Fatal(err)
- }
- if res != valueTrue {
- t.Fatal(res)
- }
- }
- func TestGoObj__Proto__(t *testing.T) {
- type S struct {
- Field int
- }
- vm := New()
- vm.Set("s", S{})
- vm.Set("m", map[string]interface{}{})
- vm.Set("mr", map[int]string{})
- vm.Set("a", []interface{}{})
- vm.Set("ar", []string{})
- _, err := vm.RunString(`
- function f(s, expectedCtor, prefix) {
- if (s.__proto__ !== expectedCtor.prototype) {
- throw new Error(prefix + ": __proto__: " + s.__proto__);
- }
- s.__proto__ = null;
- if (s.__proto__ !== undefined) { // as there is no longer a prototype, there is no longer the __proto__ property
- throw new Error(prefix + ": __proto__ is not undefined: " + s.__proto__);
- }
- var proto = Object.getPrototypeOf(s);
- if (proto !== null) {
- throw new Error(prefix + ": proto is not null: " + proto);
- }
- }
- f(s, Object, "struct");
- f(m, Object, "simple map");
- f(mr, Object, "reflect map");
- f(a, Array, "slice");
- f(ar, Array, "reflect slice");
- `)
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestGoReflectUnicodeProps(t *testing.T) {
- type S struct {
- Тест string
- }
- vm := New()
- var s S
- vm.Set("s", &s)
- _, err := vm.RunString(`
- if (!s.hasOwnProperty("Тест")) {
- throw new Error("hasOwnProperty");
- }
- `)
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestGoReflectPreserveType(t *testing.T) {
- vm := New()
- var expect = time.Duration(math.MaxInt64)
- vm.Set(`make`, func() time.Duration {
- return expect
- })
- vm.Set(`handle`, func(d time.Duration) {
- if d.String() != expect.String() {
- t.Fatal(`expect`, expect, `, but get`, d)
- }
- })
- _, e := vm.RunString(`
- var d=make()
- handle(d)
- `)
- if e != nil {
- t.Fatal(e)
- }
- }
- func TestGoReflectCopyOnWrite(t *testing.T) {
- type Inner struct {
- Field int
- }
- type S struct {
- I Inner
- }
- var s S
- s.I.Field = 1
- vm := New()
- vm.Set("s", &s)
- _, err := vm.RunString(`
- if (s.I.Field !== 1) {
- throw new Error("s.I.Field: " + s.I.Field);
- }
- let tmp = s.I; // tmp becomes a reference to s.I
- if (tmp.Field !== 1) {
- throw new Error("tmp.Field: " + tmp.Field);
- }
- s.I.Field = 2;
- if (s.I.Field !== 2) {
- throw new Error("s.I.Field (1): " + s.I.Field);
- }
- if (tmp.Field !== 2) {
- throw new Error("tmp.Field (1): " + tmp.Field);
- }
- s.I = {Field: 3}; // at this point tmp is changed to a copy
- if (s.I.Field !== 3) {
- throw new Error("s.I.Field (2): " + s.I.Field);
- }
- if (tmp.Field !== 2) {
- throw new Error("tmp.Field (2): " + tmp.Field);
- }
- `)
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestReflectSetReflectValue(t *testing.T) {
- o := []testGoReflectMethod_O{{}}
- vm := New()
- vm.Set("o", o)
- _, err := vm.RunString(`
- const t = o[0];
- t.Set("a");
- o[0] = {};
- o[0].Set("b");
- if (t.Get() !== "a") {
- throw new Error();
- }
- `)
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestReflectOverwriteReflectMap(t *testing.T) {
- vm := New()
- type S struct {
- M map[int]interface{}
- }
- var s S
- s.M = map[int]interface{}{
- 0: true,
- }
- vm.Set("s", &s)
- _, err := vm.RunString(`
- s.M = {1: false};
- `)
- if err != nil {
- t.Fatal(err)
- }
- if _, exists := s.M[0]; exists {
- t.Fatal(s)
- }
- }
- type testBoolS bool
- func (testBoolS) String() string {
- return "B"
- }
- type testIntS int
- func (testIntS) String() string {
- return "I"
- }
- type testStringS string
- func (testStringS) String() string {
- return "S"
- }
- func TestGoReflectToPrimitive(t *testing.T) {
- vm := New()
- f := func(expr string, expected Value, t *testing.T) {
- v, err := vm.RunString(expr)
- if err != nil {
- t.Fatal(err)
- }
- if IsNaN(expected) {
- if IsNaN(v) {
- return
- }
- } else {
- if v.StrictEquals(expected) {
- return
- }
- }
- t.Fatalf("%s: expected: %v, actual: %v", expr, expected, v)
- }
- t.Run("Not Stringers", func(t *testing.T) {
- type Bool bool
- var b Bool = true
- t.Run("Bool", func(t *testing.T) {
- vm.Set("b", b)
- f("+b", intToValue(1), t)
- f("`${b}`", asciiString("true"), t)
- f("b.toString()", asciiString("true"), t)
- f("b.valueOf()", valueTrue, t)
- })
- t.Run("*Bool", func(t *testing.T) {
- vm.Set("b", &b)
- f("+b", intToValue(1), t)
- f("`${b}`", asciiString("true"), t)
- f("b.toString()", asciiString("true"), t)
- f("b.valueOf()", valueTrue, t)
- })
- type Int int
- var i Int = 1
- t.Run("Int", func(t *testing.T) {
- vm.Set("i", i)
- f("+i", intToValue(1), t)
- f("`${i}`", asciiString("1"), t)
- f("i.toString()", asciiString("1"), t)
- f("i.valueOf()", intToValue(1), t)
- })
- t.Run("*Int", func(t *testing.T) {
- vm.Set("i", &i)
- f("+i", intToValue(1), t)
- f("`${i}`", asciiString("1"), t)
- f("i.toString()", asciiString("1"), t)
- f("i.valueOf()", intToValue(1), t)
- })
- type Uint uint
- var ui Uint = 1
- t.Run("Uint", func(t *testing.T) {
- vm.Set("ui", ui)
- f("+ui", intToValue(1), t)
- f("`${ui}`", asciiString("1"), t)
- f("ui.toString()", asciiString("1"), t)
- f("ui.valueOf()", intToValue(1), t)
- })
- t.Run("*Uint", func(t *testing.T) {
- vm.Set("ui", &i)
- f("+ui", intToValue(1), t)
- f("`${ui}`", asciiString("1"), t)
- f("ui.toString()", asciiString("1"), t)
- f("ui.valueOf()", intToValue(1), t)
- })
- type Float float64
- var fl Float = 1.1
- t.Run("Float", func(t *testing.T) {
- vm.Set("fl", fl)
- f("+fl", floatToValue(1.1), t)
- f("`${fl}`", asciiString("1.1"), t)
- f("fl.toString()", asciiString("1.1"), t)
- f("fl.valueOf()", floatToValue(1.1), t)
- })
- t.Run("*Float", func(t *testing.T) {
- vm.Set("fl", &fl)
- f("+fl", floatToValue(1.1), t)
- f("`${fl}`", asciiString("1.1"), t)
- f("fl.toString()", asciiString("1.1"), t)
- f("fl.valueOf()", floatToValue(1.1), t)
- })
- fl = Float(math.Inf(1))
- t.Run("FloatInf", func(t *testing.T) {
- vm.Set("fl", fl)
- f("+fl", _positiveInf, t)
- f("fl.toString()", asciiString("Infinity"), t)
- })
- type Empty struct{}
- var e Empty
- t.Run("Empty", func(t *testing.T) {
- vm.Set("e", &e)
- f("+e", _NaN, t)
- f("`${e}`", asciiString("[object Object]"), t)
- f("e.toString()", asciiString("[object Object]"), t)
- f("e.valueOf()", vm.ToValue(&e), t)
- })
- })
- t.Run("Stringers", func(t *testing.T) {
- var b testBoolS = true
- t.Run("Bool", func(t *testing.T) {
- vm.Set("b", b)
- f("`${b}`", asciiString("B"), t)
- f("b.toString()", asciiString("B"), t)
- f("b.valueOf()", valueTrue, t)
- f("+b", intToValue(1), t)
- })
- t.Run("*Bool", func(t *testing.T) {
- vm.Set("b", &b)
- f("`${b}`", asciiString("B"), t)
- f("b.toString()", asciiString("B"), t)
- f("b.valueOf()", valueTrue, t)
- f("+b", intToValue(1), t)
- })
- var i testIntS = 1
- t.Run("Int", func(t *testing.T) {
- vm.Set("i", i)
- f("`${i}`", asciiString("I"), t)
- f("i.toString()", asciiString("I"), t)
- f("i.valueOf()", intToValue(1), t)
- f("+i", intToValue(1), t)
- })
- t.Run("*Int", func(t *testing.T) {
- vm.Set("i", &i)
- f("`${i}`", asciiString("I"), t)
- f("i.toString()", asciiString("I"), t)
- f("i.valueOf()", intToValue(1), t)
- f("+i", intToValue(1), t)
- })
- var s testStringS
- t.Run("String", func(t *testing.T) {
- vm.Set("s", s)
- f("`${s}`", asciiString("S"), t)
- f("s.toString()", asciiString("S"), t)
- f("s.valueOf()", asciiString("S"), t)
- f("+s", _NaN, t)
- })
- t.Run("*String", func(t *testing.T) {
- vm.Set("s", &s)
- f("`${s}`", asciiString("S"), t)
- f("s.toString()", asciiString("S"), t)
- f("s.valueOf()", asciiString("S"), t)
- f("+s", _NaN, t)
- })
- })
- }
- type testGoReflectFuncRt struct {
- }
- func (*testGoReflectFuncRt) M(call FunctionCall, r *Runtime) Value {
- if r == nil {
- panic(typeError("Runtime is nil"))
- }
- return call.Argument(0)
- }
- func (*testGoReflectFuncRt) C(call ConstructorCall, r *Runtime) *Object {
- if r == nil {
- panic(typeError("Runtime is nil in constructor"))
- }
- call.This.Set("r", call.Argument(0))
- return nil
- }
- func TestGoReflectFuncWithRuntime(t *testing.T) {
- vm := New()
- var s testGoReflectFuncRt
- vm.Set("s", &s)
- res, err := vm.RunString("s.M(true)")
- if err != nil {
- t.Fatal(err)
- }
- if res != valueTrue {
- t.Fatal(res)
- }
- res, err = vm.RunString("new s.C(true).r")
- if err != nil {
- t.Fatal(err)
- }
- if res != valueTrue {
- t.Fatal(res)
- }
- }
- func TestGoReflectDefaultToString(t *testing.T) {
- var s testStringS
- vm := New()
- v := vm.ToValue(s).(*Object)
- v.Delete("toString")
- v.Delete("valueOf")
- vm.Set("s", v)
- _, err := vm.RunString(`
- class S {
- toString() {
- return "X";
- }
- }
- if (s.toString() !== "S") {
- throw new Error(s.toString());
- }
- if (("" + s) !== "S") {
- throw new Error("" + s);
- }
- Object.setPrototypeOf(s, S.prototype);
- if (s.toString() !== "X") {
- throw new Error(s.toString());
- }
- if (("" + s) !== "X") {
- throw new Error("" + s);
- }
- `)
- if err != nil {
- t.Fatal(err)
- }
- }
- func TestGoReflectUnexportedEmbedStruct(t *testing.T) {
- type privateEmbed struct {
- A string
- }
- type PublicEmbed struct {
- B string
- }
- type privateNested struct {
- C string
- }
- type PublicNested struct {
- D string
- }
- type Foo struct {
- privateEmbed
- PublicEmbed
- privateNested privateNested
- PublicNested PublicNested
- e string
- F string
- }
- vm := New()
- vm.Set("foo", Foo{
- privateEmbed: privateEmbed{A: "testA"},
- PublicEmbed: PublicEmbed{B: "testB"},
- privateNested: privateNested{C: "testC"},
- PublicNested: PublicNested{D: "testD"},
- e: "testE",
- F: "testF",
- })
- scenarios := []struct {
- expr string
- expected string
- }{
- {"foo.privateEmbed", "undefined"},
- {"foo.A", "testA"},
- // ---
- {"foo.PublicEmbed", "[object Object]"},
- {"foo.B", "testB"},
- {"foo.PublicEmbed.B", "testB"},
- // ---
- {"foo.privateNested", "undefined"},
- {"foo.C", "undefined"},
- // ---
- {"foo.PublicNested", "[object Object]"},
- {"foo.D", "undefined"},
- {"foo.PublicNested.D", "testD"},
- // ---
- {"foo.e", "undefined"},
- {"foo.F", "testF"},
- }
- for _, s := range scenarios {
- t.Run(s.expr, func(t *testing.T) {
- v, err := vm.RunString(s.expr)
- if err != nil {
- t.Fatal(err)
- }
- vStr := v.String()
- if vStr != s.expected {
- t.Fatalf("Expected %q, got %q", s.expected, vStr)
- }
- })
- }
- }
- func TestNestedSliceAddr(t *testing.T) {
- type document struct {
- Items []any
- }
- var d document
- runtime := New()
- runtime.Set("d", &d)
- _, err := runtime.RunString(`
- d.Items.push("Hello");
- `)
- if err != nil {
- t.Fatal(err)
- }
- if len(d.Items) != 1 || d.Items[0] != "Hello" {
- t.Fatal(d.Items)
- }
- }
|