mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
[lisa] Support dynamic types for all types.
We were only supporting dynamic struct types. With this change, users can make any type dynamic. The tool (correctly) blindly just generates the remaining methods needed to implement Marshallable using the 3 methods defined by the user on the dynamic type. This is helpful in situations like: type StringArray []string Added a test for such a use case. PiperOrigin-RevId: 364463164
This commit is contained in:
@@ -8,6 +8,7 @@ go_library(
|
||||
"generator.go",
|
||||
"generator_interfaces.go",
|
||||
"generator_interfaces_array_newtype.go",
|
||||
"generator_interfaces_dynamic.go",
|
||||
"generator_interfaces_primitive_newtype.go",
|
||||
"generator_interfaces_struct.go",
|
||||
"generator_tests.go",
|
||||
|
||||
@@ -387,36 +387,29 @@ func (g *Generator) collectImports(a *ast.File, f *token.FileSet) map[string]imp
|
||||
|
||||
func (g *Generator) generateOne(t *marshallableType, fset *token.FileSet) *interfaceGenerator {
|
||||
i := newInterfaceGenerator(t.spec, t.recv, fset)
|
||||
if t.dynamic {
|
||||
if t.slice != nil {
|
||||
abortAt(fset.Position(t.slice.comment.Slash), "Slice API is not supported for dynamic types because it assumes that each slice element is statically sized.")
|
||||
}
|
||||
// No validation needed, assume the user knows what they are doing.
|
||||
i.emitMarshallableForDynamicType()
|
||||
return i
|
||||
}
|
||||
switch ty := t.spec.Type.(type) {
|
||||
case *ast.StructType:
|
||||
if t.dynamic {
|
||||
// Don't validate because this type is dynamically sized and probably
|
||||
// contains some funky slices which the validation does not allow.
|
||||
i.emitMarshallableForStruct(ty, t.dynamic)
|
||||
if t.slice != nil {
|
||||
abortAt(fset.Position(t.slice.comment.Slash), "Slice API is not supported for dynamic types because it assumes that each slice element is statically sized.")
|
||||
}
|
||||
break
|
||||
}
|
||||
i.validateStruct(t.spec, ty)
|
||||
i.emitMarshallableForStruct(ty, t.dynamic)
|
||||
i.emitMarshallableForStruct(ty)
|
||||
if t.slice != nil {
|
||||
i.emitMarshallableSliceForStruct(ty, t.slice)
|
||||
}
|
||||
case *ast.Ident:
|
||||
i.validatePrimitiveNewtype(ty)
|
||||
if t.dynamic {
|
||||
abortAt(fset.Position(t.slice.comment.Slash), "Primitive type marked as '+marshal dynamic', but primitive types can not be dynamic.")
|
||||
}
|
||||
i.emitMarshallableForPrimitiveNewtype(ty)
|
||||
if t.slice != nil {
|
||||
i.emitMarshallableSliceForPrimitiveNewtype(ty, t.slice)
|
||||
}
|
||||
case *ast.ArrayType:
|
||||
i.validateArrayNewtype(t.spec.Name, ty)
|
||||
if t.dynamic {
|
||||
abortAt(fset.Position(t.slice.comment.Slash), "Marking array types as `dynamic` is currently not supported.")
|
||||
}
|
||||
// After validate, we can safely call arrayLen.
|
||||
i.emitMarshallableForArrayNewtype(t.spec.Name, ty, ty.Elt.(*ast.Ident))
|
||||
if t.slice != nil {
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// Copyright 2021 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gomarshal
|
||||
|
||||
func (g *interfaceGenerator) emitMarshallableForDynamicType() {
|
||||
// The user writes their own MarshalBytes, UnmarshalBytes and SizeBytes for
|
||||
// dynamic types. Generate the rest using these definitions.
|
||||
|
||||
g.emit("// Packed implements marshal.Marshallable.Packed.\n")
|
||||
g.emit("//go:nosplit\n")
|
||||
g.emit("func (%s *%s) Packed() bool {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("// Type %s is dynamic so it might have slice/string headers. Hence, it is not packed.\n", g.typeName())
|
||||
g.emit("return false\n")
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// MarshalUnsafe implements marshal.Marshallable.MarshalUnsafe.\n")
|
||||
g.emit("func (%s *%s) MarshalUnsafe(dst []byte) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("// Type %s doesn't have a packed layout in memory, fallback to MarshalBytes.\n", g.typeName())
|
||||
g.emit("%s.MarshalBytes(dst)\n", g.r)
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// UnmarshalUnsafe implements marshal.Marshallable.UnmarshalUnsafe.\n")
|
||||
g.emit("func (%s *%s) UnmarshalUnsafe(src []byte) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("// Type %s doesn't have a packed layout in memory, fallback to UnmarshalBytes.\n", g.typeName())
|
||||
g.emit("%s.UnmarshalBytes(src)\n", g.r)
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// CopyOutN implements marshal.Marshallable.CopyOutN.\n")
|
||||
g.emit("//go:nosplit\n")
|
||||
g.recordUsedImport("marshal")
|
||||
g.recordUsedImport("usermem")
|
||||
g.emit("func (%s *%s) CopyOutN(cc marshal.CopyContext, addr usermem.Addr, limit int) (int, error) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("// Type %s doesn't have a packed layout in memory, fall back to MarshalBytes.\n", g.typeName())
|
||||
g.emit("buf := cc.CopyScratchBuffer(%s.SizeBytes()) // escapes: okay.\n", g.r)
|
||||
g.emit("%s.MarshalBytes(buf) // escapes: fallback.\n", g.r)
|
||||
g.emit("return cc.CopyOutBytes(addr, buf[:limit]) // escapes: okay.\n")
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// CopyOut implements marshal.Marshallable.CopyOut.\n")
|
||||
g.emit("//go:nosplit\n")
|
||||
g.recordUsedImport("marshal")
|
||||
g.recordUsedImport("usermem")
|
||||
g.emit("func (%s *%s) CopyOut(cc marshal.CopyContext, addr usermem.Addr) (int, error) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("return %s.CopyOutN(cc, addr, %s.SizeBytes())\n", g.r, g.r)
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// CopyIn implements marshal.Marshallable.CopyIn.\n")
|
||||
g.emit("//go:nosplit\n")
|
||||
g.recordUsedImport("marshal")
|
||||
g.recordUsedImport("usermem")
|
||||
g.emit("func (%s *%s) CopyIn(cc marshal.CopyContext, addr usermem.Addr) (int, error) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("// Type %s doesn't have a packed layout in memory, fall back to UnmarshalBytes.\n", g.typeName())
|
||||
g.emit("buf := cc.CopyScratchBuffer(%s.SizeBytes()) // escapes: okay.\n", g.r)
|
||||
g.emit("length, err := cc.CopyInBytes(addr, buf) // escapes: okay.\n")
|
||||
g.emit("// Unmarshal unconditionally. If we had a short copy-in, this results in a\n")
|
||||
g.emit("// partially unmarshalled struct.\n")
|
||||
g.emit("%s.UnmarshalBytes(buf) // escapes: fallback.\n", g.r)
|
||||
g.emit("return length, err\n")
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// WriteTo implements io.WriterTo.WriteTo.\n")
|
||||
g.recordUsedImport("io")
|
||||
g.emit("func (%s *%s) WriteTo(writer io.Writer) (int64, error) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
g.emit("// Type %s doesn't have a packed layout in memory, fall back to MarshalBytes.\n", g.typeName())
|
||||
g.emit("buf := make([]byte, %s.SizeBytes())\n", g.r)
|
||||
g.emit("%s.MarshalBytes(buf)\n", g.r)
|
||||
g.emit("length, err := writer.Write(buf)\n")
|
||||
g.emit("return int64(length), err\n")
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
}
|
||||
@@ -69,11 +69,7 @@ func (g *interfaceGenerator) validateStruct(ts *ast.TypeSpec, st *ast.StructType
|
||||
})
|
||||
}
|
||||
|
||||
func (g *interfaceGenerator) isStructPacked(st *ast.StructType, isDynamic bool) bool {
|
||||
if isDynamic {
|
||||
// Dynamic types are not packed because a slice header might be present.
|
||||
return false
|
||||
}
|
||||
func (g *interfaceGenerator) isStructPacked(st *ast.StructType) bool {
|
||||
packed := true
|
||||
forEachStructField(st, func(f *ast.Field) {
|
||||
if f.Tag != nil {
|
||||
@@ -89,17 +85,165 @@ func (g *interfaceGenerator) isStructPacked(st *ast.StructType, isDynamic bool)
|
||||
return packed
|
||||
}
|
||||
|
||||
func (g *interfaceGenerator) emitMarshallableForStruct(st *ast.StructType, isDynamic bool) {
|
||||
thisPacked := g.isStructPacked(st, isDynamic)
|
||||
func (g *interfaceGenerator) emitMarshallableForStruct(st *ast.StructType) {
|
||||
thisPacked := g.isStructPacked(st)
|
||||
|
||||
// Dynamic types are supposed to manually implement SizeBytes, MarshalBytes
|
||||
// and UnmarshalBytes. The rest of the methos are autogenerated and depend on
|
||||
// the implementation of these three.
|
||||
if !isDynamic {
|
||||
g.emitSizeBytesForStruct(st)
|
||||
g.emitMarshalBytesForStruct(st)
|
||||
g.emitUnmarshalBytesForStruct(st)
|
||||
}
|
||||
g.emit("// SizeBytes implements marshal.Marshallable.SizeBytes.\n")
|
||||
g.emit("func (%s *%s) SizeBytes() int {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
primitiveSize := 0
|
||||
var dynamicSizeTerms []string
|
||||
|
||||
forEachStructField(st, fieldDispatcher{
|
||||
primitive: func(_, t *ast.Ident) {
|
||||
if size, dynamic := g.scalarSize(t); !dynamic {
|
||||
primitiveSize += size
|
||||
} else {
|
||||
g.recordUsedMarshallable(t.Name)
|
||||
dynamicSizeTerms = append(dynamicSizeTerms, fmt.Sprintf("(*%s)(nil).SizeBytes()", t.Name))
|
||||
}
|
||||
},
|
||||
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(_ *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))
|
||||
} else {
|
||||
g.recordUsedMarshallable(t.Name)
|
||||
dynamicSizeTerms = append(dynamicSizeTerms, fmt.Sprintf("(*%s)(nil).SizeBytes()*%s", t.Name, lenExpr))
|
||||
}
|
||||
},
|
||||
}.dispatch)
|
||||
g.emit("return %d", primitiveSize)
|
||||
if len(dynamicSizeTerms) > 0 {
|
||||
g.incIndent()
|
||||
}
|
||||
{
|
||||
for _, d := range dynamicSizeTerms {
|
||||
g.emitNoIndent(" +\n")
|
||||
g.emit(d)
|
||||
}
|
||||
}
|
||||
if len(dynamicSizeTerms) > 0 {
|
||||
g.decIndent()
|
||||
}
|
||||
})
|
||||
g.emit("\n}\n\n")
|
||||
|
||||
g.emit("// MarshalBytes implements marshal.Marshallable.MarshalBytes.\n")
|
||||
g.emit("func (%s *%s) MarshalBytes(dst []byte) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
forEachStructField(st, fieldDispatcher{
|
||||
primitive: func(n, t *ast.Ident) {
|
||||
if n.Name == "_" {
|
||||
g.emit("// Padding: dst[:sizeof(%s)] ~= %s(0)\n", t.Name, t.Name)
|
||||
if len, dynamic := g.scalarSize(t); !dynamic {
|
||||
g.shift("dst", len)
|
||||
} else {
|
||||
// We can't use shiftDynamic here because we don't have
|
||||
// an instance of the dynamic type we can reference here
|
||||
// (since the version in this struct is anonymous). Use
|
||||
// a typed nil pointer to call SizeBytes() instead.
|
||||
g.emit("dst = dst[(*%s)(nil).SizeBytes():]\n", t.Name)
|
||||
}
|
||||
return
|
||||
}
|
||||
g.marshalScalar(g.fieldAccessor(n), t.Name, "dst")
|
||||
},
|
||||
selector: func(n, tX, tSel *ast.Ident) {
|
||||
if n.Name == "_" {
|
||||
g.emit("// Padding: dst[:sizeof(%s)] ~= %s(0)\n", tX.Name, tSel.Name)
|
||||
g.emit("dst = dst[(*%s.%s)(nil).SizeBytes():]\n", tX.Name, tSel.Name)
|
||||
return
|
||||
}
|
||||
g.marshalScalar(g.fieldAccessor(n), fmt.Sprintf("%s.%s", tX.Name, tSel.Name), "dst")
|
||||
},
|
||||
array: func(n *ast.Ident, a *ast.ArrayType, t *ast.Ident) {
|
||||
lenExpr := g.arrayLenExpr(a)
|
||||
if n.Name == "_" {
|
||||
g.emit("// Padding: dst[:sizeof(%s)*%s] ~= [%s]%s{0}\n", t.Name, lenExpr, lenExpr, t.Name)
|
||||
if size, dynamic := g.scalarSize(t); !dynamic {
|
||||
g.emit("dst = dst[%d*(%s):]\n", size, lenExpr)
|
||||
} else {
|
||||
// We can't use shiftDynamic here because we don't have
|
||||
// an instance of the dynamic type we can reference here
|
||||
// (since the version in this struct is anonymous). Use
|
||||
// a typed nil pointer to call SizeBytes() instead.
|
||||
g.emit("dst = dst[(*%s)(nil).SizeBytes()*(%s):]\n", t.Name, lenExpr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
g.emit("for idx := 0; idx < %s; idx++ {\n", lenExpr)
|
||||
g.inIndent(func() {
|
||||
g.marshalScalar(fmt.Sprintf("%s[idx]", g.fieldAccessor(n)), t.Name, "dst")
|
||||
})
|
||||
g.emit("}\n")
|
||||
},
|
||||
}.dispatch)
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// UnmarshalBytes implements marshal.Marshallable.UnmarshalBytes.\n")
|
||||
g.emit("func (%s *%s) UnmarshalBytes(src []byte) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
forEachStructField(st, fieldDispatcher{
|
||||
primitive: func(n, t *ast.Ident) {
|
||||
if n.Name == "_" {
|
||||
g.emit("// Padding: var _ %s ~= src[:sizeof(%s)]\n", t.Name, t.Name)
|
||||
if len, dynamic := g.scalarSize(t); !dynamic {
|
||||
g.shift("src", len)
|
||||
} else {
|
||||
// We don't have an instance of the dynamic type we can
|
||||
// reference here (since the version in this struct is
|
||||
// anonymous). Use a typed nil pointer to call
|
||||
// SizeBytes() instead.
|
||||
g.shiftDynamic("src", fmt.Sprintf("(*%s)(nil)", t.Name))
|
||||
g.recordPotentiallyNonPackedField(fmt.Sprintf("(*%s)(nil)", t.Name))
|
||||
}
|
||||
return
|
||||
}
|
||||
g.unmarshalScalar(g.fieldAccessor(n), t.Name, "src")
|
||||
},
|
||||
selector: func(n, tX, tSel *ast.Ident) {
|
||||
if n.Name == "_" {
|
||||
g.emit("// Padding: %s ~= src[:sizeof(%s.%s)]\n", g.fieldAccessor(n), tX.Name, tSel.Name)
|
||||
g.emit("src = src[(*%s.%s)(nil).SizeBytes():]\n", tX.Name, tSel.Name)
|
||||
g.recordPotentiallyNonPackedField(fmt.Sprintf("(*%s.%s)(nil)", tX.Name, tSel.Name))
|
||||
return
|
||||
}
|
||||
g.unmarshalScalar(g.fieldAccessor(n), fmt.Sprintf("%s.%s", tX.Name, tSel.Name), "src")
|
||||
},
|
||||
array: func(n *ast.Ident, a *ast.ArrayType, t *ast.Ident) {
|
||||
lenExpr := g.arrayLenExpr(a)
|
||||
if n.Name == "_" {
|
||||
g.emit("// Padding: ~ copy([%s]%s(%s), src[:sizeof(%s)*%s])\n", lenExpr, t.Name, g.fieldAccessor(n), t.Name, lenExpr)
|
||||
if size, dynamic := g.scalarSize(t); !dynamic {
|
||||
g.emit("src = src[%d*(%s):]\n", size, lenExpr)
|
||||
} else {
|
||||
// We can't use shiftDynamic here because we don't have
|
||||
// an instance of the dynamic type we can referece here
|
||||
// (since the version in this struct is anonymous). Use
|
||||
// a typed nil pointer to call SizeBytes() instead.
|
||||
g.emit("src = src[(*%s)(nil).SizeBytes()*(%s):]\n", t.Name, lenExpr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
g.emit("for idx := 0; idx < %s; idx++ {\n", lenExpr)
|
||||
g.inIndent(func() {
|
||||
g.unmarshalScalar(fmt.Sprintf("%s[idx]", g.fieldAccessor(n)), t.Name, "src")
|
||||
})
|
||||
g.emit("}\n")
|
||||
},
|
||||
}.dispatch)
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
|
||||
g.emit("// Packed implements marshal.Marshallable.Packed.\n")
|
||||
g.emit("//go:nosplit\n")
|
||||
@@ -284,171 +428,8 @@ func (g *interfaceGenerator) emitMarshallableForStruct(st *ast.StructType, isDyn
|
||||
g.emit("}\n\n")
|
||||
}
|
||||
|
||||
func (g *interfaceGenerator) emitSizeBytesForStruct(st *ast.StructType) {
|
||||
g.emit("// SizeBytes implements marshal.Marshallable.SizeBytes.\n")
|
||||
g.emit("func (%s *%s) SizeBytes() int {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
primitiveSize := 0
|
||||
var dynamicSizeTerms []string
|
||||
|
||||
forEachStructField(st, fieldDispatcher{
|
||||
primitive: func(_, t *ast.Ident) {
|
||||
if size, dynamic := g.scalarSize(t); !dynamic {
|
||||
primitiveSize += size
|
||||
} else {
|
||||
g.recordUsedMarshallable(t.Name)
|
||||
dynamicSizeTerms = append(dynamicSizeTerms, fmt.Sprintf("(*%s)(nil).SizeBytes()", t.Name))
|
||||
}
|
||||
},
|
||||
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(_ *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))
|
||||
} else {
|
||||
g.recordUsedMarshallable(t.Name)
|
||||
dynamicSizeTerms = append(dynamicSizeTerms, fmt.Sprintf("(*%s)(nil).SizeBytes()*%s", t.Name, lenExpr))
|
||||
}
|
||||
},
|
||||
}.dispatch)
|
||||
g.emit("return %d", primitiveSize)
|
||||
if len(dynamicSizeTerms) > 0 {
|
||||
g.incIndent()
|
||||
}
|
||||
{
|
||||
for _, d := range dynamicSizeTerms {
|
||||
g.emitNoIndent(" +\n")
|
||||
g.emit(d)
|
||||
}
|
||||
}
|
||||
if len(dynamicSizeTerms) > 0 {
|
||||
g.decIndent()
|
||||
}
|
||||
})
|
||||
g.emit("\n}\n\n")
|
||||
}
|
||||
|
||||
func (g *interfaceGenerator) emitMarshalBytesForStruct(st *ast.StructType) {
|
||||
g.emit("// MarshalBytes implements marshal.Marshallable.MarshalBytes.\n")
|
||||
g.emit("func (%s *%s) MarshalBytes(dst []byte) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
forEachStructField(st, fieldDispatcher{
|
||||
primitive: func(n, t *ast.Ident) {
|
||||
if n.Name == "_" {
|
||||
g.emit("// Padding: dst[:sizeof(%s)] ~= %s(0)\n", t.Name, t.Name)
|
||||
if len, dynamic := g.scalarSize(t); !dynamic {
|
||||
g.shift("dst", len)
|
||||
} else {
|
||||
// We can't use shiftDynamic here because we don't have
|
||||
// an instance of the dynamic type we can reference here
|
||||
// (since the version in this struct is anonymous). Use
|
||||
// a typed nil pointer to call SizeBytes() instead.
|
||||
g.emit("dst = dst[(*%s)(nil).SizeBytes():]\n", t.Name)
|
||||
}
|
||||
return
|
||||
}
|
||||
g.marshalScalar(g.fieldAccessor(n), t.Name, "dst")
|
||||
},
|
||||
selector: func(n, tX, tSel *ast.Ident) {
|
||||
if n.Name == "_" {
|
||||
g.emit("// Padding: dst[:sizeof(%s)] ~= %s(0)\n", tX.Name, tSel.Name)
|
||||
g.emit("dst = dst[(*%s.%s)(nil).SizeBytes():]\n", tX.Name, tSel.Name)
|
||||
return
|
||||
}
|
||||
g.marshalScalar(g.fieldAccessor(n), fmt.Sprintf("%s.%s", tX.Name, tSel.Name), "dst")
|
||||
},
|
||||
array: func(n *ast.Ident, a *ast.ArrayType, t *ast.Ident) {
|
||||
lenExpr := g.arrayLenExpr(a)
|
||||
if n.Name == "_" {
|
||||
g.emit("// Padding: dst[:sizeof(%s)*%s] ~= [%s]%s{0}\n", t.Name, lenExpr, lenExpr, t.Name)
|
||||
if size, dynamic := g.scalarSize(t); !dynamic {
|
||||
g.emit("dst = dst[%d*(%s):]\n", size, lenExpr)
|
||||
} else {
|
||||
// We can't use shiftDynamic here because we don't have
|
||||
// an instance of the dynamic type we can reference here
|
||||
// (since the version in this struct is anonymous). Use
|
||||
// a typed nil pointer to call SizeBytes() instead.
|
||||
g.emit("dst = dst[(*%s)(nil).SizeBytes()*(%s):]\n", t.Name, lenExpr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
g.emit("for idx := 0; idx < %s; idx++ {\n", lenExpr)
|
||||
g.inIndent(func() {
|
||||
g.marshalScalar(fmt.Sprintf("%s[idx]", g.fieldAccessor(n)), t.Name, "dst")
|
||||
})
|
||||
g.emit("}\n")
|
||||
},
|
||||
}.dispatch)
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
}
|
||||
|
||||
func (g *interfaceGenerator) emitUnmarshalBytesForStruct(st *ast.StructType) {
|
||||
g.emit("// UnmarshalBytes implements marshal.Marshallable.UnmarshalBytes.\n")
|
||||
g.emit("func (%s *%s) UnmarshalBytes(src []byte) {\n", g.r, g.typeName())
|
||||
g.inIndent(func() {
|
||||
forEachStructField(st, fieldDispatcher{
|
||||
primitive: func(n, t *ast.Ident) {
|
||||
if n.Name == "_" {
|
||||
g.emit("// Padding: var _ %s ~= src[:sizeof(%s)]\n", t.Name, t.Name)
|
||||
if len, dynamic := g.scalarSize(t); !dynamic {
|
||||
g.shift("src", len)
|
||||
} else {
|
||||
// We don't have an instance of the dynamic type we can
|
||||
// reference here (since the version in this struct is
|
||||
// anonymous). Use a typed nil pointer to call
|
||||
// SizeBytes() instead.
|
||||
g.shiftDynamic("src", fmt.Sprintf("(*%s)(nil)", t.Name))
|
||||
g.recordPotentiallyNonPackedField(fmt.Sprintf("(*%s)(nil)", t.Name))
|
||||
}
|
||||
return
|
||||
}
|
||||
g.unmarshalScalar(g.fieldAccessor(n), t.Name, "src")
|
||||
},
|
||||
selector: func(n, tX, tSel *ast.Ident) {
|
||||
if n.Name == "_" {
|
||||
g.emit("// Padding: %s ~= src[:sizeof(%s.%s)]\n", g.fieldAccessor(n), tX.Name, tSel.Name)
|
||||
g.emit("src = src[(*%s.%s)(nil).SizeBytes():]\n", tX.Name, tSel.Name)
|
||||
g.recordPotentiallyNonPackedField(fmt.Sprintf("(*%s.%s)(nil)", tX.Name, tSel.Name))
|
||||
return
|
||||
}
|
||||
g.unmarshalScalar(g.fieldAccessor(n), fmt.Sprintf("%s.%s", tX.Name, tSel.Name), "src")
|
||||
},
|
||||
array: func(n *ast.Ident, a *ast.ArrayType, t *ast.Ident) {
|
||||
lenExpr := g.arrayLenExpr(a)
|
||||
if n.Name == "_" {
|
||||
g.emit("// Padding: ~ copy([%s]%s(%s), src[:sizeof(%s)*%s])\n", lenExpr, t.Name, g.fieldAccessor(n), t.Name, lenExpr)
|
||||
if size, dynamic := g.scalarSize(t); !dynamic {
|
||||
g.emit("src = src[%d*(%s):]\n", size, lenExpr)
|
||||
} else {
|
||||
// We can't use shiftDynamic here because we don't have
|
||||
// an instance of the dynamic type we can referece here
|
||||
// (since the version in this struct is anonymous). Use
|
||||
// a typed nil pointer to call SizeBytes() instead.
|
||||
g.emit("src = src[(*%s)(nil).SizeBytes()*(%s):]\n", t.Name, lenExpr)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
g.emit("for idx := 0; idx < %s; idx++ {\n", lenExpr)
|
||||
g.inIndent(func() {
|
||||
g.unmarshalScalar(fmt.Sprintf("%s[idx]", g.fieldAccessor(n)), t.Name, "src")
|
||||
})
|
||||
g.emit("}\n")
|
||||
},
|
||||
}.dispatch)
|
||||
})
|
||||
g.emit("}\n\n")
|
||||
}
|
||||
|
||||
func (g *interfaceGenerator) emitMarshallableSliceForStruct(st *ast.StructType, slice *sliceAPI) {
|
||||
thisPacked := g.isStructPacked(st, false /* isDynamic */)
|
||||
thisPacked := g.isStructPacked(st)
|
||||
|
||||
if slice.inner {
|
||||
abortAt(g.f.Position(slice.comment.Slash), fmt.Sprintf("The ':inner' argument to '+marshal slice:%s:inner' is only applicable to newtypes on primitives. Remove it from this struct declaration.", slice.ident))
|
||||
|
||||
@@ -23,7 +23,10 @@ go_test(
|
||||
go_library(
|
||||
name = "test",
|
||||
testonly = 1,
|
||||
srcs = ["test.go"],
|
||||
srcs = [
|
||||
"dynamic.go",
|
||||
"test.go",
|
||||
],
|
||||
marshal = True,
|
||||
visibility = ["//tools/go_marshal/test:__subpackages__"],
|
||||
deps = [
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2021 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package test
|
||||
|
||||
import "gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
|
||||
// Type12Dynamic is a dynamically sized struct which depends on the
|
||||
// autogenerator to generate some Marshallable methods for it.
|
||||
//
|
||||
// +marshal dynamic
|
||||
type Type12Dynamic struct {
|
||||
X primitive.Int64
|
||||
Y []primitive.Int64
|
||||
}
|
||||
|
||||
// SizeBytes implements marshal.Marshallable.SizeBytes.
|
||||
func (t *Type12Dynamic) SizeBytes() int {
|
||||
return (len(t.Y) * 8) + t.X.SizeBytes()
|
||||
}
|
||||
|
||||
// MarshalBytes implements marshal.Marshallable.MarshalBytes.
|
||||
func (t *Type12Dynamic) MarshalBytes(dst []byte) {
|
||||
t.X.MarshalBytes(dst)
|
||||
dst = dst[t.X.SizeBytes():]
|
||||
for i, x := range t.Y {
|
||||
x.MarshalBytes(dst[i*8 : (i+1)*8])
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalBytes implements marshal.Marshallable.UnmarshalBytes.
|
||||
func (t *Type12Dynamic) UnmarshalBytes(src []byte) {
|
||||
t.X.UnmarshalBytes(src)
|
||||
if t.Y != nil {
|
||||
t.Y = t.Y[:0]
|
||||
}
|
||||
for i := t.X.SizeBytes(); i < len(src); i += 8 {
|
||||
var x primitive.Int64
|
||||
x.UnmarshalBytes(src[i:])
|
||||
t.Y = append(t.Y, x)
|
||||
}
|
||||
}
|
||||
|
||||
// Type13Dynamic is a dynamically sized struct which depends on the
|
||||
// autogenerator to generate some Marshallable methods for it.
|
||||
//
|
||||
// It represents a string in memory which is preceded by a uint32 indicating
|
||||
// the string size.
|
||||
//
|
||||
// +marshal dynamic
|
||||
type Type13Dynamic string
|
||||
|
||||
// SizeBytes implements marshal.Marshallable.SizeBytes.
|
||||
func (t *Type13Dynamic) SizeBytes() int {
|
||||
return (*primitive.Uint32)(nil).SizeBytes() + len(*t)
|
||||
}
|
||||
|
||||
// MarshalBytes implements marshal.Marshallable.MarshalBytes.
|
||||
func (t *Type13Dynamic) MarshalBytes(dst []byte) {
|
||||
strLen := primitive.Uint32(len(*t))
|
||||
strLen.MarshalBytes(dst)
|
||||
dst = dst[strLen.SizeBytes():]
|
||||
copy(dst[:strLen], *t)
|
||||
}
|
||||
|
||||
// UnmarshalBytes implements marshal.Marshallable.UnmarshalBytes.
|
||||
func (t *Type13Dynamic) UnmarshalBytes(src []byte) {
|
||||
var strLen primitive.Uint32
|
||||
strLen.UnmarshalBytes(src)
|
||||
src = src[strLen.SizeBytes():]
|
||||
*t = Type13Dynamic(src[:strLen])
|
||||
}
|
||||
@@ -515,20 +515,39 @@ func TestLimitedSliceMarshalling(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDynamicType(t *testing.T) {
|
||||
func TestDynamicTypeStruct(t *testing.T) {
|
||||
t12 := test.Type12Dynamic{
|
||||
X: 32,
|
||||
Y: []primitive.Int64{5, 6, 7},
|
||||
}
|
||||
var cc mockCopyContext
|
||||
cc.setLimit(t12.SizeBytes())
|
||||
|
||||
var m marshal.Marshallable
|
||||
m = &t12 // Ensure that all methods were generated.
|
||||
b := make([]byte, m.SizeBytes())
|
||||
m.MarshalBytes(b)
|
||||
if _, err := t12.CopyOut(&cc, usermem.Addr(0)); err != nil {
|
||||
t.Fatalf("cc.CopyOut faile: %v", err)
|
||||
}
|
||||
|
||||
var res test.Type12Dynamic
|
||||
res.UnmarshalBytes(b)
|
||||
res := test.Type12Dynamic{
|
||||
Y: make([]primitive.Int64, len(t12.Y)),
|
||||
}
|
||||
res.CopyIn(&cc, usermem.Addr(0))
|
||||
if !reflect.DeepEqual(t12, res) {
|
||||
t.Errorf("dynamic type is not same after marshalling and unmarshalling: before = %+v, after = %+v", t12, res)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDynamicTypeIdentifier(t *testing.T) {
|
||||
s := test.Type13Dynamic("go_marshal")
|
||||
var cc mockCopyContext
|
||||
cc.setLimit(s.SizeBytes())
|
||||
|
||||
if _, err := s.CopyOut(&cc, usermem.Addr(0)); err != nil {
|
||||
t.Fatalf("cc.CopyOut faile: %v", err)
|
||||
}
|
||||
|
||||
res := test.Type13Dynamic(make([]byte, len(s)))
|
||||
res.CopyIn(&cc, usermem.Addr(0))
|
||||
if res != s {
|
||||
t.Errorf("dynamic type is not same after marshalling and unmarshalling: before = %s, after = %s", s, res)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/marshal/primitive"
|
||||
|
||||
// We're intentionally using a package name alias here even though it's not
|
||||
// necessary to test the code generator's ability to handle package aliases.
|
||||
ex "gvisor.dev/gvisor/tools/go_marshal/test/external"
|
||||
@@ -200,36 +198,3 @@ type Type11 struct {
|
||||
ex.External
|
||||
y int64
|
||||
}
|
||||
|
||||
// Type12Dynamic is a dynamically sized struct which depends on the autogenerator
|
||||
// to generate some Marshallable methods for it.
|
||||
//
|
||||
// +marshal dynamic
|
||||
type Type12Dynamic struct {
|
||||
X primitive.Int64
|
||||
Y []primitive.Int64
|
||||
}
|
||||
|
||||
// SizeBytes implements marshal.Marshallable.SizeBytes.
|
||||
func (t *Type12Dynamic) SizeBytes() int {
|
||||
return (len(t.Y) * 8) + t.X.SizeBytes()
|
||||
}
|
||||
|
||||
// MarshalBytes implements marshal.Marshallable.MarshalBytes.
|
||||
func (t *Type12Dynamic) MarshalBytes(dst []byte) {
|
||||
t.X.MarshalBytes(dst)
|
||||
dst = dst[t.X.SizeBytes():]
|
||||
for i, x := range t.Y {
|
||||
x.MarshalBytes(dst[i*8 : (i+1)*8])
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalBytes implements marshal.Marshallable.UnmarshalBytes.
|
||||
func (t *Type12Dynamic) UnmarshalBytes(src []byte) {
|
||||
t.X.UnmarshalBytes(src)
|
||||
for i := t.X.SizeBytes(); i < len(src); i += 8 {
|
||||
var x primitive.Int64
|
||||
x.UnmarshalBytes(src[i:])
|
||||
t.Y = append(t.Y, x)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user