Skip to content

Commit

Permalink
allow access to the public fields of unexported embedded struct
Browse files Browse the repository at this point in the history
  • Loading branch information
ganigeorgiev committed Jun 10, 2024
1 parent ccbae20 commit 1af89b1
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 3 deletions.
9 changes: 6 additions & 3 deletions object_goreflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -535,14 +535,17 @@ func (r *Runtime) buildFieldInfo(t reflect.Type, index []int, info *reflectField
for i := 0; i < n; i++ {
field := t.Field(i)
name := field.Name
if !ast.IsExported(name) {
isExported := ast.IsExported(name)

if !isExported && !field.Anonymous {
continue
}

if r.fieldNameMapper != nil {
name = r.fieldNameMapper.FieldName(t, field)
}

if name != "" {
if name != "" && isExported {
if inf, exists := info.Fields[name]; !exists {
info.Names = append(info.Names, name)
} else {
Expand All @@ -557,7 +560,7 @@ func (r *Runtime) buildFieldInfo(t reflect.Type, index []int, info *reflectField
copy(idx, index)
idx[len(idx)-1] = i

if name != "" {
if name != "" && isExported {
info.Fields[name] = reflectFieldInfo{
Index: idx,
Anonymous: field.Anonymous,
Expand Down
72 changes: 72 additions & 0 deletions object_goreflect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1593,3 +1593,75 @@ func TestGoReflectDefaultToString(t *testing.T) {
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)
}
})
}
}

0 comments on commit 1af89b1

Please sign in to comment.