| // 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 ir |
| |
| import ( |
| "fmt" |
| "log" |
| "math" |
| "sort" |
| "strconv" |
| "strings" |
| |
| "fidl/compiler/backend/common" |
| "fidl/compiler/backend/types" |
| ) |
| |
| const ( |
| ProxySuffix = "Interface" |
| StubSuffix = "Stub" |
| EventProxySuffix = "EventProxy" |
| ServiceSuffix = "Service" |
| TransitionalBaseSuffix = "TransitionalBase" |
| ServiceNameSuffix = "Name" |
| RequestSuffix = "InterfaceRequest" |
| TagSuffix = "Tag" |
| |
| MessageHeaderSize = 16 |
| |
| SyscallZxPackage = "syscall/zx" |
| SyscallZxAlias = "_zx" |
| |
| BindingsPackage = "syscall/zx/fidl" |
| BindingsAlias = "_bindings" |
| ) |
| |
| // Type represents a golang type. |
| type Type string |
| |
| // Const represents the idiomatic representation of a constant in golang. |
| type Const struct { |
| types.Attributes |
| |
| // Name is the name of the constant. |
| Name string |
| |
| // Type is the constant's type. |
| Type Type |
| |
| // Value is the constant's value. |
| Value string |
| } |
| |
| // Enum represents the idiomatic representation of an enum in golang. |
| // |
| // That is, something like: |
| // type MyEnum int32 |
| // const ( |
| // MyEnumMember1 MyEnum = 1 |
| // MyEnumMember2 = 2 |
| // ... |
| // ) |
| type Enum struct { |
| types.Attributes |
| |
| // Name is the name of the enum type alias. |
| Name string |
| |
| // Type is the underlying primitive type for the enum. |
| Type Type |
| |
| // Members is the list of enum variants that are a part of this enum. |
| // The values of the Members must not overlap. |
| Members []EnumMember |
| } |
| |
| // EnumMember represents a single enum variant. See Enum for more details. |
| type EnumMember struct { |
| types.Attributes |
| |
| // Name is the name of the enum variant without any prefix. |
| Name string |
| |
| // Value is the raw value of the enum variant, represented as a string |
| // to support many types. |
| Value string |
| } |
| |
| // Struct represents a golang struct. |
| type Struct struct { |
| types.Attributes |
| |
| // Name is the name of the golang struct. |
| Name string |
| |
| // Members is a list of the golang struct members. |
| Members []StructMember |
| |
| // Size is the FIDL-encoded size of the struct. |
| Size int |
| |
| // Alignment is the alignment of the FIDL-encoded struct. |
| Alignment int |
| } |
| |
| // TODO(pascallouis): document, see `readTag` function in |
| // https://fuchsia.googlesource.com/third_party/go/+/master/src/syscall/zx/fidl/encoding_new.go#211 |
| type tagNew struct { |
| reverseOfBounds []int |
| } |
| |
| func (t tagNew) String() string { |
| var ( |
| elems []string |
| allEmpty = true |
| ) |
| for i := len(t.reverseOfBounds) - 1; 0 <= i; i-- { |
| bound := t.reverseOfBounds[i] |
| if bound == math.MaxInt32 { |
| elems = append(elems, "") |
| } else { |
| elems = append(elems, strconv.Itoa(bound)) |
| allEmpty = false |
| } |
| } |
| if allEmpty { |
| return "" |
| } |
| return fmt.Sprintf(`fidl2:"%s"`, strings.Join(elems, ",")) |
| } |
| |
| // Tag loosely represents a golang struct member tag for maximum elements e.g. |
| // `fidl:"3,4,5"`. For a type like vector<vector<int>:3>:4, the tag would |
| // look like `fidl:"3,4"`, such that the commas separate nesting. Note that if |
| // a nested type that doesn't specify a max size is used, it is encoded as the |
| // empty string. For example, vector<vector<vector<int>:3>>:5 would have tag |
| // `fidl:"3,,5"`. Note that this makes a maximum length of 0 distinct. |
| // Fundamentally, this Tag exists to provide type metadata for nested FIDL2 |
| // container types to the encoder/decoder in the bindings. |
| // |
| // Note that arrays are not included because their maximum sizes are encoded in |
| // the golang type system. Because of this, FIDL types such as |
| // vector<array<int>:10>:10 will have a tag that looks like `fidl:"10"`. |
| // |
| // Rather than representing handle nullability using a pointer indirection, we |
| // include it as part of the struct tag. For example, vector<handle?>:2 will |
| // have tag `fidl:"*,2"`. |
| // |
| // Lastly, when a field is on table, its ordinal is stored in `Ordinal`, and |
| // appears in the struct tag as the rightmost element. |
| type Tag struct { |
| // MaxElems is the maximum number of elements a type is annotated with. |
| MaxElems []*int |
| |
| // Nullable is whether the innermost type is nullable. This only applies |
| // for handle types. |
| Nullable bool |
| |
| // Ordinal is the table ordinal of the field this type is annotated with. |
| Ordinal int |
| } |
| |
| // String generates a string representation for the tag. |
| func (t *Tag) String() string { |
| var elemsTag []string |
| anyData := false |
| if t.Nullable { |
| elemsTag = append(elemsTag, "*") |
| anyData = true |
| } |
| for _, elems := range t.MaxElems { |
| if elems == nil { |
| elemsTag = append(elemsTag, "") |
| continue |
| } |
| anyData = true |
| elemsTag = append(elemsTag, strconv.Itoa(*elems)) |
| } |
| if 0 < t.Ordinal { |
| anyData = true |
| elemsTag = append(elemsTag, strconv.Itoa(t.Ordinal)) |
| } |
| if !anyData { |
| return "" |
| } |
| return fmt.Sprintf(`fidl:"%s"`, strings.Join(elemsTag, ",")) |
| } |
| |
| func tagsfmt(t Tag, t2 tagNew) string { |
| var tags []string |
| if t_str := t.String(); len(t_str) != 0 { |
| tags = append(tags, t_str) |
| } |
| if t2_str := t2.String(); len(t2_str) != 0 { |
| tags = append(tags, t2_str) |
| } |
| if len(tags) == 0 { |
| return "" |
| } |
| return fmt.Sprintf("`%s`", strings.Join(tags, " ")) |
| } |
| |
| // StructMember represents the member of a golang struct. |
| type StructMember struct { |
| types.Attributes |
| |
| // Name is the name of the golang struct member. |
| Name string |
| |
| // PrivateName is the unexported version of the name of the struct member. |
| PrivateName string |
| |
| // Type is the type of the golang struct member. |
| Type Type |
| |
| // Tags are the golang struct member tag which holds additional metadata |
| // about the struct field. |
| Tags string |
| } |
| |
| // Union represents a FIDL union as a golang struct. |
| type Union struct { |
| types.Attributes |
| |
| // Name is the name of the FIDL union as a golang struct. |
| Name string |
| |
| // TagName is the name of the golang enum type for the tag of the FIDL enum. |
| TagName string |
| |
| // Members is a list of FIDL union members represented as golang struct members. |
| Members []UnionMember |
| |
| // Size is the size of the FIDL union on the wire in bytes. |
| Size int |
| |
| // Alignment is the alignment factor of the FIDL union on the wire in bytes. |
| Alignment int |
| } |
| |
| // UnionMember represents a FIDL union member as a golang struct member. |
| type UnionMember struct { |
| types.Attributes |
| |
| // Name is the exported name of the FIDL union member. |
| Name string |
| |
| // PrivateName is the unexported name of the FIDL union member. |
| PrivateName string |
| |
| // Type is the golang type of the union member. |
| Type Type |
| |
| // Tag are the golang struct member tag which holds additional metadata |
| // about the union member. |
| Tags string |
| } |
| |
| // Table represents a FIDL table as a golang struct. |
| type Table struct { |
| types.Attributes |
| Name string |
| Members []TableMember |
| Size int |
| Alignment int |
| } |
| |
| // TableMember represents a FIDL table member as two golang struct members, one |
| // for the member itself, and one to indicate presence or absence. |
| type TableMember struct { |
| types.Attributes |
| |
| // DataField is the exported name of the FIDL table member. |
| DataField string |
| |
| // PrivateDataField is an unexported name of the FIDL table member, used as |
| // argument. |
| PrivateDataField string |
| |
| // PresenceField is the exported name of boolean indicating presence of |
| // the FIDL table member. |
| PresenceField string |
| |
| // Setter is the exported name of the FIDL table member setter. |
| Setter string |
| |
| // Getter is the exported name of the FIDL table member getter. |
| Getter string |
| |
| // Clearer is the exported name of the FIDL table member clearer. |
| Clearer string |
| |
| // Haser is the exported name of the presence checker of the FIDL table |
| // member. |
| Haser string |
| |
| // Type is the golang type of the table member. |
| Type Type |
| |
| // Tag are the golang struct member tag which holds additional metadata |
| // about the table member. |
| Tags string |
| } |
| |
| // Interface represents a FIDL interface in terms of golang structures. |
| type Interface struct { |
| types.Attributes |
| |
| // Name is the Golang name of the interface. |
| Name string |
| |
| // ProxyName is the name of the proxy type for this FIDL interface. |
| ProxyName string |
| |
| // ProxyType is concrete type of proxy used for this FIDL interface. |
| ProxyType string |
| |
| // StubName is the name of the stub type for this FIDL interface. |
| StubName string |
| |
| // EventProxyName is the name of the event proxy type for this FIDL interface. |
| EventProxyName string |
| |
| // TransitionalBaseName is the name of the base implementation for transitional methods |
| // for this FIDL interface. |
| TransitionalBaseName string |
| |
| // RequestName is the name of the interface request type for this FIDL interface. |
| RequestName string |
| |
| // ServerName is the name of the server type for this FIDL interface. |
| ServerName string |
| |
| // ServiceNameString is the string service name for this FIDL interface. |
| ServiceNameString string |
| |
| // ServiceNameConstant is the name of the service name constant for this FIDL interface. |
| ServiceNameConstant string |
| |
| // Methods is a list of methods for this FIDL interface. |
| Methods []Method |
| } |
| |
| // Method represents a method of a FIDL interface in terms of golang structures. |
| type Method struct { |
| types.Attributes |
| |
| // Ordinal is the ordinal for this method. |
| Ordinal types.Ordinal |
| |
| // OrdinalName is the name of the ordinal for this method, including the interface |
| // name as a prefix. |
| OrdinalName string |
| |
| // GenOrdinal is the generated ordinal for this method. |
| GenOrdinal types.Ordinal |
| |
| // GenOrdinalName is the name of the generated ordinal for this method, |
| // including the interface name as a prefix. |
| GenOrdinalName string |
| |
| // Name is the name of the Method, including the interface name as a prefix. |
| Name string |
| |
| // Request represents a goland struct containing the request parameters. |
| Request *Struct |
| |
| // Response represents an optional golang struct containing the response parameters. |
| Response *Struct |
| |
| // EventExpectName is the name of the method for the client-side event proxy. |
| // Only relevant if the method is an event. |
| EventExpectName string |
| |
| // IsEvent is set to true if the method is an event. In this case, Response will always be |
| // non-nil while Request will always be nil. EventExpectName will also be non-empty. |
| IsEvent bool |
| |
| // IsTransitional is set to true if the method has the Transitional attribute. |
| IsTransitional bool |
| } |
| |
| // Library represents a FIDL library as a golang package. |
| type Library struct { |
| // Alias is the alias of the golang package referring to a FIDL library. |
| Alias string |
| |
| // Path is the path to the golang package referring to a FIDL library. |
| Path string |
| } |
| |
| // Root is the root of the golang backend IR structure. |
| // |
| // The golang backend IR structure is loosely modeled after an abstract syntax |
| // tree, and is used to generate golang code from templates. |
| type Root struct { |
| // Name is the name of the library. |
| Name string |
| |
| // PackageName is the name of the golang package as other Go programs would |
| // import it. |
| PackageName string |
| |
| // Consts represents a list of FIDL constants represented as Go constants. |
| Consts []Const |
| |
| // Enums represents a list of FIDL enums represented as Go enums. |
| Enums []Enum |
| |
| // Structs represents the list of FIDL structs represented as Go structs. |
| Structs []Struct |
| |
| // Unions represents the list of FIDL unions represented as Go structs. |
| Unions []Union |
| |
| // Table represents the list of FIDL tables represented as Go structs. |
| Tables []Table |
| |
| // Interfaces represents the list of FIDL interfaces represented as Go types. |
| Interfaces []Interface |
| |
| // Libraries represents the set of library dependencies for this FIDL library. |
| Libraries []Library |
| } |
| |
| // compiler contains the state necessary for recursive compilation. |
| type compiler struct { |
| // decls contains all top-level declarations for the FIDL source. |
| decls types.DeclMap |
| |
| // library is the identifier for the current library. |
| library types.LibraryIdentifier |
| |
| // libraryDeps is a mapping of compiled library identifiers (go package paths) |
| // to aliases, which is used to resolve references to types outside of the current |
| // FIDL library. |
| libraryDeps map[string]string |
| |
| // usedLibraryDeps is identical to libraryDeps except it is built up as references |
| // are made into libraryDeps. Thus, after Compile is run, it contains the subset of |
| // libraryDeps that's actually being used. The purpose is to figure out which |
| // dependencies need to be imported. |
| usedLibraryDeps map[string]string |
| } |
| |
| // Contains the full set of reserved golang keywords, in addition to a set of |
| // primitive named types. Note that this will result in potentially unnecessary |
| // identifier renaming, but this isn't a big deal for generated code. |
| var reservedWords = map[string]bool{ |
| // Officially reserved keywords. |
| "break": true, |
| "case": true, |
| "chan": true, |
| "const": true, |
| "continue": true, |
| "default": true, |
| "defer": true, |
| "else": true, |
| "fallthrough": true, |
| "for": true, |
| "func": true, |
| "go": true, |
| "goto": true, |
| "if": true, |
| "int": true, |
| "interface": true, |
| "map": true, |
| "package": true, |
| "range": true, |
| "return": true, |
| "select": true, |
| "struct": true, |
| "switch": true, |
| "try": true, |
| "type": true, |
| "var": true, |
| |
| // Reserved types. |
| "bool": true, |
| "byte": true, |
| "int8": true, |
| "int16": true, |
| "int32": true, |
| "int64": true, |
| "rune": true, |
| "string": true, |
| "uint8": true, |
| "uint16": true, |
| "uint32": true, |
| "uint64": true, |
| |
| // Reserved values. |
| "false": true, |
| "true": true, |
| } |
| |
| var primitiveTypes = map[types.PrimitiveSubtype]string{ |
| types.Bool: "bool", |
| types.Int8: "int8", |
| types.Int16: "int16", |
| types.Int32: "int32", |
| types.Int64: "int64", |
| types.Uint8: "uint8", |
| types.Uint16: "uint16", |
| types.Uint32: "uint32", |
| types.Uint64: "uint64", |
| types.Float32: "float32", |
| types.Float64: "float64", |
| } |
| |
| var handleTypes = map[types.HandleSubtype]string{ |
| // TODO(mknyszek): Add support here for process, thread, job, resource, |
| // interrupt, eventpair, fifo, guest, and time once these are actually |
| // supported in the Go runtime. |
| types.Handle: "_zx.Handle", |
| types.Vmo: "_zx.VMO", |
| types.Channel: "_zx.Channel", |
| types.Event: "_zx.Event", |
| types.Port: "_zx.Port", |
| types.Log: "_zx.Log", |
| types.Socket: "_zx.Socket", |
| types.Vmar: "_zx.VMAR", |
| } |
| |
| func exportIdentifier(name types.EncodedCompoundIdentifier) types.CompoundIdentifier { |
| ci := types.ParseCompoundIdentifier(name) |
| ci.Name = types.Identifier(common.ToUpperCamelCase(string(ci.Name))) |
| return ci |
| } |
| |
| func isReservedWord(str string) bool { |
| _, ok := reservedWords[str] |
| return ok |
| } |
| |
| func changeIfReserved(val types.Identifier, ext string) string { |
| // TODO(mknyszek): Detect name collision within a scope as a result of transforming. |
| str := string(val) + ext |
| if isReservedWord(str) { |
| return str + "_" |
| } |
| return str |
| } |
| |
| func (c *compiler) inExternalLibrary(ci types.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 (_ *compiler) compileIdentifier(id types.Identifier, export bool, ext string) string { |
| str := string(id) |
| if export { |
| str = common.ToUpperCamelCase(str) |
| } else { |
| str = common.ToLowerCamelCase(str) |
| } |
| return changeIfReserved(types.Identifier(str), ext) |
| } |
| |
| func (c *compiler) compileCompoundIdentifier(eci types.EncodedCompoundIdentifier, ext string) string { |
| ci := exportIdentifier(eci) |
| pkg := compileLibraryIdentifier(ci.Library) |
| strs := []string{} |
| if c.inExternalLibrary(ci) { |
| pkgAlias := c.libraryDeps[pkg] |
| strs = append(strs, pkgAlias) |
| c.usedLibraryDeps[pkg] = pkgAlias |
| } |
| strs = append(strs, changeIfReserved(ci.Name, ext)) |
| return strings.Join(strs, ".") |
| } |
| |
| func (_ *compiler) compileLiteral(val types.Literal) string { |
| switch val.Kind { |
| // TODO(mknyszek): Support string and default literals. |
| case types.NumericLiteral: |
| return val.Value |
| case types.TrueLiteral: |
| return "true" |
| case types.FalseLiteral: |
| return "false" |
| default: |
| log.Fatal("Unknown literal kind: ", val.Kind) |
| return "" |
| } |
| } |
| |
| func (c *compiler) compileConstant(val types.Constant) string { |
| switch val.Kind { |
| // TODO(mknyszek): Support identifiers. |
| case types.LiteralConstant: |
| return c.compileLiteral(val.Literal) |
| default: |
| log.Fatal("Unknown constant kind: ", val.Kind) |
| return "" |
| } |
| } |
| |
| func (c *compiler) compilePrimitiveSubtype(val types.PrimitiveSubtype) Type { |
| t, ok := primitiveTypes[val] |
| if !ok { |
| log.Fatal("Unknown primitive type: ", val) |
| } |
| return Type(t) |
| } |
| |
| func (c *compiler) compileType(val types.Type) (r Type, t Tag, t2 tagNew) { |
| switch val.Kind { |
| case types.ArrayType: |
| e, et, et2 := c.compileType(*val.ElementType) |
| r = Type(fmt.Sprintf("[%s]%s", strconv.Itoa(*val.ElementCount), e)) |
| t = et |
| t2 = et2 |
| case types.StringType: |
| t.MaxElems = append(t.MaxElems, val.ElementCount) |
| if val.ElementCount == nil { |
| t2.reverseOfBounds = append(t2.reverseOfBounds, math.MaxInt32) |
| } else { |
| t2.reverseOfBounds = append(t2.reverseOfBounds, *val.ElementCount) |
| } |
| if val.Nullable { |
| r = Type("*string") |
| } else { |
| r = Type("string") |
| } |
| case types.HandleType: |
| // Note here that we require the SyscallZx package. |
| c.usedLibraryDeps[SyscallZxPackage] = SyscallZxAlias |
| e, ok := handleTypes[val.HandleSubtype] |
| if !ok { |
| // Fall back onto a generic handle if we don't support that particular |
| // handle subtype. |
| e = handleTypes[types.Handle] |
| } |
| var nullability int |
| if val.Nullable { |
| t.Nullable = true |
| nullability = 1 |
| } |
| t2.reverseOfBounds = append(t2.reverseOfBounds, nullability) |
| r = Type(e) |
| case types.RequestType: |
| e := c.compileCompoundIdentifier(val.RequestSubtype, RequestSuffix) |
| var nullability int |
| if val.Nullable { |
| t.Nullable = true |
| nullability = 1 |
| } |
| t2.reverseOfBounds = append(t2.reverseOfBounds, nullability) |
| r = Type(e) |
| case types.VectorType: |
| e, et, et2 := c.compileType(*val.ElementType) |
| et.MaxElems = append(et.MaxElems, val.ElementCount) |
| if val.ElementCount == nil { |
| et2.reverseOfBounds = append(et2.reverseOfBounds, math.MaxInt32) |
| } else { |
| et2.reverseOfBounds = append(et2.reverseOfBounds, *val.ElementCount) |
| } |
| if val.Nullable { |
| r = Type(fmt.Sprintf("*[]%s", e)) |
| } else { |
| r = Type(fmt.Sprintf("[]%s", e)) |
| } |
| t = et |
| t2 = et2 |
| case types.PrimitiveType: |
| r = c.compilePrimitiveSubtype(val.PrimitiveSubtype) |
| case types.IdentifierType: |
| e := c.compileCompoundIdentifier(val.Identifier, "") |
| declType, ok := c.decls[val.Identifier] |
| if !ok { |
| log.Fatal("Unknown identifier: ", val.Identifier) |
| } |
| switch declType { |
| case types.EnumDeclType: |
| r = Type(e) |
| case types.InterfaceDeclType: |
| if val.Nullable { |
| t.Nullable = true |
| } |
| r = Type(e + ProxySuffix) |
| case types.StructDeclType: |
| fallthrough |
| case types.UnionDeclType: |
| fallthrough |
| case types.TableDeclType: |
| if val.Nullable { |
| r = Type("*" + e) |
| } else { |
| r = Type(e) |
| } |
| default: |
| log.Fatal("Unknown declaration type: ", declType) |
| } |
| default: |
| log.Fatal("Unknown type kind: ", val.Kind) |
| } |
| return |
| } |
| |
| func (c *compiler) compileConst(val types.Const) Const { |
| // It's OK to ignore the tag because this type is guaranteed by the frontend |
| // to be either an enum, a primitive, or a string. |
| t, _, _ := c.compileType(val.Type) |
| return Const{ |
| Attributes: val.Attributes, |
| Name: c.compileCompoundIdentifier(val.Name, ""), |
| Type: t, |
| Value: c.compileConstant(val.Value), |
| } |
| } |
| |
| func (c *compiler) compileEnumMember(val types.EnumMember) EnumMember { |
| return EnumMember{ |
| Attributes: val.Attributes, |
| Name: c.compileIdentifier(val.Name, true, ""), |
| Value: c.compileConstant(val.Value), |
| } |
| } |
| |
| func (c *compiler) compileEnum(val types.Enum) Enum { |
| r := Enum{ |
| Attributes: val.Attributes, |
| Name: c.compileCompoundIdentifier(val.Name, ""), |
| Type: c.compilePrimitiveSubtype(val.Type), |
| } |
| for _, v := range val.Members { |
| r.Members = append(r.Members, c.compileEnumMember(v)) |
| } |
| return r |
| } |
| |
| func (c *compiler) compileStructMember(val types.StructMember) StructMember { |
| ty, tag, tag2 := c.compileType(val.Type) |
| return StructMember{ |
| Attributes: val.Attributes, |
| Type: ty, |
| Name: c.compileIdentifier(val.Name, true, ""), |
| PrivateName: c.compileIdentifier(val.Name, false, ""), |
| Tags: tagsfmt(tag, tag2), |
| } |
| } |
| |
| func (c *compiler) compileStruct(val types.Struct) Struct { |
| r := Struct{ |
| Attributes: val.Attributes, |
| Name: c.compileCompoundIdentifier(val.Name, ""), |
| Size: val.Size, |
| Alignment: val.Alignment, |
| } |
| |
| for _, v := range val.Members { |
| r.Members = append(r.Members, c.compileStructMember(v)) |
| } |
| |
| return r |
| } |
| |
| func (c *compiler) compileUnionMember(unionName string, val types.UnionMember) UnionMember { |
| ty, tag, tag2 := c.compileType(val.Type) |
| return UnionMember{ |
| Attributes: val.Attributes, |
| Type: ty, |
| Name: c.compileIdentifier(val.Name, true, ""), |
| PrivateName: c.compileIdentifier(val.Name, false, ""), |
| Tags: tagsfmt(tag, tag2), |
| } |
| } |
| |
| func (c *compiler) compileUnion(val types.Union) Union { |
| r := Union{ |
| Attributes: val.Attributes, |
| Name: c.compileCompoundIdentifier(val.Name, ""), |
| TagName: c.compileCompoundIdentifier(val.Name, TagSuffix), |
| Size: val.Size, |
| Alignment: val.Alignment, |
| } |
| for _, v := range val.Members { |
| r.Members = append(r.Members, c.compileUnionMember(r.Name, v)) |
| } |
| return r |
| } |
| |
| func (c *compiler) compileTable(val types.Table) Table { |
| var members []TableMember |
| for _, member := range val.Members { |
| if member.Reserved { |
| continue |
| } |
| var ( |
| ty, tag, tag2 = c.compileType(member.Type) |
| name = c.compileIdentifier(member.Name, true, "") |
| privateName = c.compileIdentifier(member.Name, false, "") |
| ) |
| tag.Ordinal = member.Ordinal |
| tag2.reverseOfBounds = append(tag2.reverseOfBounds, member.Ordinal) |
| members = append(members, TableMember{ |
| Attributes: member.Attributes, |
| DataField: name, |
| PrivateDataField: privateName, |
| PresenceField: name + "Present", |
| Setter: "Set" + name, |
| Getter: "Get" + name, |
| Haser: "Has" + name, |
| Clearer: "Clear" + name, |
| Type: ty, |
| Tags: tagsfmt(tag, tag2), |
| }) |
| } |
| return Table{ |
| Attributes: val.Attributes, |
| Name: c.compileCompoundIdentifier(val.Name, ""), |
| Size: val.Size, |
| Alignment: val.Alignment, |
| Members: members, |
| } |
| } |
| |
| func (c *compiler) compileParameter(p types.Parameter) StructMember { |
| ty, tag, tag2 := c.compileType(p.Type) |
| return StructMember{ |
| Type: ty, |
| Name: c.compileIdentifier(p.Name, true, ""), |
| PrivateName: c.compileIdentifier(p.Name, false, ""), |
| Tags: tagsfmt(tag, tag2), |
| } |
| } |
| |
| func (c *compiler) compileMethod(ifaceName types.EncodedCompoundIdentifier, val types.Method) Method { |
| methodName := c.compileIdentifier(val.Name, true, "") |
| r := Method{ |
| Attributes: val.Attributes, |
| Name: methodName, |
| Ordinal: val.Ordinal, |
| OrdinalName: c.compileCompoundIdentifier(ifaceName, methodName+"Ordinal"), |
| GenOrdinal: val.GenOrdinal, |
| GenOrdinalName: c.compileCompoundIdentifier(ifaceName, methodName+"GenOrdinal"), |
| EventExpectName: "Expect" + methodName, |
| IsEvent: !val.HasRequest && val.HasResponse, |
| IsTransitional: val.IsTransitional(), |
| } |
| if val.HasRequest { |
| req := Struct{ |
| Name: c.compileCompoundIdentifier(ifaceName, methodName+"Request"), |
| // We want just the size of the parameter array as a struct, not |
| // including the message header size. |
| Size: val.RequestSize - MessageHeaderSize, |
| } |
| for _, p := range val.Request { |
| req.Members = append(req.Members, c.compileParameter(p)) |
| } |
| r.Request = &req |
| } |
| if val.HasResponse { |
| resp := Struct{ |
| Name: c.compileCompoundIdentifier(ifaceName, methodName+"Response"), |
| // We want just the size of the parameter array as a struct, not |
| // including the message header size. |
| Size: val.ResponseSize - MessageHeaderSize, |
| } |
| for _, p := range val.Response { |
| resp.Members = append(resp.Members, c.compileParameter(p)) |
| } |
| r.Response = &resp |
| } |
| return r |
| } |
| |
| func (c *compiler) compileInterface(val types.Interface) Interface { |
| proxyType := "ChannelProxy" |
| if val.Attributes.GetAttribute("Transport").Value == "SocketControl" { |
| proxyType = "SocketControlProxy" |
| } |
| r := Interface{ |
| Attributes: val.Attributes, |
| Name: c.compileCompoundIdentifier(val.Name, ""), |
| TransitionalBaseName: c.compileCompoundIdentifier(val.Name, TransitionalBaseSuffix), |
| ProxyName: c.compileCompoundIdentifier(val.Name, ProxySuffix), |
| ProxyType: proxyType, |
| StubName: c.compileCompoundIdentifier(val.Name, StubSuffix), |
| RequestName: c.compileCompoundIdentifier(val.Name, RequestSuffix), |
| EventProxyName: c.compileCompoundIdentifier(val.Name, EventProxySuffix), |
| ServerName: c.compileCompoundIdentifier(val.Name, ServiceSuffix), |
| ServiceNameConstant: c.compileCompoundIdentifier(val.Name, ServiceNameSuffix), |
| ServiceNameString: val.GetServiceName(), |
| } |
| for _, v := range val.Methods { |
| r.Methods = append(r.Methods, c.compileMethod(val.Name, v)) |
| } |
| return r |
| } |
| |
| func compileLibraryIdentifier(lib types.LibraryIdentifier) string { |
| return "fidl/" + joinLibraryIdentifier(lib, "/") |
| } |
| |
| func joinLibraryIdentifier(lib types.LibraryIdentifier, sep string) string { |
| str := make([]string, len([]types.Identifier(lib))) |
| for i, id := range lib { |
| str[i] = string(id) |
| } |
| return strings.Join(str, sep) |
| } |
| |
| // Compile translates parsed FIDL IR into golang backend IR for code generation. |
| func Compile(fidlData types.Root) Root { |
| libraryName := types.ParseLibraryName(fidlData.Name) |
| libraryPath := compileLibraryIdentifier(libraryName) |
| |
| // Collect all libraries. |
| godeps := make(map[string]string) |
| for _, v := range fidlData.Libraries { |
| // Don't try to import yourself. |
| if v.Name == fidlData.Name { |
| continue |
| } |
| libComponents := types.ParseLibraryName(v.Name) |
| path := compileLibraryIdentifier(libComponents) |
| alias := changeIfReserved( |
| types.Identifier(common.ToLowerCamelCase( |
| joinLibraryIdentifier(libComponents, ""), |
| )), |
| "", |
| ) |
| godeps[path] = alias |
| } |
| |
| // Instantiate a compiler context. |
| c := compiler{ |
| decls: fidlData.Decls, |
| library: libraryName, |
| libraryDeps: godeps, |
| usedLibraryDeps: make(map[string]string), |
| } |
| |
| // Compile fidlData into r. |
| r := Root{ |
| Name: string(libraryName[len(libraryName)-1]), |
| PackageName: libraryPath, |
| } |
| for _, v := range fidlData.Consts { |
| r.Consts = append(r.Consts, c.compileConst(v)) |
| } |
| for _, v := range fidlData.Enums { |
| r.Enums = append(r.Enums, c.compileEnum(v)) |
| } |
| for _, v := range fidlData.Structs { |
| r.Structs = append(r.Structs, c.compileStruct(v)) |
| } |
| for _, v := range fidlData.Unions { |
| r.Unions = append(r.Unions, c.compileUnion(v)) |
| } |
| for _, v := range fidlData.Tables { |
| r.Tables = append(r.Tables, c.compileTable(v)) |
| } |
| if len(fidlData.Structs) != 0 || len(fidlData.Interfaces) != 0 { |
| c.usedLibraryDeps[BindingsPackage] = BindingsAlias |
| } |
| if len(fidlData.Interfaces) != 0 { |
| c.usedLibraryDeps[SyscallZxPackage] = SyscallZxAlias |
| } |
| for _, v := range fidlData.Interfaces { |
| r.Interfaces = append(r.Interfaces, c.compileInterface(v)) |
| } |
| for path, alias := range c.usedLibraryDeps { |
| r.Libraries = append(r.Libraries, Library{ |
| Path: path, |
| Alias: alias, |
| }) |
| } |
| // Sort the libraries according to Path. |
| sort.Slice(r.Libraries, func(i, j int) bool { |
| return strings.Compare(r.Libraries[i].Path, r.Libraries[j].Path) == -1 |
| }) |
| return r |
| } |