Support embedded fields in go-marshal.

PiperOrigin-RevId: 334437990
This commit is contained in:
Rahat Mahmood 2020-09-29 12:32:32 -07:00 committed by gVisor bot
parent 7d64bc1fdf
commit 44c7d55074
5 changed files with 60 additions and 15 deletions

View File

@ -113,3 +113,18 @@ The following are some guidelines for modifying the `go_marshal` tool:
- No runtime reflection in the code generated for the marshallable interface.
The entire point of the tool is to avoid runtime reflection. The generated
tests may use reflection.
## Debugging
To enable debugging output from the go-marshal tool, use one of the following
options, depending on how go-marshal is being invoked:
- Pass `--define gomarshal=verbose` to the bazel command. Note that this can
generate a lot of output depending on what's being compiled, as this will
enable debugging for all packages built by the command.
- Set `marshal_debug = True` on the top-level `go_library` BUILD rule.
- Set `debug = True` on the `go_marshal` BUILD rule.
- Pass `-debug` to the go-marshal tool invocation.

View File

@ -43,8 +43,8 @@ type interfaceGenerator struct {
// of t's interfaces.
ms map[string]struct{}
// as records embedded fields in t that are potentially not packed. The key
// is the accessor for the field.
// as records fields in t that are potentially not packed. The key is the
// accessor for the field.
as map[string]struct{}
}

View File

@ -50,12 +50,6 @@ func (g *interfaceGenerator) areFieldsPackedExpression() (string, bool) {
// checks are done ahead of time and in one place so we can make assumptions
// later.
func (g *interfaceGenerator) validateStruct(ts *ast.TypeSpec, st *ast.StructType) {
forEachStructField(st, func(f *ast.Field) {
if len(f.Names) == 0 {
g.abortAt(f.Pos(), "Cannot marshal structs with embedded fields, give the field a name; use '_' for anonymous fields such as padding fields")
}
})
forEachStructField(st, func(f *ast.Field) {
fieldDispatcher{
primitive: func(_, t *ast.Ident) {
@ -101,7 +95,7 @@ func (g *interfaceGenerator) emitMarshallableForStruct(st *ast.StructType) {
var dynamicSizeTerms []string
forEachStructField(st, fieldDispatcher{
primitive: func(n, t *ast.Ident) {
primitive: func(_, t *ast.Ident) {
if size, dynamic := g.scalarSize(t); !dynamic {
primitiveSize += size
} else {
@ -109,13 +103,13 @@ func (g *interfaceGenerator) emitMarshallableForStruct(st *ast.StructType) {
dynamicSizeTerms = append(dynamicSizeTerms, fmt.Sprintf("(*%s)(nil).SizeBytes()", t.Name))
}
},
selector: func(n, tX, tSel *ast.Ident) {
selector: func(_, tX, tSel *ast.Ident) {
tName := fmt.Sprintf("%s.%s", tX.Name, tSel.Name)
g.recordUsedImport(tX.Name)
g.recordUsedMarshallable(tName)
dynamicSizeTerms = append(dynamicSizeTerms, fmt.Sprintf("(*%s)(nil).SizeBytes()", tName))
},
array: func(n *ast.Ident, a *ast.ArrayType, t *ast.Ident) {
array: func(_ *ast.Ident, a *ast.ArrayType, t *ast.Ident) {
lenExpr := g.arrayLenExpr(a)
if size, dynamic := g.scalarSize(t); !dynamic {
dynamicSizeTerms = append(dynamicSizeTerms, fmt.Sprintf("%d*%s", size, lenExpr))

View File

@ -79,7 +79,7 @@ type fieldDispatcher struct {
}
// Precondition: All dispatch callbacks that will be invoked must be
// provided. Embedded fields are not allowed, len(f.Names) >= 1.
// provided.
func (fd fieldDispatcher) dispatch(f *ast.Field) {
// Each field declaration may actually be multiple declarations of the same
// type. For example, consider:
@ -88,12 +88,24 @@ func (fd fieldDispatcher) dispatch(f *ast.Field) {
// x, y, z int
// }
//
// We invoke the call-backs once per such instance. Embedded fields are not
// allowed, and results in a panic.
// We invoke the call-backs once per such instance.
// Handle embedded fields. Embedded fields have no names, but can be
// referenced by the type name.
if len(f.Names) < 1 {
panic("Precondition not met: attempted to dispatch on embedded field")
switch v := f.Type.(type) {
case *ast.Ident:
fd.primitive(v, v)
case *ast.SelectorExpr:
fd.selector(v.Sel, v.X.(*ast.Ident), v.Sel)
default:
// Note: Arrays can't be embedded, which is handled here.
panic(fmt.Sprintf("Attempted to dispatch on embedded field of unsupported kind: %#v", f.Type))
}
return
}
// Non-embedded field.
for _, name := range f.Names {
switch v := f.Type.(type) {
case *ast.Ident:

View File

@ -174,3 +174,27 @@ type Type9 struct {
x int64
y [sizeA]int32
}
// Type10Embed is a test data type which is be embedded into another type.
//
// +marshal
type Type10Embed struct {
x int64
}
// Type10 is a test data type which contains an embedded struct.
//
// +marshal
type Type10 struct {
Type10Embed
y int64
}
// Type11 is a test data type which contains an embedded struct from an external
// package.
//
// +marshal
type Type11 struct {
ex.External
y int64
}