diff --git a/object_goreflect.go b/object_goreflect.go index 7ad5970e..f8ca6d05 100644 --- a/object_goreflect.go +++ b/object_goreflect.go @@ -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 { @@ -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, diff --git a/object_goreflect_test.go b/object_goreflect_test.go index cf1e8f04..6dc096c8 100644 --- a/object_goreflect_test.go +++ b/object_goreflect_test.go @@ -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) + } + }) + } +}