blob: dc2bb0fa0c1249bfc5bb12e3b2e057876c1e8465 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package fidlgen_cpp
import (
"encoding/binary"
"fmt"
"strings"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
)
type Struct struct {
Attributes
fidlgen.Resourceness
nameVariants
AnonymousChildren []ScopedLayout
CodingTableType name
Members []StructMember
PaddingV1 []StructPadding
PaddingV2 []StructPadding
BackingBufferTypeV1 string
BackingBufferTypeV2 string
IsInResult bool
ParametersTupleDecl name
// Full decls needed to check if a type is memcpy compatible.
// Only set if it may be possible for a type to be memcpy compatible,
// e.g. has no padding.
// See the struct template for usage.
FullDeclMemcpyCompatibleDeps []nameVariants
TypeShapeV1 TypeShape
TypeShapeV2 TypeShape
isEmptyStruct bool
isAnonymousRequestOrResponse bool
}
func (*Struct) Kind() declKind {
return Kinds.Struct
}
// AsParameters flattens the struct's members into a parameter list.
func (s *Struct) AsParameters(_ *Type, _ *HandleInformation) []Parameter {
var out []Parameter
for _, sm := range s.Members {
out = append(out, sm.AsParameter())
}
return out
}
// SetInResult marks the struct as being used in as the success variant in a
// method Result, and takes note of the tuple declaration of the result's
// parameters. Because a named struct may be in multiple results, every
// call to this function after the first one per instance is a no-op, since the
// struct would have already been marked with the same information.
func (s *Struct) SetInResult(result *Result) {
if !s.IsInResult {
s.IsInResult = true
s.ParametersTupleDecl = result.ValueTupleDecl
}
}
func (s *Struct) IsEmpty() bool {
return s.isEmptyStruct
}
var _ Kinded = (*Struct)(nil)
var _ namespaced = (*Struct)(nil)
type StructMember struct {
Attributes
nameVariants
Type Type
DefaultValue ConstantValue
OffsetV1 int
OffsetV2 int
HandleInformation *HandleInformation
NaturalConstraint string
WireConstraint string
}
var _ Member = (*StructMember)(nil)
func (sm StructMember) AsParameter() Parameter {
return Parameter{
nameVariants: sm.nameVariants,
Type: sm.Type,
OffsetV1: sm.OffsetV1,
OffsetV2: sm.OffsetV2,
HandleInformation: sm.HandleInformation,
WireConstraint: sm.WireConstraint,
}
}
func (sm StructMember) NameAndType() (string, Type) {
return sm.Name(), sm.Type
}
func (sm StructMember) StorageName() string {
return sm.Name() + "_"
}
// NaturalInitializer is an expression in natural types for initializing the
// struct member within its struct field definition. May be empty if we choose
// to delegate to the default constructor of the member type.
func (sm StructMember) NaturalInitializer() string {
var unwrapArray func(ty *Type) string
unwrapArray = func(ty *Type) string {
if ty.IsPrimitiveType() {
// Zero initialize them.
return "{}"
}
if ty.Kind == TypeKinds.Array {
return unwrapArray(ty.ElementType)
}
return ""
}
if !sm.Type.Nullable {
return unwrapArray(&sm.Type)
}
return ""
}
// NaturalPossiblyInvalidDefaultInitializer is an expression in natural types
// for how to default initialize the struct member within its struct field
// definition. May result in an invalid object if it has a strict union
// somewhere.
func (sm StructMember) NaturalPossiblyInvalidDefaultInitializer() string {
if !sm.Type.Nullable {
switch sm.Type.Kind {
case TypeKinds.Array:
return fmt.Sprintf("::fidl::internal::DefaultConstructPossiblyInvalidObject<%s>::Make()", sm.Type.Unified)
case TypeKinds.Struct, TypeKinds.Table, TypeKinds.Union:
return "::fidl::internal::DefaultConstructPossiblyInvalidObjectTag{}"
}
}
return "{}"
}
func (c *compiler) compileStructMember(val fidlgen.StructMember) StructMember {
t := c.compileType(val.Type)
defaultValue := ConstantValue{}
if val.MaybeDefaultValue != nil {
defaultValue = c.compileConstant(*val.MaybeDefaultValue, &t, val.Type)
}
return StructMember{
Attributes: Attributes{val.Attributes},
nameVariants: structMemberContext.transform(val.Name),
Type: t,
DefaultValue: defaultValue,
OffsetV1: val.FieldShapeV1.Offset,
OffsetV2: val.FieldShapeV2.Offset,
HandleInformation: c.fieldHandleInformation(&val.Type),
NaturalConstraint: t.NaturalFieldConstraint,
WireConstraint: t.WireFieldConstraint,
}
}
type StructPadding struct {
Offset int
MaskType string
Mask string
}
func toStructPadding(in fidlgen.PaddingMarker) StructPadding {
switch len(in.Mask) {
case 2:
return StructPadding{
Offset: in.Offset,
MaskType: "uint16_t",
Mask: fmt.Sprintf("0x%04x", binary.LittleEndian.Uint16(in.Mask)),
}
case 4:
return StructPadding{
Offset: in.Offset,
MaskType: "uint32_t",
Mask: fmt.Sprintf("0x%08x", binary.LittleEndian.Uint32(in.Mask)),
}
case 8:
return StructPadding{
Offset: in.Offset,
MaskType: "uint64_t",
Mask: fmt.Sprintf("0x%016xull", binary.LittleEndian.Uint64(in.Mask)),
}
default:
panic("unexpected mask size")
}
}
func toStructPaddings(in []fidlgen.PaddingMarker) []StructPadding {
var out []StructPadding
for _, m := range in {
out = append(out, toStructPadding(m))
}
return out
}
func (c *compiler) compileStruct(val fidlgen.Struct) *Struct {
name := c.compileNameVariants(val.Name)
codingTableType := name.Wire.ns.member(c.compileCodingTableType(val.Name))
r := Struct{
Attributes: Attributes{val.Attributes},
AnonymousChildren: c.getAnonymousChildren(val.Layout),
TypeShapeV1: TypeShape{val.TypeShapeV1},
TypeShapeV2: TypeShape{val.TypeShapeV2},
Resourceness: val.Resourceness,
nameVariants: name,
CodingTableType: codingTableType,
Members: []StructMember{},
BackingBufferTypeV1: computeAllocation(
TypeShape{val.TypeShapeV1}.MaxTotalSize(), TypeShape{val.TypeShapeV1}.MaxHandles, boundednessBounded).
BackingBufferType(),
BackingBufferTypeV2: computeAllocation(
TypeShape{val.TypeShapeV2}.MaxTotalSize(), TypeShape{val.TypeShapeV2}.MaxHandles, boundednessBounded).
BackingBufferType(),
IsInResult: false,
PaddingV1: toStructPaddings(val.BuildPaddingMarkers(fidlgen.WireFormatVersionV1)),
PaddingV2: toStructPaddings(val.BuildPaddingMarkers(fidlgen.WireFormatVersionV2)),
}
for _, v := range val.Members {
r.Members = append(r.Members, c.compileStructMember(v))
}
if len(r.Members) == 0 {
r.isEmptyStruct = true
r.Members = []StructMember{
c.compileStructMember(fidlgen.EmptyStructMember("__reserved")),
}
}
// Construct a deduped list of decls for IsMemcpyCompatible template definitions.
seen := make(map[string]struct{})
for _, member := range r.Members {
if _, ok := seen[member.Type.HLCPP.String()]; ok {
continue
}
seen[member.Type.HLCPP.String()] = struct{}{}
// The dangerous identifiers test package contains identifiers that won't compile.
// e.g. ::fidl::test::dangerous::struct::types::camel::Interface gives an
// "expected unqualified-id" error because of "struct".
// There isn't an easily accessible dangerous identifiers list to replace identifiers.
if strings.Contains(member.Type.HLCPP.String(), "::fidl::test::dangerous::") {
continue
}
r.FullDeclMemcpyCompatibleDeps = append(r.FullDeclMemcpyCompatibleDeps, member.Type.nameVariants)
}
return &r
}