| // Copyright 2018 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 codegen |
| |
| import ( |
| "bytes" |
| "encoding/binary" |
| "fmt" |
| "sort" |
| "strings" |
| |
| fidl "go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen" |
| ) |
| |
| type EncodedCompoundIdentifier = fidl.EncodedCompoundIdentifier |
| |
| type Type struct { |
| Decl string |
| DeclType fidl.DeclType |
| Derives derives |
| } |
| |
| type Bits struct { |
| fidl.Bits |
| Name string |
| Type string |
| Members []BitsMember |
| } |
| |
| type BitsMember struct { |
| fidl.BitsMember |
| Name string |
| Value string |
| } |
| |
| type Const struct { |
| fidl.Attributes |
| Name string |
| Type string |
| Value string |
| } |
| |
| type Enum struct { |
| fidl.Enum |
| Name string |
| Type string |
| Members []EnumMember |
| } |
| |
| type EnumMember struct { |
| fidl.EnumMember |
| Name string |
| Value string |
| } |
| |
| type Union struct { |
| fidl.Union |
| Derives derives |
| ECI EncodedCompoundIdentifier |
| Name string |
| Members []UnionMember |
| } |
| |
| type UnionMember struct { |
| fidl.Attributes |
| Type string |
| OGType fidl.Type |
| Name string |
| Ordinal int |
| HasHandleMetadata bool |
| HandleRights string |
| HandleSubtype string |
| } |
| |
| type ResultOkEntry struct { |
| OGType fidl.Type |
| Type string |
| HasHandleMetadata bool |
| HandleWrapperName string |
| } |
| |
| type Result struct { |
| fidl.Attributes |
| ECI EncodedCompoundIdentifier |
| Derives derives |
| Name string |
| Ok []ResultOkEntry |
| ErrOGType fidl.Type |
| ErrType string |
| Size int |
| Alignment int |
| } |
| |
| type Struct struct { |
| fidl.Attributes |
| ECI EncodedCompoundIdentifier |
| Derives derives |
| Name string |
| Members []StructMember |
| PaddingMarkers []PaddingMarker |
| FlattenedPaddingMarkers []PaddingMarker |
| // Store size and alignment for Old and V1 versions of the wire format. The |
| // numbers will be different if the struct contains a union within it. Only |
| // structs have this information because fidl::encoding only uses these |
| // precalculated numbers for structs and unions. |
| Size int |
| Alignment int |
| HasPadding bool |
| // True iff the fidl_struct_copy! macro should be used instead of fidl_struct!. |
| UseFidlStructCopy bool |
| } |
| |
| type StructMember struct { |
| fidl.Attributes |
| OGType fidl.Type |
| Type string |
| Name string |
| Offset int |
| HasDefault bool |
| DefaultValue string |
| HasHandleMetadata bool |
| HandleRights string |
| HandleSubtype string |
| } |
| |
| type PaddingMarker struct { |
| Type string |
| Offset int |
| // Mask is a string so it can be in hex. |
| Mask string |
| } |
| |
| type Table struct { |
| fidl.Table |
| Derives derives |
| ECI EncodedCompoundIdentifier |
| Name string |
| Members []TableMember |
| } |
| |
| type TableMember struct { |
| fidl.Attributes |
| OGType fidl.Type |
| Type string |
| Name string |
| Ordinal int |
| HasHandleMetadata bool |
| HandleRights string |
| HandleSubtype string |
| } |
| |
| type Protocol struct { |
| fidl.Attributes |
| ECI EncodedCompoundIdentifier |
| Name string |
| Methods []Method |
| ServiceName string |
| } |
| |
| type Method struct { |
| fidl.Attributes |
| Ordinal uint64 |
| Name string |
| CamelName string |
| HasRequest bool |
| Request []Parameter |
| HasResponse bool |
| Response []Parameter |
| Result *Result |
| IsTransitional bool |
| } |
| |
| type Parameter struct { |
| OGType fidl.Type |
| Type string |
| BorrowedType string |
| Name string |
| HandleWrapperName string |
| HasHandleMetadata bool |
| } |
| |
| type HandleMetadataWrapper struct { |
| Name string |
| Subtype string |
| Rights string |
| } |
| |
| type Service struct { |
| fidl.Attributes |
| Name string |
| Members []ServiceMember |
| ServiceName string |
| } |
| |
| type ServiceMember struct { |
| fidl.Attributes |
| ProtocolType string |
| Name string |
| CamelName string |
| SnakeName string |
| } |
| |
| type Root struct { |
| ExternCrates []string |
| Bits []Bits |
| Consts []Const |
| Enums []Enum |
| Structs []Struct |
| Unions []Union |
| Results []Result |
| Tables []Table |
| Protocols []Protocol |
| Services []Service |
| HandleMetadataWrappers []HandleMetadataWrapper |
| } |
| |
| func (r *Root) findProtocol(eci EncodedCompoundIdentifier) *Protocol { |
| for i := range r.Protocols { |
| if r.Protocols[i].ECI == eci { |
| return &r.Protocols[i] |
| } |
| } |
| return nil |
| } |
| |
| func (r *Root) findStruct(eci EncodedCompoundIdentifier) *Struct { |
| for i := range r.Structs { |
| if r.Structs[i].ECI == eci { |
| return &r.Structs[i] |
| } |
| } |
| return nil |
| } |
| |
| func (r *Root) findTable(eci EncodedCompoundIdentifier) *Table { |
| for i := range r.Tables { |
| if r.Tables[i].ECI == eci { |
| return &r.Tables[i] |
| } |
| } |
| return nil |
| } |
| |
| func (r *Root) findResult(eci EncodedCompoundIdentifier) *Result { |
| for i := range r.Results { |
| if r.Results[i].ECI == eci { |
| return &r.Results[i] |
| } |
| } |
| return nil |
| } |
| |
| func (r *Root) findUnion(eci EncodedCompoundIdentifier) *Union { |
| for i := range r.Unions { |
| if r.Unions[i].ECI == eci { |
| return &r.Unions[i] |
| } |
| } |
| return nil |
| } |
| |
| var reservedWords = map[string]struct{}{ |
| "as": {}, |
| "box": {}, |
| "break": {}, |
| "const": {}, |
| "continue": {}, |
| "crate": {}, |
| "else": {}, |
| "enum": {}, |
| "extern": {}, |
| "false": {}, |
| "fn": {}, |
| "for": {}, |
| "if": {}, |
| "impl": {}, |
| "in": {}, |
| "let": {}, |
| "loop": {}, |
| "match": {}, |
| "mod": {}, |
| "move": {}, |
| "mut": {}, |
| "pub": {}, |
| "ref": {}, |
| "return": {}, |
| "self": {}, |
| "Self": {}, |
| "static": {}, |
| "struct": {}, |
| "super": {}, |
| "trait": {}, |
| "true": {}, |
| "type": {}, |
| "unsafe": {}, |
| "use": {}, |
| "where": {}, |
| "while": {}, |
| |
| // Keywords reserved for future use (future-proofing...) |
| "abstract": {}, |
| "alignof": {}, |
| "await": {}, |
| "become": {}, |
| "do": {}, |
| "final": {}, |
| "macro": {}, |
| "offsetof": {}, |
| "override": {}, |
| "priv": {}, |
| "proc": {}, |
| "pure": {}, |
| "sizeof": {}, |
| "typeof": {}, |
| "unsized": {}, |
| "virtual": {}, |
| "yield": {}, |
| |
| // Weak keywords (special meaning in specific contexts) |
| // These are ok in all contexts of fidl names. |
| //"default": {}, |
| //"union": {}, |
| |
| // Things that are not keywords, but for which collisions would be very unpleasant |
| "Result": {}, |
| "Ok": {}, |
| "Err": {}, |
| "Vec": {}, |
| "Option": {}, |
| "Some": {}, |
| "None": {}, |
| "Box": {}, |
| "Future": {}, |
| "Stream": {}, |
| "Never": {}, |
| "Send": {}, |
| "fidl": {}, |
| "futures": {}, |
| "zx": {}, |
| "async": {}, |
| "on_open": {}, |
| "OnOpen": {}, |
| } |
| |
| var reservedSuffixes = []string{ |
| "Impl", |
| "Marker", |
| "Proxy", |
| "ProxyProtocol", |
| "ControlHandle", |
| "Responder", |
| "Server", |
| } |
| |
| func isReservedWord(str string) bool { |
| _, ok := reservedWords[str] |
| return ok |
| } |
| |
| func hasReservedSuffix(str string) bool { |
| for _, suffix := range reservedSuffixes { |
| if strings.HasSuffix(str, suffix) { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func changeIfReserved(val fidl.Identifier) string { |
| str := string(val) |
| if hasReservedSuffix(str) || isReservedWord(str) { |
| return str + "_" |
| } |
| return str |
| } |
| |
| var primitiveTypes = map[fidl.PrimitiveSubtype]string{ |
| fidl.Bool: "bool", |
| fidl.Int8: "i8", |
| fidl.Int16: "i16", |
| fidl.Int32: "i32", |
| fidl.Int64: "i64", |
| fidl.Uint8: "u8", |
| fidl.Uint16: "u16", |
| fidl.Uint32: "u32", |
| fidl.Uint64: "u64", |
| fidl.Float32: "f32", |
| fidl.Float64: "f64", |
| } |
| |
| var handleSubtypes = map[fidl.HandleSubtype]string{ |
| fidl.Bti: "Bti", |
| fidl.Channel: "Channel", |
| fidl.Clock: "Clock", |
| fidl.DebugLog: "DebugLog", |
| fidl.Event: "Event", |
| fidl.Eventpair: "EventPair", |
| fidl.Exception: "Exception", |
| fidl.Fifo: "Fifo", |
| fidl.Guest: "Guest", |
| fidl.Handle: "Handle", |
| fidl.Interrupt: "Interrupt", |
| fidl.Iommu: "Iommu", |
| fidl.Job: "Job", |
| fidl.Pager: "Pager", |
| fidl.PciDevice: "PciDevice", |
| fidl.Pmt: "Pmt", |
| fidl.Port: "Port", |
| fidl.Process: "Process", |
| fidl.Profile: "Profile", |
| fidl.Resource: "Resource", |
| fidl.Socket: "Socket", |
| fidl.Stream: "Stream", |
| fidl.SuspendToken: "SuspendToken", |
| fidl.Thread: "Thread", |
| fidl.Time: "Timer", |
| fidl.Vcpu: "Vcpu", |
| fidl.Vmar: "Vmar", |
| fidl.Vmo: "Vmo", |
| } |
| |
| var handleSubtypeConsts = map[fidl.HandleSubtype]string{ |
| fidl.Bti: "BTI", |
| fidl.Channel: "CHANNEL", |
| fidl.Clock: "CLOCK", |
| fidl.DebugLog: "LOG", |
| fidl.Event: "EVENT", |
| fidl.Eventpair: "EVENTPAIR", |
| fidl.Exception: "EXCEPTION", |
| fidl.Fifo: "FIFO", |
| fidl.Guest: "GUEST", |
| fidl.Handle: "NONE", |
| fidl.Interrupt: "INTERRUPT", |
| fidl.Iommu: "IOMMU", |
| fidl.Job: "JOB", |
| fidl.Pager: "PAGER", |
| fidl.PciDevice: "PCI_DEVICE", |
| fidl.Pmt: "PMT", |
| fidl.Port: "PORT", |
| fidl.Process: "PROCESS", |
| fidl.Profile: "PROFILE", |
| fidl.Resource: "RESOURCE", |
| fidl.Socket: "SOCKET", |
| fidl.Stream: "STREAM", |
| fidl.SuspendToken: "SUSPEND_TOKEN", |
| fidl.Thread: "THREAD", |
| fidl.Time: "TIMER", |
| fidl.Vcpu: "VCPU", |
| fidl.Vmar: "VMAR", |
| fidl.Vmo: "VMO", |
| } |
| |
| type compiler struct { |
| decls fidl.DeclInfoMap |
| library fidl.LibraryIdentifier |
| externCrates map[string]struct{} |
| requestResponsePayload map[fidl.EncodedCompoundIdentifier]fidl.Struct |
| structs map[fidl.EncodedCompoundIdentifier]fidl.Struct |
| results map[fidl.EncodedCompoundIdentifier]Result |
| handleMetadataWrappers map[string]HandleMetadataWrapper |
| } |
| |
| func (c *compiler) inExternalLibrary(ci fidl.CompoundIdentifier) bool { |
| if len(ci.Library) != len(c.library) { |
| return true |
| } |
| for i, part := range c.library { |
| if ci.Library[i] != part { |
| return true |
| } |
| } |
| return false |
| } |
| |
| func compileCamelIdentifier(val fidl.Identifier) string { |
| return fidl.ToUpperCamelCase(changeIfReserved(val)) |
| } |
| |
| func compileLibraryName(library fidl.LibraryIdentifier) string { |
| parts := []string{"fidl"} |
| for _, part := range library { |
| parts = append(parts, string(part)) |
| } |
| return changeIfReserved(fidl.Identifier(strings.Join(parts, "_"))) |
| } |
| |
| func compileSnakeIdentifier(val fidl.Identifier) string { |
| return fidl.ToSnakeCase(changeIfReserved(val)) |
| } |
| |
| func compileScreamingSnakeIdentifier(val fidl.Identifier) string { |
| return fidl.ConstNameToAllCapsSnake(changeIfReserved(val)) |
| } |
| |
| func (c *compiler) compileCompoundIdentifier(val fidl.CompoundIdentifier) string { |
| strs := []string{} |
| if c.inExternalLibrary(val) { |
| externName := compileLibraryName(val.Library) |
| c.externCrates[externName] = struct{}{} |
| strs = append(strs, externName) |
| } |
| str := changeIfReserved(val.Name) |
| strs = append(strs, str) |
| if val.Member != "" { |
| strs = append(strs, string(val.Member)) |
| } |
| return strings.Join(strs, "::") |
| } |
| |
| func (c *compiler) compileCamelCompoundIdentifier(eci fidl.EncodedCompoundIdentifier) string { |
| val := fidl.ParseCompoundIdentifier(eci) |
| val.Name = fidl.Identifier(compileCamelIdentifier(val.Name)) |
| return c.compileCompoundIdentifier(val) |
| } |
| |
| func (c *compiler) compileSnakeCompoundIdentifier(eci fidl.EncodedCompoundIdentifier) string { |
| val := fidl.ParseCompoundIdentifier(eci) |
| val.Name = fidl.Identifier(compileSnakeIdentifier(val.Name)) |
| return c.compileCompoundIdentifier(val) |
| } |
| |
| func (c *compiler) compileScreamingSnakeCompoundIdentifier(eci fidl.EncodedCompoundIdentifier) string { |
| val := fidl.ParseCompoundIdentifier(eci) |
| val.Name = fidl.Identifier(compileScreamingSnakeIdentifier(val.Name)) |
| return c.compileCompoundIdentifier(val) |
| } |
| |
| func compileLiteral(val fidl.Literal, typ fidl.Type) string { |
| switch val.Kind { |
| case fidl.StringLiteral: |
| return fmt.Sprintf("r###\"%s\"###", val.Value) |
| case fidl.NumericLiteral: |
| if typ.Kind == fidl.PrimitiveType && |
| (typ.PrimitiveSubtype == fidl.Float32 || typ.PrimitiveSubtype == fidl.Float64) { |
| if !strings.ContainsRune(val.Value, '.') { |
| return fmt.Sprintf("%s.0", val.Value) |
| } |
| return val.Value |
| } |
| return val.Value |
| case fidl.TrueLiteral: |
| return "true" |
| case fidl.FalseLiteral: |
| return "false" |
| case fidl.DefaultLiteral: |
| return "::Default::default()" |
| default: |
| panic(fmt.Sprintf("unknown literal kind: %v", val.Kind)) |
| } |
| } |
| |
| func (c *compiler) compileConstant(val fidl.Constant, typ fidl.Type) string { |
| switch val.Kind { |
| case fidl.IdentifierConstant: |
| parts := fidl.ParseCompoundIdentifier(val.Identifier) |
| if parts.Member == fidl.Identifier("") { |
| // Top-level constant. |
| parts.Name = fidl.Identifier(compileScreamingSnakeIdentifier(parts.Name)) |
| } else { |
| // Bits or enum member. |
| parts.Name = fidl.Identifier(compileCamelIdentifier(parts.Name)) |
| // TODO(fxbug.dev/47034) For bits the member should be SCREAMING_SNAKE_CASE. |
| parts.Member = fidl.Identifier(compileCamelIdentifier(parts.Member)) |
| } |
| return c.compileCompoundIdentifier(parts) |
| case fidl.LiteralConstant: |
| return compileLiteral(val.Literal, typ) |
| default: |
| panic(fmt.Sprintf("unknown constant kind: %v", val.Kind)) |
| } |
| } |
| |
| func (c *compiler) compileConst(val fidl.Const) Const { |
| name := c.compileScreamingSnakeCompoundIdentifier(val.Name) |
| var r Const |
| if val.Type.Kind == fidl.StringType { |
| r = Const{ |
| Attributes: val.Attributes, |
| Type: "&str", |
| Name: name, |
| Value: c.compileConstant(val.Value, val.Type), |
| } |
| } else { |
| r = Const{ |
| Attributes: val.Attributes, |
| Type: c.compileType(val.Type, false).Decl, |
| Name: name, |
| Value: c.compileConstant(val.Value, val.Type), |
| } |
| } |
| return r |
| } |
| |
| func compilePrimitiveSubtype(val fidl.PrimitiveSubtype) string { |
| if t, ok := primitiveTypes[val]; ok { |
| return t |
| } |
| panic(fmt.Sprintf("unknown primitive type: %v", val)) |
| } |
| |
| func compileHandleSubtype(val fidl.HandleSubtype) string { |
| if t, ok := handleSubtypes[val]; ok { |
| return t |
| } |
| panic(fmt.Sprintf("unknown handle type: %v", val)) |
| } |
| |
| type FieldHandleInformation struct { |
| fullObjectType string |
| fullRights string |
| shortObjectType string |
| shortRights string |
| hasHandleMetadata bool |
| } |
| |
| func (c *compiler) fieldHandleInformation(val *fidl.Type) FieldHandleInformation { |
| if val.ElementType != nil { |
| return c.fieldHandleInformation(val.ElementType) |
| } |
| if val.Kind == fidl.RequestType { |
| return FieldHandleInformation{ |
| fullObjectType: "fidl::ObjectType::CHANNEL", |
| fullRights: "fidl::Rights::CHANNEL_DEFAULT", |
| shortObjectType: "CHANNEL", |
| shortRights: "CHANNEL_DEFAULT", |
| hasHandleMetadata: true, |
| } |
| } |
| if val.Kind == fidl.IdentifierType { |
| declInfo, ok := c.decls[val.Identifier] |
| if !ok { |
| panic(fmt.Sprintf("unknown identifier: %v", val.Identifier)) |
| } |
| if declInfo.Type == fidl.ProtocolDeclType { |
| return FieldHandleInformation{ |
| fullObjectType: "fidl::ObjectType::CHANNEL", |
| fullRights: "fidl::Rights::CHANNEL_DEFAULT", |
| shortObjectType: "CHANNEL", |
| shortRights: "CHANNEL_DEFAULT", |
| hasHandleMetadata: true, |
| } |
| } |
| } |
| if val.Kind != fidl.HandleType { |
| return FieldHandleInformation{ |
| fullObjectType: "fidl::ObjectType::NONE", |
| fullRights: "fidl::Rights::NONE", |
| shortObjectType: "NONE", |
| shortRights: "NONE", |
| hasHandleMetadata: false, |
| } |
| } |
| subtype, ok := handleSubtypeConsts[val.HandleSubtype] |
| if !ok { |
| panic(fmt.Sprintf("unknown handle type for const: %v", val)) |
| } |
| return FieldHandleInformation{ |
| fullObjectType: fmt.Sprintf("fidl::ObjectType::%s", subtype), |
| fullRights: fmt.Sprintf("fidl::Rights::from_bits_const(%d).unwrap()", val.HandleRights), |
| shortObjectType: subtype, |
| shortRights: fmt.Sprintf("%d", val.HandleRights), |
| hasHandleMetadata: true, |
| } |
| } |
| |
| func (c *compiler) compileType(val fidl.Type, borrowed bool) Type { |
| var r string |
| var declType fidl.DeclType |
| switch val.Kind { |
| case fidl.ArrayType: |
| t := c.compileType(*val.ElementType, borrowed) |
| r = fmt.Sprintf("[%s; %v]", t.Decl, *val.ElementCount) |
| if borrowed { |
| r = fmt.Sprintf("&mut %s", r) |
| } |
| case fidl.VectorType: |
| t := c.compileType(*val.ElementType, borrowed) |
| var inner string |
| if borrowed { |
| // We use slices for primitive numeric types so that |
| // encoding becomes a memcpy. Rust does not guarantee |
| // the bit patterns for bool values, so we omit them |
| // from the optimization. |
| if val.ElementType.Kind == fidl.PrimitiveType && val.ElementType.PrimitiveSubtype != fidl.Bool { |
| inner = fmt.Sprintf("&[%s]", t.Decl) |
| } else { |
| inner = fmt.Sprintf("&mut dyn ExactSizeIterator<Item = %s>", t.Decl) |
| } |
| } else { |
| inner = fmt.Sprintf("Vec<%s>", t.Decl) |
| } |
| if val.Nullable { |
| r = fmt.Sprintf("Option<%s>", inner) |
| } else { |
| r = inner |
| } |
| case fidl.StringType: |
| if borrowed { |
| if val.Nullable { |
| r = "Option<&str>" |
| } else { |
| r = "&str" |
| } |
| } else { |
| if val.Nullable { |
| r = "Option<String>" |
| } else { |
| r = "String" |
| } |
| } |
| case fidl.HandleType: |
| r = fmt.Sprintf("fidl::%s", compileHandleSubtype(val.HandleSubtype)) |
| if val.Nullable { |
| r = fmt.Sprintf("Option<%s>", r) |
| } |
| case fidl.RequestType: |
| r = c.compileCamelCompoundIdentifier(val.RequestSubtype) |
| r = fmt.Sprintf("fidl::endpoints::ServerEnd<%sMarker>", r) |
| if val.Nullable { |
| r = fmt.Sprintf("Option<%s>", r) |
| } |
| case fidl.PrimitiveType: |
| // Primitive types are small, simple, and never contain handles, |
| // so there's no need to borrow them |
| r = compilePrimitiveSubtype(val.PrimitiveSubtype) |
| case fidl.IdentifierType: |
| t := c.compileCamelCompoundIdentifier(val.Identifier) |
| declInfo, ok := c.decls[val.Identifier] |
| if !ok { |
| panic(fmt.Sprintf("unknown identifier: %v", val.Identifier)) |
| } |
| declType = declInfo.Type |
| switch declType { |
| case fidl.BitsDeclType, fidl.EnumDeclType: |
| // Bits and enums are small, simple, and never contain handles, |
| // so no need to borrow |
| borrowed = false |
| fallthrough |
| case fidl.ConstDeclType, fidl.StructDeclType, fidl.UnionDeclType: |
| if val.Nullable { |
| if borrowed { |
| r = fmt.Sprintf("Option<&mut %s>", t) |
| } else { |
| r = fmt.Sprintf("Option<Box<%s>>", t) |
| } |
| } else { |
| if borrowed { |
| r = fmt.Sprintf("&mut %s", t) |
| } else { |
| r = t |
| } |
| } |
| case fidl.TableDeclType: |
| if val.Nullable { |
| panic("tables cannot be nullable") |
| } |
| // TODO(fxbug.dev/42304): Replace with "&mut %s". |
| r = t |
| case fidl.ProtocolDeclType: |
| r = fmt.Sprintf("fidl::endpoints::ClientEnd<%sMarker>", t) |
| if val.Nullable { |
| r = fmt.Sprintf("Option<%s>", r) |
| } |
| default: |
| panic(fmt.Sprintf("unknown declaration type: %v", declType)) |
| } |
| default: |
| panic(fmt.Sprintf("unknown type kind: %v", val.Kind)) |
| } |
| |
| return Type{ |
| Decl: r, |
| DeclType: declType, |
| } |
| } |
| |
| func (c *compiler) compileBits(val fidl.Bits) Bits { |
| e := Bits{ |
| Bits: val, |
| Name: c.compileCamelCompoundIdentifier(val.Name), |
| Type: c.compileType(val.Type, false).Decl, |
| Members: []BitsMember{}, |
| } |
| for _, v := range val.Members { |
| e.Members = append(e.Members, BitsMember{ |
| BitsMember: v, |
| // TODO(fxbug.dev/47034) Should be SCREAMING_SNAKE_CASE. |
| Name: compileCamelIdentifier(v.Name), |
| Value: c.compileConstant(v.Value, val.Type), |
| }) |
| } |
| return e |
| } |
| |
| func (c *compiler) compileEnum(val fidl.Enum) Enum { |
| e := Enum{ |
| Enum: val, |
| Name: c.compileCamelCompoundIdentifier(val.Name), |
| Type: compilePrimitiveSubtype(val.Type), |
| Members: []EnumMember{}, |
| } |
| for _, v := range val.Members { |
| e.Members = append(e.Members, EnumMember{ |
| EnumMember: v, |
| Name: compileCamelIdentifier(v.Name), |
| // TODO(fxbug.dev/7660): When we expose types consistently in the IR, we |
| // will not need to plug this here. |
| Value: c.compileConstant(v.Value, fidl.Type{ |
| Kind: fidl.PrimitiveType, |
| PrimitiveSubtype: val.Type, |
| }), |
| }) |
| } |
| return e |
| } |
| |
| func (c *compiler) compileHandleMetadataWrapper(val *fidl.Type) (string, bool) { |
| hi := c.fieldHandleInformation(val) |
| name := fmt.Sprintf("HandleWrapperObjectType%sRights%s", hi.shortObjectType, hi.shortRights) |
| wrapper := HandleMetadataWrapper{ |
| Name: name, |
| Subtype: hi.fullObjectType, |
| Rights: hi.fullRights, |
| } |
| if hi.hasHandleMetadata { |
| if _, ok := c.handleMetadataWrappers[name]; !ok { |
| c.handleMetadataWrappers[name] = wrapper |
| } |
| } |
| return name, hi.hasHandleMetadata |
| } |
| |
| func (c *compiler) compileParameterArray(payload fidl.EncodedCompoundIdentifier) []Parameter { |
| val, ok := c.requestResponsePayload[payload] |
| if !ok { |
| panic(fmt.Sprintf("unknown request/response struct: %v", payload)) |
| } |
| |
| var parameters []Parameter |
| for _, v := range val.Members { |
| wrapperName, hasHandleMetadata := c.compileHandleMetadataWrapper(&v.Type) |
| parameters = append(parameters, Parameter{ |
| OGType: v.Type, |
| Type: c.compileType(v.Type, false).Decl, |
| BorrowedType: c.compileType(v.Type, true).Decl, |
| Name: compileSnakeIdentifier(v.Name), |
| HandleWrapperName: wrapperName, |
| HasHandleMetadata: hasHandleMetadata, |
| }) |
| } |
| return parameters |
| } |
| |
| func (c *compiler) compileProtocol(val fidl.Protocol) Protocol { |
| r := Protocol{ |
| Attributes: val.Attributes, |
| ECI: val.Name, |
| Name: c.compileCamelCompoundIdentifier(val.Name), |
| Methods: []Method{}, |
| ServiceName: strings.Trim(val.GetServiceName(), "\""), |
| } |
| |
| for _, v := range val.Methods { |
| name := compileSnakeIdentifier(v.Name) |
| camelName := compileCamelIdentifier(v.Name) |
| var foundResult *Result |
| if len(v.Response) == 1 && v.Response[0].Type.Kind == fidl.IdentifierType { |
| responseType := v.Response[0].Type |
| if result, ok := c.results[responseType.Identifier]; ok { |
| foundResult = &result |
| } |
| } |
| m := Method{ |
| Attributes: v.Attributes, |
| Ordinal: v.Ordinal, |
| Name: name, |
| CamelName: camelName, |
| HasRequest: v.HasRequest, |
| HasResponse: v.HasResponse, |
| Result: foundResult, |
| IsTransitional: v.IsTransitional(), |
| } |
| if v.RequestPayload != "" { |
| m.Request = c.compileParameterArray(v.RequestPayload) |
| } |
| if v.ResponsePayload != "" { |
| m.Response = c.compileParameterArray(v.ResponsePayload) |
| } |
| r.Methods = append(r.Methods, m) |
| } |
| |
| return r |
| } |
| |
| func (c *compiler) compileService(val fidl.Service) Service { |
| r := Service{ |
| Attributes: val.Attributes, |
| Name: c.compileCamelCompoundIdentifier(val.Name), |
| Members: []ServiceMember{}, |
| ServiceName: val.GetServiceName(), |
| } |
| |
| for _, v := range val.Members { |
| m := ServiceMember{ |
| Attributes: v.Attributes, |
| Name: string(v.Name), |
| CamelName: compileCamelIdentifier(v.Name), |
| SnakeName: compileSnakeIdentifier(v.Name), |
| ProtocolType: c.compileCamelCompoundIdentifier(v.Type.Identifier), |
| } |
| r.Members = append(r.Members, m) |
| } |
| |
| return r |
| } |
| |
| func (c *compiler) compileStructMember(val fidl.StructMember) StructMember { |
| memberType := c.compileType(val.Type, false) |
| hi := c.fieldHandleInformation(&val.Type) |
| return StructMember{ |
| Attributes: val.Attributes, |
| Type: memberType.Decl, |
| OGType: val.Type, |
| Name: compileSnakeIdentifier(val.Name), |
| Offset: val.FieldShapeV1.Offset, |
| HasDefault: false, |
| DefaultValue: "", // TODO(cramertj) support defaults |
| HasHandleMetadata: hi.hasHandleMetadata, |
| HandleSubtype: hi.fullObjectType, |
| HandleRights: hi.fullRights, |
| } |
| } |
| |
| func (c *compiler) populateFullStructMaskForStruct(mask []byte, val fidl.Struct, flatten bool) { |
| paddingEnd := val.TypeShapeV1.InlineSize - 1 |
| for i := len(val.Members) - 1; i >= 0; i-- { |
| member := val.Members[i] |
| if flatten { |
| c.populateFullStructMaskForType(mask[member.FieldShapeV1.Offset:paddingEnd+1], &member.Type, flatten) |
| } |
| for j := 0; j < member.FieldShapeV1.Padding; j++ { |
| mask[paddingEnd-j] = 0xff |
| } |
| paddingEnd = member.FieldShapeV1.Offset - 1 |
| } |
| } |
| |
| func (c *compiler) populateFullStructMaskForType(mask []byte, typ *fidl.Type, flatten bool) { |
| if typ.Nullable { |
| return |
| } |
| switch typ.Kind { |
| case fidl.ArrayType: |
| elemByteSize := len(mask) / *typ.ElementCount |
| for i := 0; i < *typ.ElementCount; i++ { |
| c.populateFullStructMaskForType(mask[i*elemByteSize:(i+1)*elemByteSize], typ.ElementType, flatten) |
| } |
| case fidl.IdentifierType: |
| if c.inExternalLibrary(fidl.ParseCompoundIdentifier(typ.Identifier)) { |
| // This behavior is matched by computeUseFullStructCopy. |
| return |
| } |
| declType := c.decls[typ.Identifier].Type |
| if declType == fidl.StructDeclType { |
| st, ok := c.structs[typ.Identifier] |
| if !ok { |
| panic(fmt.Sprintf("struct not found: %v", typ.Identifier)) |
| } |
| c.populateFullStructMaskForStruct(mask, st, flatten) |
| } |
| } |
| } |
| |
| func (c *compiler) buildPaddingMarkers(val fidl.Struct, flatten bool) []PaddingMarker { |
| var paddingMarkers []PaddingMarker |
| |
| // Construct a mask across the whole struct with 0xff bytes where there is padding. |
| fullStructMask := make([]byte, val.TypeShapeV1.InlineSize) |
| c.populateFullStructMaskForStruct(fullStructMask, val, flatten) |
| |
| // Split up the mask into aligned integer mask segments that can be outputted in the |
| // fidl_struct! macro. |
| // Only the sections needing padding are outputted. |
| // e.g. 00ffff0000ffff000000000000000000 -> 00ffff0000ffff00, 0000000000000000 |
| // -> []PaddingMarker{"u64", 0, "0x00ffff0000ffff00u64"} |
| extractNonzeroSliceOffsets := func(stride int) []int { |
| var offsets []int |
| for endi := stride - 1; endi < len(fullStructMask); endi += stride { |
| i := endi - (stride - 1) |
| if bytes.Contains(fullStructMask[i:i+stride], []byte{0xff}) { |
| offsets = append(offsets, i) |
| } |
| } |
| return offsets |
| } |
| zeroSlice := func(s []byte) { |
| for i := range s { |
| s[i] = 0 |
| } |
| } |
| for _, i := range extractNonzeroSliceOffsets(8) { |
| s := fullStructMask[i : i+8] |
| paddingMarkers = append(paddingMarkers, PaddingMarker{ |
| Type: "u64", |
| Offset: i, |
| Mask: fmt.Sprintf("0x%016xu64", binary.LittleEndian.Uint64(s)), |
| }) |
| zeroSlice(s) // Reset the buffer for the next iteration. |
| } |
| for _, i := range extractNonzeroSliceOffsets(4) { |
| s := fullStructMask[i : i+4] |
| paddingMarkers = append(paddingMarkers, PaddingMarker{ |
| Type: "u32", |
| Offset: i, |
| Mask: fmt.Sprintf("0x%08xu32", binary.LittleEndian.Uint32(s)), |
| }) |
| zeroSlice(s) // Reset the buffer for the next iteration. |
| } |
| for _, i := range extractNonzeroSliceOffsets(2) { |
| s := fullStructMask[i : i+2] |
| paddingMarkers = append(paddingMarkers, PaddingMarker{ |
| Type: "u16", |
| Offset: i, |
| Mask: fmt.Sprintf("0x%04xu16", binary.LittleEndian.Uint16(s)), |
| }) |
| zeroSlice(s) // Reset the buffer for the next iteration. |
| } |
| if bytes.Contains(fullStructMask, []byte{0xff}) { |
| // This shouldn't be possible because it requires an alignment 1 struct to have padding. |
| panic(fmt.Sprintf("expected mask to be zero, was %v", fullStructMask)) |
| } |
| return paddingMarkers |
| } |
| |
| func (c *compiler) computeUseFidlStructCopyForStruct(st fidl.Struct) bool { |
| for _, member := range st.Members { |
| if !c.computeUseFidlStructCopy(&member.Type) { |
| return false |
| } |
| } |
| return true |
| } |
| |
| func (c *compiler) computeUseFidlStructCopy(typ *fidl.Type) bool { |
| if typ.Nullable { |
| return false |
| } |
| switch typ.Kind { |
| case fidl.ArrayType: |
| return c.computeUseFidlStructCopy(typ.ElementType) |
| case fidl.VectorType: |
| return false |
| case fidl.StringType: |
| return false |
| case fidl.HandleType: |
| return false |
| case fidl.RequestType: |
| return false |
| case fidl.PrimitiveType: |
| switch typ.PrimitiveSubtype { |
| case fidl.Bool, fidl.Float32, fidl.Float64: |
| return false |
| } |
| return true |
| case fidl.IdentifierType: |
| if c.inExternalLibrary(fidl.ParseCompoundIdentifier(typ.Identifier)) { |
| return false |
| } |
| declType := c.decls[typ.Identifier].Type |
| switch declType { |
| case fidl.BitsDeclType: |
| return false |
| case fidl.EnumDeclType: |
| return false |
| case fidl.ProtocolDeclType: |
| return false |
| case fidl.StructDeclType: |
| st, ok := c.structs[typ.Identifier] |
| if !ok { |
| panic(fmt.Sprintf("struct not found: %v", typ.Identifier)) |
| } |
| return c.computeUseFidlStructCopyForStruct(st) |
| case fidl.UnionDeclType: |
| return false |
| case fidl.TableDeclType: |
| return false |
| default: |
| panic(fmt.Sprintf("unknown declaration type: %v", declType)) |
| } |
| default: |
| panic(fmt.Sprintf("unknown type kind: %v", typ.Kind)) |
| } |
| } |
| |
| func (c *compiler) compileStruct(val fidl.Struct) Struct { |
| name := c.compileCamelCompoundIdentifier(val.Name) |
| r := Struct{ |
| Attributes: val.Attributes, |
| ECI: val.Name, |
| Name: name, |
| Members: []StructMember{}, |
| Size: val.TypeShapeV1.InlineSize, |
| Alignment: val.TypeShapeV1.Alignment, |
| PaddingMarkers: c.buildPaddingMarkers(val, false), |
| FlattenedPaddingMarkers: c.buildPaddingMarkers(val, true), |
| } |
| |
| for _, v := range val.Members { |
| member := c.compileStructMember(v) |
| r.Members = append(r.Members, member) |
| r.HasPadding = r.HasPadding || (v.FieldShapeV1.Padding != 0) |
| } |
| |
| r.UseFidlStructCopy = c.computeUseFidlStructCopyForStruct(val) |
| |
| return r |
| } |
| |
| func (c *compiler) compileUnionMember(val fidl.UnionMember) UnionMember { |
| hi := c.fieldHandleInformation(&val.Type) |
| return UnionMember{ |
| Attributes: val.Attributes, |
| Type: c.compileType(val.Type, false).Decl, |
| OGType: val.Type, |
| Name: compileCamelIdentifier(val.Name), |
| Ordinal: val.Ordinal, |
| HasHandleMetadata: hi.hasHandleMetadata, |
| HandleSubtype: hi.fullObjectType, |
| HandleRights: hi.fullRights, |
| } |
| } |
| |
| func (c *compiler) compileUnion(val fidl.Union) Union { |
| r := Union{ |
| Union: val, |
| ECI: val.Name, |
| Name: c.compileCamelCompoundIdentifier(val.Name), |
| Members: []UnionMember{}, |
| } |
| |
| for _, v := range val.Members { |
| if v.Reserved { |
| continue |
| } |
| r.Members = append(r.Members, c.compileUnionMember(v)) |
| } |
| |
| return r |
| } |
| |
| func (c *compiler) compileResultFromUnion(val fidl.Union, root Root) Result { |
| r := Result{ |
| Attributes: val.Attributes, |
| ECI: val.Name, |
| Name: c.compileCamelCompoundIdentifier(val.Name), |
| Ok: []ResultOkEntry{}, |
| ErrOGType: val.Members[1].Type, |
| ErrType: c.compileUnionMember(val.Members[1]).Type, |
| Size: val.TypeShapeV1.InlineSize, |
| Alignment: val.TypeShapeV1.Alignment, |
| } |
| |
| OkArm := val.Members[0] |
| ci := c.compileCamelCompoundIdentifier(OkArm.Type.Identifier) |
| |
| // always a struct on the Ok arms in Results |
| for _, v := range root.Structs { |
| if v.Name == ci { |
| for _, m := range v.Members { |
| wrapperName, hasHandleMetadata := c.compileHandleMetadataWrapper(&m.OGType) |
| r.Ok = append(r.Ok, ResultOkEntry{ |
| OGType: m.OGType, |
| Type: m.Type, |
| HasHandleMetadata: hasHandleMetadata, |
| HandleWrapperName: wrapperName, |
| }) |
| } |
| } |
| } |
| |
| c.results[r.ECI] = r |
| |
| return r |
| } |
| |
| func (c *compiler) compileTable(table fidl.Table) Table { |
| var members []TableMember |
| for _, member := range table.SortedMembersNoReserved() { |
| hi := c.fieldHandleInformation(&member.Type) |
| members = append(members, TableMember{ |
| Attributes: member.Attributes, |
| OGType: member.Type, |
| Type: c.compileType(member.Type, false).Decl, |
| Name: compileSnakeIdentifier(member.Name), |
| Ordinal: member.Ordinal, |
| HasHandleMetadata: hi.hasHandleMetadata, |
| HandleSubtype: hi.fullObjectType, |
| HandleRights: hi.fullRights, |
| }) |
| } |
| return Table{ |
| Table: table, |
| ECI: table.Name, |
| Name: c.compileCamelCompoundIdentifier(table.Name), |
| Members: members, |
| } |
| } |
| |
| type derives uint16 |
| |
| const ( |
| derivesDebug derives = 1 << iota |
| derivesCopy |
| derivesClone |
| derivesEq |
| derivesPartialEq |
| derivesOrd |
| derivesPartialOrd |
| derivesHash |
| derivesAsBytes |
| derivesFromBytes |
| derivesAll derives = (1 << iota) - 1 |
| |
| derivesMinimal derives = derivesDebug | derivesPartialEq |
| derivesMinimalNonResource derives = derivesMinimal | derivesClone |
| derivesAllButZerocopy derives = derivesAll & ^derivesAsBytes & ^derivesFromBytes |
| ) |
| |
| // note: keep this list in the same order as the derives definitions |
| var derivesNames = []string{ |
| // [START derived_traits] |
| "Debug", |
| "Copy", |
| "Clone", |
| "Eq", |
| "PartialEq", |
| "Ord", |
| "PartialOrd", |
| "Hash", |
| "zerocopy::AsBytes", |
| "zerocopy::FromBytes", |
| // [END derived_traits] |
| } |
| |
| func (v derives) and(others derives) derives { |
| return v & others |
| } |
| |
| func (v derives) remove(others ...derives) derives { |
| result := v |
| for _, other := range others { |
| result &= ^other |
| } |
| return result |
| } |
| |
| func (v derives) andUnknown() derives { |
| return v.and(derivesMinimal) |
| } |
| |
| func (v derives) andUnknownNonResource() derives { |
| return v.and(derivesMinimalNonResource) |
| } |
| |
| func (v derives) contains(other derives) bool { |
| return (v & other) != 0 |
| } |
| |
| func (v derives) String() string { |
| var parts []string |
| for i, bit := 0, derives(1); bit&derivesAll != 0; i, bit = i+1, bit<<1 { |
| if v.contains(bit) { |
| parts = append(parts, derivesNames[i]) |
| } |
| } |
| if len(parts) == 0 { |
| return "" |
| } |
| return fmt.Sprintf("#[derive(%s)]", strings.Join(parts, ", ")) |
| } |
| |
| // The status of derive calculation for a particular type. |
| type deriveStatus struct { |
| // recursing indicates whether or not we've passed through this type already |
| // on a recursive descent. This is used to prevent unbounded recursion on |
| // mutually-recursive types. |
| recursing bool |
| // complete indicates whether or not the derive for the given type has |
| // already been successfully calculated and stored in the IR. |
| complete bool |
| } |
| |
| // The state of the current calculation of derives. |
| type derivesCompiler struct { |
| *compiler |
| topMostCall bool |
| didShortCircuitOnRecursion bool |
| statuses map[EncodedCompoundIdentifier]deriveStatus |
| root *Root |
| } |
| |
| // [START fill_derives] |
| // Calculates what traits should be derived for each output type, |
| // filling in all `*derives` in the IR. |
| func (c *compiler) fillDerives(ir *Root) { |
| // [END fill_derives] |
| dc := &derivesCompiler{ |
| compiler: c, |
| topMostCall: true, |
| didShortCircuitOnRecursion: false, |
| statuses: make(map[EncodedCompoundIdentifier]deriveStatus), |
| root: ir, |
| } |
| |
| // Bits and enums always derive all traits |
| for _, v := range ir.Protocols { |
| dc.fillDerivesForECI(v.ECI) |
| } |
| for _, v := range ir.Structs { |
| dc.fillDerivesForECI(v.ECI) |
| } |
| for _, v := range ir.Unions { |
| dc.fillDerivesForECI(v.ECI) |
| } |
| for _, v := range ir.Results { |
| dc.fillDerivesForECI(v.ECI) |
| } |
| for _, v := range ir.Tables { |
| dc.fillDerivesForECI(v.ECI) |
| } |
| } |
| |
| func (dc *derivesCompiler) fillDerivesForECI(eci EncodedCompoundIdentifier) derives { |
| declInfo, ok := dc.decls[eci] |
| if !ok { |
| panic(fmt.Sprintf("declaration not found: %v", eci)) |
| } |
| |
| // TODO(fxbug.dev/61760): Make external type information available here. |
| // Currently, we conservatively assume external structs/tables/unions only |
| // derive the minimal set of traits, plus Clone for value types (not having |
| // Clone is especially annoying, so we put resourceness of external types |
| // into the IR as a stopgap solution). |
| if dc.inExternalLibrary(fidl.ParseCompoundIdentifier(eci)) { |
| switch declInfo.Type { |
| case fidl.StructDeclType, fidl.TableDeclType, fidl.UnionDeclType: |
| if declInfo.IsValueType() { |
| return derivesMinimalNonResource |
| } |
| return derivesMinimal |
| } |
| } |
| |
| // Return early for declaration types that do not require recursion. |
| switch declInfo.Type { |
| case fidl.ConstDeclType: |
| panic("const decl should never have derives") |
| case fidl.BitsDeclType, fidl.EnumDeclType: |
| // Enums and bits are always simple, non-float primitives which |
| // implement all derivable traits except zerocopy. |
| return derivesAllButZerocopy |
| case fidl.ProtocolDeclType: |
| // When a protocol is used as a type, it means a ClientEnd in Rust. |
| return derivesAllButZerocopy.remove(derivesCopy, derivesClone) |
| } |
| |
| topMostCall := dc.topMostCall |
| if dc.topMostCall { |
| dc.topMostCall = false |
| } |
| deriveStatus := dc.statuses[eci] |
| if deriveStatus.recursing { |
| // If we've already seen the current type while recursing, |
| // the algorithm has already explored all of the other fields contained |
| // within the cycle, so we can return true for all derives, and the |
| // correct results will be bubbled up. |
| dc.didShortCircuitOnRecursion = true |
| return derivesAll |
| } |
| deriveStatus.recursing = true |
| dc.statuses[eci] = deriveStatus |
| |
| var derivesOut derives |
| typeSwitch: |
| switch declInfo.Type { |
| case fidl.StructDeclType: |
| st := dc.root.findStruct(eci) |
| if st == nil { |
| panic(fmt.Sprintf("struct not found: %v", eci)) |
| } |
| // Check if the derives have already been calculated |
| if deriveStatus.complete { |
| derivesOut = st.Derives |
| break typeSwitch |
| } |
| derivesOut = derivesAll |
| for _, member := range st.Members { |
| derivesOut = derivesOut.and(dc.fillDerivesForType(member.OGType)) |
| } |
| if st.HasPadding { |
| derivesOut = derivesOut.remove(derivesAsBytes, derivesFromBytes) |
| } |
| st.Derives = derivesOut |
| case fidl.TableDeclType: |
| table := dc.root.findTable(eci) |
| if table == nil { |
| panic(fmt.Sprintf("table not found: %v", eci)) |
| } |
| // Check if the derives have already been calculated |
| if deriveStatus.complete { |
| derivesOut = table.Derives |
| break typeSwitch |
| } |
| derivesOut = derivesAllButZerocopy |
| for _, member := range table.Members { |
| derivesOut = derivesOut.and(dc.fillDerivesForType(member.OGType)) |
| } |
| if table.IsResourceType() { |
| derivesOut = derivesOut.andUnknown() |
| } else { |
| derivesOut = derivesOut.andUnknownNonResource() |
| } |
| table.Derives = derivesOut |
| case fidl.UnionDeclType: |
| union := dc.root.findUnion(eci) |
| var result *Result |
| if union == nil { |
| result = dc.root.findResult(eci) |
| } |
| if union == nil && result == nil { |
| panic(fmt.Sprintf("union not found: %v", eci)) |
| } |
| if union != nil { |
| // It's a union, not a result |
| // Check if the derives have already been calculated |
| if deriveStatus.complete { |
| derivesOut = union.Derives |
| break typeSwitch |
| } |
| derivesOut = derivesAllButZerocopy |
| for _, member := range union.Members { |
| derivesOut = derivesOut.and(dc.fillDerivesForType(member.OGType)) |
| } |
| if union.IsFlexible() { |
| if union.IsResourceType() { |
| derivesOut = derivesOut.andUnknown() |
| } else { |
| derivesOut = derivesOut.andUnknownNonResource() |
| } |
| } |
| union.Derives = derivesOut |
| } else { |
| // It's a Result, not a union |
| // Check if the derives have already been calculated |
| if deriveStatus.complete { |
| derivesOut = result.Derives |
| break typeSwitch |
| } |
| derivesOut = derivesAllButZerocopy |
| for _, ok := range result.Ok { |
| derivesOut = derivesOut.and(dc.fillDerivesForType(ok.OGType)) |
| } |
| derivesOut = derivesOut.and(dc.fillDerivesForType(result.ErrOGType)) |
| result.Derives = derivesOut |
| } |
| default: |
| panic(fmt.Sprintf("unknown declaration type: %v", declInfo.Type)) |
| } |
| if topMostCall || !dc.didShortCircuitOnRecursion { |
| // Our completed result is only valid if it's either at top-level |
| // (ensuring we've fully visited all child types in the recursive |
| // substructure at least once) or if we performed no recursion-based |
| // short-circuiting, in which case results are correct and absolute. |
| // |
| // Note that non-topMostCalls are invalid if we did short-circuit |
| // on recursion, because we might have short-circuited just beneath |
| // a type without having explored all of its children at least once |
| // beneath it. |
| // |
| // For example, imagine A -> B -> C -> A. |
| // When we start on A, we go to B, then go to C, then go to A, at which |
| // point we short-circuit. The intermediate result for C is invalid |
| // for anything except the computation of A and B above, as it does |
| // not take into account that C contains A and B, only that it contains |
| // its top-level fields (other than A). In order to get a correct |
| // idea of the shape of C, we have to start with C, following through |
| // every branch until we find C again. |
| deriveStatus.complete = true |
| } |
| if topMostCall { |
| // Reset intermediate state |
| dc.topMostCall = true |
| dc.didShortCircuitOnRecursion = false |
| } |
| deriveStatus.recursing = false |
| dc.statuses[eci] = deriveStatus |
| return derivesOut |
| } |
| |
| func (dc *derivesCompiler) fillDerivesForType(ogType fidl.Type) derives { |
| switch ogType.Kind { |
| case fidl.ArrayType: |
| return dc.fillDerivesForType(*ogType.ElementType) |
| case fidl.VectorType: |
| return derivesAllButZerocopy.remove(derivesCopy).and(dc.fillDerivesForType(*ogType.ElementType)) |
| case fidl.StringType: |
| return derivesAllButZerocopy.remove(derivesCopy) |
| case fidl.HandleType, fidl.RequestType: |
| return derivesAllButZerocopy.remove(derivesCopy, derivesClone) |
| case fidl.PrimitiveType: |
| switch ogType.PrimitiveSubtype { |
| case fidl.Bool: |
| return derivesAllButZerocopy |
| case fidl.Int8, fidl.Int16, fidl.Int32, fidl.Int64: |
| return derivesAll |
| case fidl.Uint8, fidl.Uint16, fidl.Uint32, fidl.Uint64: |
| return derivesAll |
| case fidl.Float32, fidl.Float64: |
| // Floats don't have a total ordering due to NAN and its multiple representations. |
| return derivesAllButZerocopy.remove(derivesEq, derivesOrd, derivesHash) |
| default: |
| panic(fmt.Sprintf("unknown primitive type: %v", ogType.PrimitiveSubtype)) |
| } |
| case fidl.IdentifierType: |
| internalTypeDerives := dc.fillDerivesForECI(ogType.Identifier) |
| if ogType.Nullable { |
| // A nullable struct/union gets put in Option<Box<...>> and so |
| // cannot derive Copy, AsBytes, or FromBytes. Bits, enums, and |
| // tables are never nullable. The only other possibility for an |
| // identifier type is a protocol, which cannot derive these either. |
| return internalTypeDerives.remove(derivesCopy, derivesAsBytes, derivesFromBytes) |
| } |
| return internalTypeDerives |
| default: |
| panic(fmt.Sprintf("unknown type kind: %v", ogType.Kind)) |
| } |
| } |
| |
| func Compile(r fidl.Root) Root { |
| r = r.ForBindings("rust") |
| root := Root{} |
| thisLibParsed := fidl.ParseLibraryName(r.Name) |
| c := compiler{ |
| r.DeclsWithDependencies(), |
| thisLibParsed, |
| map[string]struct{}{}, |
| map[fidl.EncodedCompoundIdentifier]fidl.Struct{}, |
| map[fidl.EncodedCompoundIdentifier]fidl.Struct{}, |
| map[fidl.EncodedCompoundIdentifier]Result{}, |
| map[string]HandleMetadataWrapper{}, |
| } |
| |
| for _, s := range r.Structs { |
| c.structs[s.Name] = s |
| } |
| |
| for _, v := range r.Bits { |
| root.Bits = append(root.Bits, c.compileBits(v)) |
| } |
| |
| for _, v := range r.Consts { |
| root.Consts = append(root.Consts, c.compileConst(v)) |
| } |
| |
| for _, v := range r.Enums { |
| root.Enums = append(root.Enums, c.compileEnum(v)) |
| } |
| |
| for _, v := range r.Services { |
| root.Services = append(root.Services, c.compileService(v)) |
| } |
| |
| for _, v := range r.Structs { |
| if v.Anonymous { |
| c.requestResponsePayload[v.Name] = v |
| } else { |
| root.Structs = append(root.Structs, c.compileStruct(v)) |
| } |
| } |
| |
| for _, v := range r.Unions { |
| if v.Attributes.HasAttribute("Result") { |
| root.Results = append(root.Results, c.compileResultFromUnion(v, root)) |
| } else { |
| root.Unions = append(root.Unions, c.compileUnion(v)) |
| } |
| } |
| |
| for _, v := range r.Tables { |
| root.Tables = append(root.Tables, c.compileTable(v)) |
| } |
| |
| for _, v := range r.Protocols { |
| root.Protocols = append(root.Protocols, c.compileProtocol(v)) |
| } |
| |
| // Sort by wrapper name for deterministic output order. |
| handleMetadataWrapperNames := make([]string, 0, len(c.handleMetadataWrappers)) |
| for name := range c.handleMetadataWrappers { |
| handleMetadataWrapperNames = append(handleMetadataWrapperNames, name) |
| } |
| sort.Strings(handleMetadataWrapperNames) |
| for _, name := range handleMetadataWrapperNames { |
| root.HandleMetadataWrappers = append(root.HandleMetadataWrappers, c.handleMetadataWrappers[name]) |
| } |
| |
| c.fillDerives(&root) |
| |
| thisLibCompiled := compileLibraryName(thisLibParsed) |
| |
| // Sort the extern crates to make sure the generated file is |
| // consistent across builds. |
| var externCrates []string |
| for k := range c.externCrates { |
| externCrates = append(externCrates, k) |
| } |
| sort.Strings(externCrates) |
| |
| for _, k := range externCrates { |
| if k != thisLibCompiled { |
| root.ExternCrates = append(root.ExternCrates, k) |
| } |
| } |
| |
| return root |
| } |