Browse Source

Merge pull request #578 from ganigeorgiev/master

Allow access to the public fields of unexported embedded struct
Dmitry Panov 1 year ago
parent
commit
c24c3bde40
2 changed files with 78 additions and 3 deletions
  1. 6 3
      object_goreflect.go
  2. 72 0
      object_goreflect_test.go

+ 6 - 3
object_goreflect.go

@@ -535,14 +535,17 @@ func (r *Runtime) buildFieldInfo(t reflect.Type, index []int, info *reflectField
 	for i := 0; i < n; i++ {
 	for i := 0; i < n; i++ {
 		field := t.Field(i)
 		field := t.Field(i)
 		name := field.Name
 		name := field.Name
-		if !ast.IsExported(name) {
+		isExported := ast.IsExported(name)
+
+		if !isExported && !field.Anonymous {
 			continue
 			continue
 		}
 		}
+
 		if r.fieldNameMapper != nil {
 		if r.fieldNameMapper != nil {
 			name = r.fieldNameMapper.FieldName(t, field)
 			name = r.fieldNameMapper.FieldName(t, field)
 		}
 		}
 
 
-		if name != "" {
+		if name != "" && isExported {
 			if inf, exists := info.Fields[name]; !exists {
 			if inf, exists := info.Fields[name]; !exists {
 				info.Names = append(info.Names, name)
 				info.Names = append(info.Names, name)
 			} else {
 			} else {
@@ -557,7 +560,7 @@ func (r *Runtime) buildFieldInfo(t reflect.Type, index []int, info *reflectField
 			copy(idx, index)
 			copy(idx, index)
 			idx[len(idx)-1] = i
 			idx[len(idx)-1] = i
 
 
-			if name != "" {
+			if name != "" && isExported {
 				info.Fields[name] = reflectFieldInfo{
 				info.Fields[name] = reflectFieldInfo{
 					Index:     idx,
 					Index:     idx,
 					Anonymous: field.Anonymous,
 					Anonymous: field.Anonymous,

+ 72 - 0
object_goreflect_test.go

@@ -1593,3 +1593,75 @@ func TestGoReflectDefaultToString(t *testing.T) {
 		t.Fatal(err)
 		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)
+			}
+		})
+	}
+}