Browse Source

Made nested structs addressable if possible. Closes #129.

Dmitry Panov 5 years ago
parent
commit
ebfbf88ae3
2 changed files with 98 additions and 4 deletions
  1. 5 4
      object_goreflect.go
  2. 93 0
      object_goreflect_test.go

+ 5 - 4
object_goreflect.go

@@ -89,9 +89,6 @@ func (o *objectGoReflect) get(n Value) Value {
 func (o *objectGoReflect) _getField(jsName string) reflect.Value {
 func (o *objectGoReflect) _getField(jsName string) reflect.Value {
 	if info, exists := o.valueTypeInfo.Fields[jsName]; exists {
 	if info, exists := o.valueTypeInfo.Fields[jsName]; exists {
 		v := o.value.FieldByIndex(info.Index)
 		v := o.value.FieldByIndex(info.Index)
-		if info.Anonymous {
-			v = v.Addr()
-		}
 		return v
 		return v
 	}
 	}
 
 
@@ -145,9 +142,13 @@ func (o *objectGoReflect) getPropStr(name string) Value {
 func (o *objectGoReflect) getOwnProp(name string) Value {
 func (o *objectGoReflect) getOwnProp(name string) Value {
 	if o.value.Kind() == reflect.Struct {
 	if o.value.Kind() == reflect.Struct {
 		if v := o._getField(name); v.IsValid() {
 		if v := o._getField(name); v.IsValid() {
+			canSet := v.CanSet()
+			if v.Kind() == reflect.Struct && v.CanAddr() {
+				v = v.Addr()
+			}
 			return &valueProperty{
 			return &valueProperty{
 				value:      o.val.runtime.ToValue(v.Interface()),
 				value:      o.val.runtime.ToValue(v.Interface()),
-				writable:   v.CanSet(),
+				writable:   canSet,
 				enumerable: true,
 				enumerable: true,
 			}
 			}
 		}
 		}

+ 93 - 0
object_goreflect_test.go

@@ -811,3 +811,96 @@ func BenchmarkGoReflectGet(b *testing.B) {
 		v.Get("Test")
 		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 writable");
+	}
+	var thrown = false;
+	try {
+		a1.B.Field = 42;
+	} catch (e) {
+		if (e instanceof TypeError) {
+			thrown = true;
+		}
+	}
+	if (!thrown) {
+		throw new Error("TypeError was not thrown");
+	}
+	`
+	a := A{
+		B: B{
+			Field: 1,
+		},
+	}
+	vm := New()
+	vm.Set("a", &a)
+	vm.Set("a1", a)
+	_, err := vm.RunString(SCRIPT)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	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())
+	}
+
+}