From fdb999e670c945aad9b426c46f65f26dd5a81863 Mon Sep 17 00:00:00 2001 From: Dmitry Panov Date: Fri, 5 Aug 2022 09:39:22 +0100 Subject: [PATCH] Always create a map when exporting. Fixes #422. --- builtin_map.go | 4 +--- builtin_map_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ builtin_set.go | 1 + builtin_set_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ object.go | 4 +--- object_goreflect_test.go | 21 +++++++++++++++++++++ 6 files changed, 104 insertions(+), 6 deletions(-) diff --git a/builtin_map.go b/builtin_map.go index 2b84baf8..13e4fb5a 100644 --- a/builtin_map.go +++ b/builtin_map.go @@ -68,9 +68,7 @@ func (mo *mapObject) export(ctx *objectExportCtx) interface{} { } func (mo *mapObject) exportToMap(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { - if dst.IsNil() { - dst.Set(reflect.MakeMap(typ)) - } + dst.Set(reflect.MakeMap(typ)) ctx.putTyped(mo.val, typ, dst.Interface()) keyTyp := typ.Key() elemTyp := typ.Elem() diff --git a/builtin_map_test.go b/builtin_map_test.go index 78c76952..f1d8a3ee 100644 --- a/builtin_map_test.go +++ b/builtin_map_test.go @@ -62,6 +62,46 @@ func TestMapEvilIterator(t *testing.T) { testScriptWithTestLib(SCRIPT, _undefined, t) } +func TestMapExportToNilMap(t *testing.T) { + vm := New() + var m map[int]interface{} + res, err := vm.RunString("new Map([[1, true]])") + if err != nil { + t.Fatal(err) + } + err = vm.ExportTo(res, &m) + if err != nil { + t.Fatal(err) + } + if len(m) != 1 { + t.Fatal(m) + } + if _, exists := m[1]; !exists { + t.Fatal(m) + } +} + +func TestMapExportToNonNilMap(t *testing.T) { + vm := New() + m := map[int]interface{}{ + 2: true, + } + res, err := vm.RunString("new Map([[1, true]])") + if err != nil { + t.Fatal(err) + } + err = vm.ExportTo(res, &m) + if err != nil { + t.Fatal(err) + } + if len(m) != 1 { + t.Fatal(m) + } + if _, exists := m[1]; !exists { + t.Fatal(m) + } +} + func ExampleObject_Export_map() { vm := New() m, err := vm.RunString(` diff --git a/builtin_set.go b/builtin_set.go index e554b75c..11b3bc37 100644 --- a/builtin_set.go +++ b/builtin_set.go @@ -89,6 +89,7 @@ func (so *setObject) exportToArrayOrSlice(dst reflect.Value, typ reflect.Type, c } func (so *setObject) exportToMap(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { + dst.Set(reflect.MakeMap(typ)) keyTyp := typ.Key() elemTyp := typ.Elem() iter := so.m.newIter() diff --git a/builtin_set_test.go b/builtin_set_test.go index 9daa3489..1cb82e16 100644 --- a/builtin_set_test.go +++ b/builtin_set_test.go @@ -100,3 +100,43 @@ func TestSetExportToArrayMismatchedLengths(t *testing.T) { t.Fatalf("unexpected error: %v", err) } } + +func TestSetExportToNilMap(t *testing.T) { + vm := New() + var m map[int]interface{} + res, err := vm.RunString("new Set([1])") + if err != nil { + t.Fatal(err) + } + err = vm.ExportTo(res, &m) + if err != nil { + t.Fatal(err) + } + if len(m) != 1 { + t.Fatal(m) + } + if _, exists := m[1]; !exists { + t.Fatal(m) + } +} + +func TestSetExportToNonNilMap(t *testing.T) { + vm := New() + m := map[int]interface{}{ + 2: true, + } + res, err := vm.RunString("new Set([1])") + if err != nil { + t.Fatal(err) + } + err = vm.ExportTo(res, &m) + if err != nil { + t.Fatal(err) + } + if len(m) != 1 { + t.Fatal(m) + } + if _, exists := m[1]; !exists { + t.Fatal(m) + } +} diff --git a/object.go b/object.go index 3fe5a573..0d493b40 100644 --- a/object.go +++ b/object.go @@ -987,9 +987,7 @@ func (o *baseObject) exportType() reflect.Type { } func genericExportToMap(o *Object, dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { - if dst.IsNil() { - dst.Set(reflect.MakeMap(typ)) - } + dst.Set(reflect.MakeMap(typ)) ctx.putTyped(o, typ, dst.Interface()) keyTyp := typ.Key() elemTyp := typ.Elem() diff --git a/object_goreflect_test.go b/object_goreflect_test.go index 0968704b..832a82f9 100644 --- a/object_goreflect_test.go +++ b/object_goreflect_test.go @@ -1244,3 +1244,24 @@ func TestGoReflectCopyOnWrite(t *testing.T) { 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) + } +}