| // 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 fidlgen |
| |
| import ( |
| "bytes" |
| "encoding/json" |
| "fmt" |
| "io" |
| "os" |
| "sort" |
| "strconv" |
| "strings" |
| ) |
| |
| /* |
| This file contains types which describe FIDL protocols. |
| |
| These types are intended to be directly deserialized from the FIDL protocol |
| JSON representation. The types are then passed directly to language-specific |
| generators which produce source code. |
| |
| Note that these are different from a naive AST-based representation of |
| FIDL text. Before being transformed into JSON, FIDL sources are preprocessed |
| to generate metadata required by all of the backends, such as the size of |
| types. Importantly, this removes the need for language-specific backends to |
| implement field, name, or type resolution and analysis. |
| */ |
| |
| // ReadJSONIr reads a JSON IR file. |
| func ReadJSONIr(filename string) (Root, error) { |
| f, err := os.Open(filename) |
| if err != nil { |
| return Root{}, fmt.Errorf("Error reading from %s: %w", filename, err) |
| } |
| return DecodeJSONIr(f) |
| } |
| |
| // DecodeJSONIr reads the JSON content from a reader. |
| func DecodeJSONIr(r io.Reader) (Root, error) { |
| d := json.NewDecoder(r) |
| var root Root |
| if err := d.Decode(&root); err != nil { |
| return Root{}, fmt.Errorf("Error parsing JSON IR: %w", err) |
| } |
| |
| root.initializeDeclarationsMap() |
| |
| return root, nil |
| } |
| |
| // ReadJSONIrContent reads JSON IR content. |
| func ReadJSONIrContent(b []byte) (Root, error) { |
| return DecodeJSONIr(bytes.NewReader(b)) |
| } |
| |
| type Identifier string |
| |
| // A LibraryIdentifier identifies a FIDL library, from the library declaration |
| // at the start of a FIDL file. |
| type LibraryIdentifier []Identifier |
| |
| // A CompoundIdentifier identifies a particular declaration in a library or |
| // member in a declaration. |
| type CompoundIdentifier struct { |
| // Library the declaration is in. |
| Library LibraryIdentifier |
| // Name of the declaration. |
| Name Identifier |
| // Member of the declaration. If set to empty string, this |
| // CompoundIdentifier refers to the declaration rather than a member. |
| Member Identifier |
| } |
| |
| // An EncodedLibraryIdentifier is a LibraryIdentifier encoded as a string, |
| // suitable for use in map keys. |
| type EncodedLibraryIdentifier string |
| |
| // An EncodedCompoundIdentifier is a CompoundIdentifier encoded as a string, |
| // suitable for use in map keys. |
| type EncodedCompoundIdentifier string |
| |
| // Encode formats a LibraryIdentifier as a string by joining the identifier |
| // components with ".", e.g. "my.fidl.library". |
| func (li LibraryIdentifier) Encode() EncodedLibraryIdentifier { |
| ss := make([]string, len(li)) |
| for i, s := range li { |
| ss[i] = string(s) |
| } |
| return EncodedLibraryIdentifier(strings.Join(ss, ".")) |
| } |
| |
| // EncodeDecl encodes the fully-qualified declaration portion of the |
| // CompoundIdentifier. |
| // |
| // Encoded form consists of the encoded form of the library identifier, followed |
| // by a slash, then the name of the declaration. If the CompoundIdentifier does |
| // not have a Member, this will be the same as Encode. Example: |
| // "my.fidl.library/MyProtocol". |
| func (ci CompoundIdentifier) EncodeDecl() EncodedCompoundIdentifier { |
| return EncodedCompoundIdentifier(string(ci.Library.Encode()) + "/" + string(ci.Name)) |
| } |
| |
| // Encode encodes the fully-qualified declaration or member identified by this |
| // CompoundIdentifier. |
| // |
| // Encoded form consists of the encoded library identifier, then the declaration |
| // name. If a member is specified, it will come after the declaration name, |
| // separated by a dot. Example: |
| // - With no Member: "my.fidl.library/MyProtocol" |
| // - With Member: "my.fidl.library/MyProtocol.SomeMethod" |
| func (ci CompoundIdentifier) Encode() EncodedCompoundIdentifier { |
| if ci.Member != "" { |
| return EncodedCompoundIdentifier(fmt.Sprintf("%s.%s", ci.EncodeDecl(), ci.Member)) |
| } |
| return ci.EncodeDecl() |
| } |
| |
| // Parts splits the library identifier back into component parts. |
| func (eli EncodedLibraryIdentifier) Parts() []string { |
| return strings.Split(string(eli), ".") |
| } |
| |
| // Parse decodes an EncodedLibraryIdentifier back into a LibraryIdentifier. |
| func (eli EncodedLibraryIdentifier) Parse() LibraryIdentifier { |
| parts := eli.Parts() |
| idents := make([]Identifier, len(parts)) |
| for i, part := range parts { |
| idents[i] = Identifier(part) |
| } |
| return LibraryIdentifier(idents) |
| } |
| |
| // PartsReversed splits the library identifier back into component parts and |
| // returns them in reverse order. |
| func (eli EncodedLibraryIdentifier) PartsReversed() []string { |
| parts := eli.Parts() |
| partsReversed := make([]string, len(parts)) |
| for i, part := range parts { |
| partsReversed[len(parts)-i-1] = string(part) |
| } |
| |
| return partsReversed |
| } |
| |
| // Parts splits an EncodedCompoundIdentifier into an optional library name and |
| // declaration or member id. |
| // |
| // This splits off the library name, but does not check whether the referenced |
| // member is a delaration or member of a declaration. |
| func (eci EncodedCompoundIdentifier) Parts() []string { |
| return strings.SplitN(string(eci), "/", 2) |
| } |
| |
| // LibraryName retrieves the library name from an EncodedCompoundIdentifier. |
| func (eci EncodedCompoundIdentifier) LibraryName() EncodedLibraryIdentifier { |
| raw_library := "" |
| if parts := eci.Parts(); len(parts) == 2 { |
| raw_library = parts[0] |
| } |
| return EncodedLibraryIdentifier(raw_library) |
| } |
| |
| // DeclName retrieves the fully-qualified declaration name from an |
| // EncodedCompoundIdentifier. This operation is idempotent. |
| func (eci EncodedCompoundIdentifier) DeclName() EncodedCompoundIdentifier { |
| ci := eci.Parse() |
| parts := []string{} |
| for _, l := range ci.Library { |
| parts = append(parts, string(l)) |
| } |
| return EncodedCompoundIdentifier(fmt.Sprintf("%s/%s", |
| strings.Join(parts, "."), ci.Name)) |
| } |
| |
| // Parse converts an EncodedCompoundIdentifier back into a CompoundIdentifier. |
| func (eci EncodedCompoundIdentifier) Parse() CompoundIdentifier { |
| parts := eci.Parts() |
| raw_library := "" |
| raw_name := parts[0] |
| if len(parts) == 2 { |
| raw_library = parts[0] |
| raw_name = parts[1] |
| } |
| library := EncodedLibraryIdentifier(raw_library).Parse() |
| name_parts := strings.SplitN(raw_name, ".", 2) |
| name := Identifier(name_parts[0]) |
| member := Identifier("") |
| if len(name_parts) == 2 { |
| member = Identifier(name_parts[1]) |
| } |
| return CompoundIdentifier{library, name, member} |
| } |
| |
| type PrimitiveSubtype string |
| |
| const ( |
| Bool PrimitiveSubtype = "bool" |
| Int8 PrimitiveSubtype = "int8" |
| Int16 PrimitiveSubtype = "int16" |
| Int32 PrimitiveSubtype = "int32" |
| Int64 PrimitiveSubtype = "int64" |
| Uint8 PrimitiveSubtype = "uint8" |
| Uint16 PrimitiveSubtype = "uint16" |
| Uint32 PrimitiveSubtype = "uint32" |
| Uint64 PrimitiveSubtype = "uint64" |
| Float32 PrimitiveSubtype = "float32" |
| Float64 PrimitiveSubtype = "float64" |
| ) |
| |
| var unsignedSubtypes = map[PrimitiveSubtype]struct{}{ |
| Uint8: {}, |
| Uint16: {}, |
| Uint32: {}, |
| Uint64: {}, |
| } |
| |
| // IsSigned indicates whether this subtype represents a signed number such as |
| // `int16`, or `float32`. |
| func (typ PrimitiveSubtype) IsSigned() bool { |
| return !typ.IsUnsigned() |
| } |
| |
| // IsUnsigned indicates whether this subtype represents an unsigned number such |
| // as `uint16`. |
| func (typ PrimitiveSubtype) IsUnsigned() bool { |
| _, ok := unsignedSubtypes[typ] |
| return ok |
| } |
| |
| type HandleSubtype string |
| |
| const ( |
| Handle HandleSubtype = "handle" |
| Bti HandleSubtype = "bti" |
| Channel HandleSubtype = "channel" |
| Clock HandleSubtype = "clock" |
| DebugLog HandleSubtype = "debuglog" |
| Event HandleSubtype = "event" |
| Eventpair HandleSubtype = "eventpair" |
| Exception HandleSubtype = "exception" |
| Fifo HandleSubtype = "fifo" |
| Guest HandleSubtype = "guest" |
| Interrupt HandleSubtype = "interrupt" |
| Iommu HandleSubtype = "iommu" |
| Job HandleSubtype = "job" |
| Pager HandleSubtype = "pager" |
| PciDevice HandleSubtype = "pcidevice" |
| Pmt HandleSubtype = "pmt" |
| Port HandleSubtype = "port" |
| Process HandleSubtype = "process" |
| Profile HandleSubtype = "profile" |
| Resource HandleSubtype = "resource" |
| Socket HandleSubtype = "socket" |
| Stream HandleSubtype = "stream" |
| SuspendToken HandleSubtype = "suspendtoken" |
| Thread HandleSubtype = "thread" |
| Time HandleSubtype = "timer" |
| Vcpu HandleSubtype = "vcpu" |
| Vmar HandleSubtype = "vmar" |
| Vmo HandleSubtype = "vmo" |
| ) |
| |
| // TODO(fxb/64629): Remove, source of truth is library zx. |
| // |
| // One complication is that GIDL parses nice handle subtypes in its grammar, |
| // e.g. `#0 = event(rights: execute + write )`. And some GIDL backends care |
| // about the object type. This means that we need to duplicate this mapping :/ |
| // It would be cleaner to limit this to GIDL and GIDL backends, rather than |
| // offer that in the general purpose lib/fidlgen. |
| type ObjectType uint32 |
| |
| const ( |
| ObjectTypeNone = ObjectType(iota) |
| ObjectTypeProcess |
| ObjectTypeThread |
| ObjectTypeVmo |
| ObjectTypeChannel |
| ObjectTypeEvent |
| ObjectTypePort |
| _ // 7 |
| _ // 8 |
| ObjectTypeInterrupt |
| _ // 10 |
| ObjectTypePciDevice |
| ObjectTypeLog |
| _ // 13 |
| ObjectTypeSocket |
| ObjectTypeResource |
| ObjectTypeEventPair |
| ObjectTypeJob |
| ObjectTypeVmar |
| ObjectTypeFifo |
| ObjectTypeGuest |
| ObjectTypeVcpu |
| ObjectTypeTimer |
| ObjectTypeIommu |
| ObjectTypeBti |
| ObjectTypeProfile |
| ObjectTypePmt |
| ObjectTypeSuspendToken |
| ObjectTypePager |
| ObjectTypeException |
| ObjectTypeClock |
| ) |
| |
| func ObjectTypeFromHandleSubtype(val HandleSubtype) ObjectType { |
| switch val { |
| case Bti: |
| return ObjectTypeBti |
| case Channel: |
| return ObjectTypeChannel |
| case Clock: |
| return ObjectTypeClock |
| case DebugLog: |
| return ObjectTypeLog |
| case Event: |
| return ObjectTypeEvent |
| case Eventpair: |
| return ObjectTypeEventPair |
| case Exception: |
| return ObjectTypeException |
| case Fifo: |
| return ObjectTypeFifo |
| case Guest: |
| return ObjectTypeGuest |
| case Interrupt: |
| return ObjectTypeInterrupt |
| case Iommu: |
| return ObjectTypeIommu |
| case Job: |
| return ObjectTypeJob |
| case Pager: |
| return ObjectTypePager |
| case PciDevice: |
| return ObjectTypePciDevice |
| case Pmt: |
| return ObjectTypePmt |
| case Port: |
| return ObjectTypePort |
| case Process: |
| return ObjectTypeProcess |
| case Profile: |
| return ObjectTypeProfile |
| case Resource: |
| return ObjectTypeResource |
| case Socket: |
| return ObjectTypeSocket |
| case SuspendToken: |
| return ObjectTypeSuspendToken |
| case Thread: |
| return ObjectTypeThread |
| case Time: |
| return ObjectTypeTimer |
| case Vcpu: |
| return ObjectTypeVcpu |
| case Vmar: |
| return ObjectTypeVmar |
| case Vmo: |
| return ObjectTypeVmo |
| default: |
| return ObjectTypeNone |
| } |
| } |
| |
| type HandleRights uint32 |
| |
| const ( |
| HandleRightsNone HandleRights = 0 |
| |
| HandleRightsDuplicate HandleRights = 1 << 0 |
| HandleRightsTransfer HandleRights = 1 << 1 |
| HandleRightsRead HandleRights = 1 << 2 |
| HandleRightsWrite HandleRights = 1 << 3 |
| HandleRightsExecute HandleRights = 1 << 4 |
| HandleRightsMap HandleRights = 1 << 5 |
| HandleRightsGetProperty HandleRights = 1 << 6 |
| HandleRightsSetProperty HandleRights = 1 << 7 |
| HandleRightsEnumerate HandleRights = 1 << 8 |
| HandleRightsDestroy HandleRights = 1 << 9 |
| HandleRightsSetPolicy HandleRights = 1 << 10 |
| HandleRightsGetPolicy HandleRights = 1 << 11 |
| HandleRightsSignal HandleRights = 1 << 12 |
| HandleRightsSignalPeer HandleRights = 1 << 13 |
| HandleRightsWait HandleRights = 1 << 14 |
| HandleRightsInspect HandleRights = 1 << 15 |
| HandleRightsManageJob HandleRights = 1 << 16 |
| HandleRightsManageProcess HandleRights = 1 << 17 |
| HandleRightsManageThread HandleRights = 1 << 18 |
| HandleRightsApplyProfile HandleRights = 1 << 19 |
| |
| HandleRightsSameRights HandleRights = 1 << 31 |
| |
| HandleRightsBasic HandleRights = HandleRightsTransfer | HandleRightsDuplicate | HandleRightsWait | HandleRightsInspect |
| ) |
| |
| type LiteralKind string |
| |
| const ( |
| StringLiteral LiteralKind = "string" |
| NumericLiteral LiteralKind = "numeric" |
| BoolLiteral LiteralKind = "bool" |
| DefaultLiteral LiteralKind = "default" |
| ) |
| |
| type Literal struct { |
| Kind LiteralKind `json:"kind"` |
| Value string `json:"value,omitempty"` |
| } |
| |
| type ConstantKind string |
| |
| const ( |
| IdentifierConstant ConstantKind = "identifier" |
| LiteralConstant ConstantKind = "literal" |
| BinaryOperator ConstantKind = "binary_operator" |
| ) |
| |
| type Constant struct { |
| Kind ConstantKind `json:"kind"` |
| Identifier EncodedCompoundIdentifier `json:"identifier,omitempty"` |
| Literal Literal `json:"literal,omitempty"` |
| Value string `json:"value"` |
| } |
| |
| // Location gives the location of the FIDL declaration in its source `.fidl` |
| // file. |
| type Location struct { |
| Filename string `json:"filename"` |
| Line int `json:"line"` |
| Column int `json:"column"` |
| Length int `json:"length"` |
| } |
| |
| type TypeKind string |
| |
| const ( |
| ArrayType TypeKind = "array" |
| VectorType TypeKind = "vector" |
| StringType TypeKind = "string" |
| HandleType TypeKind = "handle" |
| RequestType TypeKind = "request" |
| PrimitiveType TypeKind = "primitive" |
| IdentifierType TypeKind = "identifier" |
| ) |
| |
| type Type struct { |
| Kind TypeKind |
| ElementType *Type |
| ElementCount *int |
| HandleSubtype HandleSubtype |
| HandleRights HandleRights |
| RequestSubtype EncodedCompoundIdentifier |
| PrimitiveSubtype PrimitiveSubtype |
| Identifier EncodedCompoundIdentifier |
| Nullable bool |
| ProtocolTransport string |
| ObjType uint32 |
| ResourceIdentifier string |
| TypeShapeV1 TypeShape |
| TypeShapeV2 TypeShape |
| } |
| |
| // UnmarshalJSON customizes the JSON unmarshalling for Type. |
| func (t *Type) UnmarshalJSON(b []byte) error { |
| var obj map[string]*json.RawMessage |
| err := json.Unmarshal(b, &obj) |
| if err != nil { |
| return err |
| } |
| |
| err = json.Unmarshal(*obj["kind"], &t.Kind) |
| if err != nil { |
| return err |
| } |
| err = json.Unmarshal(*obj["type_shape_v1"], &t.TypeShapeV1) |
| if err != nil { |
| return err |
| } |
| err = json.Unmarshal(*obj["type_shape_v2"], &t.TypeShapeV2) |
| if err != nil { |
| return err |
| } |
| |
| switch t.Kind { |
| case ArrayType: |
| t.ElementType = &Type{} |
| err = json.Unmarshal(*obj["element_type"], t.ElementType) |
| if err != nil { |
| return err |
| } |
| err = json.Unmarshal(*obj["element_count"], &t.ElementCount) |
| if err != nil { |
| return err |
| } |
| case VectorType: |
| t.ElementType = &Type{} |
| err = json.Unmarshal(*obj["element_type"], t.ElementType) |
| if err != nil { |
| return err |
| } |
| if elementCount, ok := obj["maybe_element_count"]; ok { |
| err = json.Unmarshal(*elementCount, &t.ElementCount) |
| if err != nil { |
| return err |
| } |
| } |
| err = json.Unmarshal(*obj["nullable"], &t.Nullable) |
| if err != nil { |
| return err |
| } |
| case StringType: |
| if elementCount, ok := obj["maybe_element_count"]; ok { |
| err = json.Unmarshal(*elementCount, &t.ElementCount) |
| if err != nil { |
| return err |
| } |
| } |
| err = json.Unmarshal(*obj["nullable"], &t.Nullable) |
| if err != nil { |
| return err |
| } |
| case HandleType: |
| err = json.Unmarshal(*obj["subtype"], &t.HandleSubtype) |
| if err != nil { |
| return err |
| } |
| err = json.Unmarshal(*obj["rights"], &t.HandleRights) |
| if err != nil { |
| return err |
| } |
| err = json.Unmarshal(*obj["nullable"], &t.Nullable) |
| if err != nil { |
| return err |
| } |
| err = json.Unmarshal(*obj["obj_type"], &t.ObjType) |
| if err != nil { |
| return err |
| } |
| err = json.Unmarshal(*obj["resource_identifier"], &t.ResourceIdentifier) |
| if err != nil { |
| return err |
| } |
| case RequestType: |
| err = json.Unmarshal(*obj["subtype"], &t.RequestSubtype) |
| if err != nil { |
| return err |
| } |
| err = json.Unmarshal(*obj["nullable"], &t.Nullable) |
| if err != nil { |
| return err |
| } |
| err = json.Unmarshal(*obj["protocol_transport"], &t.ProtocolTransport) |
| if err != nil { |
| return err |
| } |
| case PrimitiveType: |
| err = json.Unmarshal(*obj["subtype"], &t.PrimitiveSubtype) |
| if err != nil { |
| return err |
| } |
| case IdentifierType: |
| err = json.Unmarshal(*obj["identifier"], &t.Identifier) |
| if err != nil { |
| return err |
| } |
| err = json.Unmarshal(*obj["nullable"], &t.Nullable) |
| if err != nil { |
| return err |
| } |
| if protocolTransport, ok := obj["protocol_transport"]; ok { |
| err = json.Unmarshal(*protocolTransport, &t.ProtocolTransport) |
| if err != nil { |
| return err |
| } |
| } |
| default: |
| return fmt.Errorf("Unknown type kind: %s", t.Kind) |
| } |
| |
| return nil |
| } |
| |
| type AttributeArg struct { |
| Name Identifier `json:"name"` |
| Value Constant `json:"value"` |
| } |
| |
| // ValueString returns the attribute arg's value in string form. |
| // TODO(fxbug.dev/81390): Attribute values may only be string literals for now. |
| // Make sure to fix this API once that changes to resolve the constant value |
| // for all constant types. |
| func (el AttributeArg) ValueString() string { |
| return el.Value.Value |
| } |
| |
| type Attribute struct { |
| Name Identifier `json:"name"` |
| Args []AttributeArg `json:"arguments,omitempty"` |
| } |
| |
| func (el Attribute) LookupArg(name Identifier) (AttributeArg, bool) { |
| for _, a := range el.Args { |
| if a.Name == name { |
| return a, true |
| } |
| } |
| return AttributeArg{}, false |
| } |
| |
| func (el Attribute) LookupArgStandalone() (AttributeArg, bool) { |
| if len(el.Args) != 1 { |
| return AttributeArg{}, false |
| } |
| return el.Args[0], true |
| } |
| |
| func (el Attribute) HasArg(name Identifier) bool { |
| _, ok := el.LookupArg(name) |
| return ok |
| } |
| |
| // Attributes represents a list of attributes. It conveniently implements the |
| // `Annotated` protocol, such that it can be embedded into other node structs |
| // which are annotated. |
| type Attributes struct { |
| Attributes []Attribute `json:"maybe_attributes,omitempty"` |
| } |
| |
| func (el Attributes) LookupAttribute(name Identifier) (Attribute, bool) { |
| for _, a := range el.Attributes { |
| if ToSnakeCase(string(a.Name)) == ToSnakeCase(string(name)) { |
| return a, true |
| } |
| } |
| return Attribute{}, false |
| } |
| |
| func (el Attributes) HasAttribute(name Identifier) bool { |
| _, ok := el.LookupAttribute(name) |
| return ok |
| } |
| |
| func (el Attributes) DocComments() []string { |
| attr, ok := el.LookupAttribute("doc") |
| if !ok { |
| return nil |
| } |
| doc, ok := attr.LookupArgStandalone() |
| docVal := doc.ValueString() |
| if !ok || docVal == "" { |
| return nil |
| } |
| return strings.Split(docVal[0:len(docVal)-1], "\n") |
| } |
| |
| func (el Attributes) Transports() map[string]struct{} { |
| transports := make(map[string]struct{}) |
| attr, ok := el.LookupAttribute("transport") |
| if ok { |
| raw, ok := attr.LookupArgStandalone() |
| if ok && raw.ValueString() != "" { |
| for _, transport := range strings.Split(raw.ValueString(), ",") { |
| transports[strings.TrimSpace(transport)] = struct{}{} |
| } |
| } |
| } |
| // No transport attribute => just Channel |
| if !ok { |
| transports["Channel"] = struct{}{} |
| } |
| return transports |
| } |
| |
| // BindingsDenylistIncludes returns true if the comma-separated |
| // bindings_denylist attribute includes targetLanguage (meaning the bindings for |
| // targetLanguage should not emit this declaration). |
| func (el Attributes) BindingsDenylistIncludes(targetLanguage string) bool { |
| attr, ok := el.LookupAttribute("bindings_denylist") |
| if !ok { |
| return false |
| } |
| raw, ok := attr.LookupArgStandalone() |
| if ok && raw.ValueString() != "" { |
| for _, language := range strings.Split(raw.ValueString(), ",") { |
| if strings.TrimSpace(language) == targetLanguage { |
| return true |
| } |
| } |
| } |
| return false |
| } |
| |
| // TypeShape represents the shape of the type on the wire. |
| // See JSON IR schema, e.g. fidlc --json-schema |
| type TypeShape struct { |
| InlineSize int `json:"inline_size"` |
| Alignment int `json:"alignment"` |
| Depth int `json:"depth"` |
| MaxHandles int `json:"max_handles"` |
| MaxOutOfLine int `json:"max_out_of_line"` |
| HasPadding bool `json:"has_padding"` |
| HasEnvelope bool `json:"has_envelope"` |
| HasFlexibleEnvelope bool `json:"has_flexible_envelope"` |
| } |
| |
| // FieldShape represents the shape of the field on the wire. |
| // See JSON IR schema, e.g. fidlc --json-schema |
| type FieldShape struct { |
| Offset int `json:"offset"` |
| Padding int `json:"padding"` |
| } |
| |
| type Declaration interface { |
| GetName() EncodedCompoundIdentifier |
| } |
| |
| type Decl struct { |
| Attributes |
| Name EncodedCompoundIdentifier `json:"name"` |
| Location `json:"location"` |
| } |
| |
| func (d *Decl) GetName() EncodedCompoundIdentifier { |
| return d.Name |
| } |
| |
| // NamingContext represents the content of the `naming_context` JSON IR field, |
| // which enumerates inr order the names of the parent declarations of some |
| // declaration. Top-level declarations have a list of size 1, with their own |
| // name as the only member. Nested (ie, anonymous) declarations are lists of a |
| // size greater than 1, starting with the outer most ancestor declaration. |
| // |
| // While the `name` and the last string in a `naming_context` are usually |
| // identical, the `name` can be arbitrarily changed using the |
| // `@generated_name()` FIDL annotation, so this is not guaranteed to be the |
| // case. |
| type NamingContext []string |
| |
| // IsAnonymous states whether the described NamingContext indicates anonymous |
| // declaration (ie, not |
| // explicitly named in the source FIDL). |
| func (nc NamingContext) IsAnonymous() bool { |
| return len(nc) > 1 |
| } |
| |
| // Join builds a name string out of the disparate parts of the NamingContext. |
| func (nc NamingContext) Join() string { |
| return strings.Join(nc, "") |
| } |
| |
| // scopedNamingContext stores a NamingContext that also includes the library |
| // from which that naming context was sourced. This is useful for comparing |
| // identical NamingContexts from different libraries for uniqueness. |
| type scopedNamingContext struct { |
| lib EncodedLibraryIdentifier |
| nc NamingContext |
| } |
| |
| // isDenied is a function that, given a list of denied scopedNamingContext |
| // prefixes generated by the deniedContexts() function, should take the supplied |
| // naming context and validate that none of the prefix lists in deniedContexts |
| // are identical to the start of this naming context. If any of them are, the |
| // naming context in question is attached to type or method that was declared |
| // anonymously inside of another declaration bearing a @bindings_denylist |
| // enumerating the language currently being compiled, meaning that that type or |
| // method should also be omitted from the output. |
| func (snc scopedNamingContext) isDenied(dcs []scopedNamingContext) bool { |
| // For each denied naming context prefix, check to see if the candidate |
| // layout's naming context starts with all of the same members and is sourced |
| // from the same library. If that is the case, the naming context in question |
| // is denied. |
| outer: |
| for _, dc := range dcs { |
| if snc.lib != dc.lib || len(snc.nc) < len(dc.nc) { |
| continue |
| } |
| for i, v := range dc.nc { |
| if snc.nc[i] != v { |
| continue outer |
| } |
| } |
| return true |
| } |
| return false |
| } |
| |
| // Layout represents data specific to bits/enums/structs/tables/unions. All |
| // layouts are decls, but not all decls are layouts (e.g. protocols). |
| type Layout struct { |
| Decl |
| NamingContext NamingContext `json:"naming_context"` |
| } |
| |
| // IsAnonymous states whether this Layout has an anonymous naming context. We |
| // treat inner layouts (i.e. layouts defined within another layout) as |
| // anonymous. All such layouts have a naming context with length greater than |
| // one, since they include at least the top level name followed by one or more |
| // inner names. |
| func (l *Layout) IsAnonymous() bool { |
| return l.NamingContext.IsAnonymous() |
| } |
| |
| // Assert that declarations conform to the Declaration interface |
| var _ = []Declaration{ |
| (*TypeAlias)(nil), |
| (*Union)(nil), |
| (*Table)(nil), |
| (*Struct)(nil), |
| (*Protocol)(nil), |
| (*Service)(nil), |
| (*Enum)(nil), |
| (*Bits)(nil), |
| (*Const)(nil), |
| } |
| |
| // TypeAlias represents the declaration of a FIDL type alias. |
| type TypeAlias struct { |
| Decl |
| PartialTypeConstructor PartialTypeConstructor `json:"partial_type_ctor"` |
| } |
| |
| // PartialTypeConstructor represents a FIDL type as it is constructed from |
| // other type arguments. |
| type PartialTypeConstructor struct { |
| Name EncodedCompoundIdentifier `json:"name"` |
| Args []PartialTypeConstructor `json:"args"` |
| Nullable bool `json:"nullable"` |
| MaybeSize *Constant `json:"maybe_size,omitempty"` |
| } |
| |
| // Union represents the declaration of a FIDL union. |
| type Union struct { |
| Layout |
| Members []UnionMember `json:"members"` |
| Strictness `json:"strict"` |
| Resourceness `json:"resource"` |
| TypeShapeV1 TypeShape `json:"type_shape_v1"` |
| TypeShapeV2 TypeShape `json:"type_shape_v2"` |
| } |
| |
| // UnionMember represents the declaration of a field in a FIDL extensible |
| // union. |
| type UnionMember struct { |
| Attributes |
| Reserved bool `json:"reserved"` |
| Ordinal int `json:"ordinal"` |
| Type Type `json:"type"` |
| Name Identifier `json:"name"` |
| Offset int `json:"offset"` |
| MaxOutOfLine int `json:"max_out_of_line"` |
| } |
| |
| // Table represents a declaration of a FIDL table. |
| type Table struct { |
| Layout |
| Members []TableMember `json:"members"` |
| Resourceness `json:"resource"` |
| TypeShapeV1 TypeShape `json:"type_shape_v1"` |
| TypeShapeV2 TypeShape `json:"type_shape_v2"` |
| } |
| |
| // TableMember represents the declaration of a field in a FIDL table. |
| type TableMember struct { |
| Attributes |
| Reserved bool `json:"reserved"` |
| Type Type `json:"type"` |
| Name Identifier `json:"name"` |
| Ordinal int `json:"ordinal"` |
| MaybeDefaultValue *Constant `json:"maybe_default_value,omitempty"` |
| MaxOutOfLine int `json:"max_out_of_line"` |
| } |
| |
| // byTableOrdinal is a wrapper type for sorting a []TableMember. |
| type byTableOrdinal []TableMember |
| |
| func (s byTableOrdinal) Len() int { |
| return len(s) |
| } |
| |
| func (s byTableOrdinal) Less(i, j int) bool { |
| return s[i].Ordinal < s[j].Ordinal |
| } |
| |
| func (s byTableOrdinal) Swap(i, j int) { |
| s[i], s[j] = s[j], s[i] |
| } |
| |
| // SortedMembersNoReserved returns the table's members sorted by ordinal, |
| // excluding reserved members. |
| func (t *Table) SortedMembersNoReserved() []TableMember { |
| var members []TableMember |
| for _, member := range t.Members { |
| if !member.Reserved { |
| members = append(members, member) |
| } |
| } |
| sort.Sort(byTableOrdinal(members)) |
| return members |
| } |
| |
| // Struct represents a declaration of a FIDL struct. |
| type Struct struct { |
| Layout |
| Members []StructMember `json:"members"` |
| Resourceness `json:"resource"` |
| TypeShapeV1 TypeShape `json:"type_shape_v1"` |
| TypeShapeV2 TypeShape `json:"type_shape_v2"` |
| } |
| |
| // StructMember represents the declaration of a field in a FIDL struct. |
| type StructMember struct { |
| Attributes |
| Type Type `json:"type"` |
| Name Identifier `json:"name"` |
| MaybeDefaultValue *Constant `json:"maybe_default_value,omitempty"` |
| MaxHandles int `json:"max_handles"` |
| FieldShapeV1 FieldShape `json:"field_shape_v1"` |
| FieldShapeV2 FieldShape `json:"field_shape_v2"` |
| } |
| |
| // EmptyStructMember returns a StructMember that's suitable as the sole member |
| // of an empty struct. |
| func EmptyStructMember(name string) StructMember { |
| // Empty structs have a size of 1, so the uint8 struct member returned by this |
| // function can be used to pad the struct to the correct size. |
| |
| return StructMember{ |
| Type: Type{ |
| Kind: PrimitiveType, |
| PrimitiveSubtype: Uint8, |
| }, |
| Name: Identifier(name), |
| MaybeDefaultValue: &Constant{ |
| Kind: "literal", |
| Identifier: "", |
| Literal: Literal{ |
| Kind: "numeric", |
| Value: "0", |
| }, |
| }, |
| } |
| } |
| |
| // Openness of a protocol. Affects whether unknown interaction handlers are generated. Also controls |
| // whether methods are allowed to be flexible, but that is enforced by fidlc, not fidlgen. |
| type Openness string |
| |
| const ( |
| Open Openness = "open" |
| Ajar Openness = "ajar" |
| Closed Openness = "closed" |
| ) |
| |
| func (o Openness) IsClosed() bool { |
| switch o { |
| case Open, Ajar: |
| return false |
| case Closed, "": |
| return true |
| default: |
| panic(fmt.Errorf("invalid openness %s", o)) |
| } |
| } |
| |
| // Protocol represents the declaration of a FIDL protocol. |
| type Protocol struct { |
| Decl |
| // Whether the protocol is open. This affects whether the server-side generates handlers for |
| // unknown interactions. |
| Openness Openness `json:"openness,omitempty"` |
| // List of methods that are part of this protocol. |
| Methods []Method `json:"methods"` |
| } |
| |
| // If the protocol is discoverable, gets the discovery name for the protocol, consisting of the |
| // library name and protocol declaration name separated by dots and enclosed in quotes. For example, |
| // "\"my.library.MyProtocol\"". This part of legacy service discovery (pre-RFC-0041). |
| func (d *Protocol) GetServiceName() string { |
| attr, ok := d.LookupAttribute("discoverable") |
| if !ok { |
| return "" |
| } |
| var name string |
| if arg, ok := attr.LookupArgStandalone(); ok { |
| name = arg.ValueString() |
| } else { |
| // TODO(fxbug.dev/102803): Construct this string in fidlc, not here. |
| ci := d.Name.Parse() |
| var parts []string |
| for _, i := range ci.Library { |
| parts = append(parts, string(i)) |
| } |
| parts = append(parts, string(ci.Name)) |
| name = strings.Join(parts, ".") |
| } |
| return strconv.Quote(name) |
| } |
| |
| // Returns true if this protocol must handle one-way unknown interactions. |
| func (p *Protocol) OneWayUnknownInteractions() bool { |
| return p.Openness == Open || p.Openness == Ajar |
| } |
| |
| // Returns true if this protocol must handle two-way unknown interactions. |
| func (p *Protocol) TwoWayUnknownInteractions() bool { |
| return p.Openness == Open |
| } |
| |
| // Service represents the declaration of a FIDL service. |
| type Service struct { |
| Decl |
| Members []ServiceMember `json:"members"` |
| } |
| |
| func (s *Service) GetServiceName() string { |
| ci := s.Name.Parse() |
| var parts []string |
| for _, i := range ci.Library { |
| parts = append(parts, string(i)) |
| } |
| parts = append(parts, string(ci.Name)) |
| return strings.Join(parts, ".") |
| } |
| |
| // ServiceMember represents the declaration of a field in a FIDL service. |
| type ServiceMember struct { |
| Attributes |
| Name Identifier `json:"name"` |
| Type Type `json:"type"` |
| } |
| |
| // Method represents the declaration of a FIDL method. |
| type Method struct { |
| Attributes |
| // Computed ordinal to use to identify the method on the wire. |
| Ordinal uint64 `json:"ordinal"` |
| // Unqualified name of the method. |
| Name Identifier `json:"name"` |
| // Whether the method is marked as strict (other wise flexible). |
| // |
| // While unknown interactions are experimental, a not-set strictness should |
| // be treated as strict. After unknown interactions are released this field |
| // will be made required. For now, use IsStrict() to access the value |
| // correctly. |
| // |
| // TODO(fxbug.dev/88366): make "strict" required once fidlc always provides |
| // it. It's currently gated by unknown_interactions and should not default |
| // to false when not provided. |
| MaybeStrict *bool `json:"strict,omitempty"` |
| // True if the method was composed into this protocol from another protocol |
| // definition. |
| IsComposed bool `json:"is_composed"` |
| // True if this method has a request. This is true for all client-initiated |
| // methods, and false for server-initiated events. There may still be no |
| // request payload, for example "Foo()" has a request but no request |
| // payload. |
| HasRequest bool `json:"has_request"` |
| // The request payload of the method, given in the method arguments. |
| RequestPayload *Type `json:"maybe_request_payload,omitempty"` |
| // True if this method has a response. This is true for two-way methods and |
| // for server-initiated events. There may still be no response payload, for |
| // example "Foo(...) -> ()" or "-> Bar()" have a response but no response |
| // payload. |
| HasResponse bool `json:"has_response"` |
| // The full response payload, as it appears on the wire. If flexible or |
| // error syntax are used, this is a struct with a single field containing |
| // the ResultType. Otherwise, it is the same as the return value of the |
| // method. |
| ResponsePayload *Type `json:"maybe_response_payload,omitempty"` |
| // Whether the method uses the "error" syntax. If true, ResponsePayload will |
| // be a single-element struct wrapping the result union, and response will |
| // be further broken down in the ResultType, ValueType, and ErrorType |
| // fields. |
| HasError bool `json:"has_error"` |
| // If flexible or error syntax are used, this is the type of the result |
| // union containing the ValueType and (if error syntax) ErrorType. |
| ResultType *Type `json:"maybe_response_result_type,omitempty"` |
| // If flexible or error syntax are used, this is the type of the success |
| // variant of the ResultType union, which is the same as the return value. |
| ValueType *Type `json:"maybe_response_success_type,omitempty"` |
| // If error syntax is used, this is the type of the error variant of the |
| // ResultType union. |
| ErrorType *Type `json:"maybe_response_err_type,omitempty"` |
| } |
| |
| // GetRequestPayloadIdentifier retrieves the identifier that points to the |
| // declaration of the request payload. |
| func (m *Method) GetRequestPayloadIdentifier() (EncodedCompoundIdentifier, bool) { |
| if m.RequestPayload == nil { |
| return "", false |
| } |
| return m.RequestPayload.Identifier, true |
| } |
| |
| // GetResponsePayloadIdentifier retrieves the identifier that points to the |
| // declaration of the response payload. |
| func (m *Method) GetResponsePayloadIdentifier() (EncodedCompoundIdentifier, bool) { |
| if m.ResponsePayload == nil { |
| return "", false |
| } |
| return m.ResponsePayload.Identifier, true |
| } |
| |
| // Helper for getting whether the method is strict. Strict is optional while |
| // unknown interactions are experimental, and if strict is missing it should be |
| // treated as true. |
| // TODO(fxbug.dev/88366): replace this method with direct access to the Strict |
| // field, once it is required. |
| func (m *Method) IsStrict() bool { |
| return m.MaybeStrict == nil || *m.MaybeStrict |
| } |
| |
| // IsFlexible is the inverse of IsStrict. Convenience for templates so they |
| // don't have to use (not .IsStrict) |
| func (m *Method) IsFlexible() bool { |
| return !m.IsStrict() |
| } |
| |
| // IsTransitional returns whether this method has the `Transitional` attribute. |
| func (m *Method) IsTransitional() bool { |
| return m.HasAttribute("transitional") |
| } |
| |
| func (m *Method) HasRequestPayload() bool { |
| return m.RequestPayload != nil |
| } |
| |
| func (m *Method) HasResponsePayload() bool { |
| return m.ResponsePayload != nil |
| } |
| |
| // HasTransportError returns true if the method uses a result union with |
| // transport_err variant. This is true if it is a flexible two-way method. |
| func (m *Method) HasTransportError() bool { |
| return m.HasRequest && m.HasResponse && m.IsFlexible() |
| } |
| |
| // Enum represents a FIDL declaration of an enum. |
| type Enum struct { |
| Layout |
| Type PrimitiveSubtype `json:"type"` |
| Members []EnumMember `json:"members"` |
| Strictness `json:"strict"` |
| RawUnknownValue int64OrUint64 `json:"maybe_unknown_value"` |
| } |
| |
| // UnknownValueAsInt64 retrieves the unknown value. Succeeds only for signed |
| // flexible enums. |
| func (enum *Enum) UnknownValueAsInt64() (int64, error) { |
| if enum.IsStrict() { |
| return 0, fmt.Errorf("cannot retrieve unknown value of strict enum") |
| } |
| if enum.Type.IsUnsigned() { |
| return 0, fmt.Errorf("cannot retrieve signed unknown value of unsigned flexible enum") |
| } |
| return enum.RawUnknownValue.readInt64(), nil |
| } |
| |
| // UnknownValueAsUint64 retrieves the unknown value. Succeeds only for unsigned |
| // flexible enums. |
| func (enum *Enum) UnknownValueAsUint64() (uint64, error) { |
| if enum.IsStrict() { |
| return 0, fmt.Errorf("cannot retrieve unknown value of strict enum") |
| } |
| if enum.Type.IsSigned() { |
| return 0, fmt.Errorf("cannot retrieve unsigned unknown value of signed flexible enum") |
| } |
| return enum.RawUnknownValue.readUint64(), nil |
| } |
| |
| // UnknownValueForTmpl retrieves the signed or unsigned unknown value. Panics |
| // if called on a strict enum. |
| func (enum *Enum) UnknownValueForTmpl() interface{} { |
| if enum.Type.IsSigned() { |
| unknownValue, err := enum.UnknownValueAsInt64() |
| if err != nil { |
| panic(err) |
| } |
| return unknownValue |
| } |
| |
| unknownValue, err := enum.UnknownValueAsUint64() |
| if err != nil { |
| panic(err) |
| } |
| return unknownValue |
| } |
| |
| // EnumMember represents a single variant in a FIDL enum. |
| type EnumMember struct { |
| Attributes |
| Name Identifier `json:"name"` |
| Value Constant `json:"value"` |
| } |
| |
| // IsUnknown indicates whether this member represents a custom unknown flexible |
| // enum member. |
| func (member *EnumMember) IsUnknown() bool { |
| return member.HasAttribute("Unknown") |
| } |
| |
| // Bits represents a FIDL declaration of an bits. |
| type Bits struct { |
| Layout |
| Type Type `json:"type"` |
| Mask string `json:"mask"` |
| Members []BitsMember `json:"members"` |
| Strictness `json:"strict"` |
| } |
| |
| // BitsMember represents a single variant in a FIDL bits. |
| type BitsMember struct { |
| Attributes |
| Name Identifier `json:"name"` |
| Value Constant `json:"value"` |
| } |
| |
| // Const represents a FIDL declaration of a named constant. |
| type Const struct { |
| Decl |
| Type Type `json:"type"` |
| Value Constant `json:"value"` |
| } |
| |
| // Strictness represents whether a FIDL object is strict or flexible. See |
| // <https://fuchsia.dev/fuchsia-src/development/languages/fidl/reference/ftp/ftp-033> for more |
| // information. |
| type Strictness bool |
| |
| const ( |
| IsFlexible Strictness = false |
| IsStrict Strictness = true |
| ) |
| |
| // IsStrict indicates whether this type is strict. |
| func (s Strictness) IsStrict() bool { |
| return s == IsStrict |
| } |
| |
| // IsFlexible indicates whether this type is flexible. |
| func (s Strictness) IsFlexible() bool { |
| return s == IsFlexible |
| } |
| |
| // Resourceness represents whether a FIDL object may contain any resource types, |
| // such as handles. See https://fuchsia.dev/fuchsia-src/contribute/governance/fidl/ftp/ftp-057 |
| // for more information. |
| type Resourceness bool |
| |
| const ( |
| IsResourceType Resourceness = true |
| IsValueType Resourceness = false |
| ) |
| |
| // IsResourceType indicates whether this type is marked as a resource type |
| func (r Resourceness) IsResourceType() bool { |
| return r == IsResourceType |
| } |
| |
| // IsValueType indicates whether this type is not marked as a resource type |
| func (r Resourceness) IsValueType() bool { |
| return r == IsValueType |
| } |
| |
| type DeclType string |
| |
| const ( |
| LibraryDeclType DeclType = "library" |
| |
| ConstDeclType DeclType = "const" |
| BitsDeclType DeclType = "bits" |
| EnumDeclType DeclType = "enum" |
| ProtocolDeclType DeclType = "protocol" |
| ServiceDeclType DeclType = "service" |
| StructDeclType DeclType = "struct" |
| TableDeclType DeclType = "table" |
| UnionDeclType DeclType = "union" |
| TypeAliasDeclType DeclType = "type_alias" |
| ) |
| |
| type DeclInfo struct { |
| Type DeclType `json:"kind"` |
| // Present for structs, tables, and unions. |
| *Resourceness `json:"resource,omitempty"` |
| } |
| |
| type DeclMap map[EncodedCompoundIdentifier]DeclType |
| type DeclInfoMap map[EncodedCompoundIdentifier]DeclInfo |
| |
| func (dt DeclType) IsPrimitive() bool { |
| switch dt { |
| case BitsDeclType, EnumDeclType: |
| return true |
| } |
| |
| return false |
| } |
| |
| // Library represents a FIDL dependency on a separate library. |
| type Library struct { |
| Name EncodedLibraryIdentifier `json:"name,omitempty"` |
| Decls DeclInfoMap `json:"declarations,omitempty"` |
| } |
| |
| // Root is the top-level object for a FIDL library. |
| // It contains lists of all declarations and dependencies within the library. |
| type Root struct { |
| Name EncodedLibraryIdentifier `json:"name,omitempty"` |
| Consts []Const `json:"const_declarations,omitempty"` |
| Bits []Bits `json:"bits_declarations,omitempty"` |
| Enums []Enum `json:"enum_declarations,omitempty"` |
| Protocols []Protocol `json:"protocol_declarations,omitempty"` |
| Services []Service `json:"service_declarations,omitempty"` |
| Structs []Struct `json:"struct_declarations,omitempty"` |
| ExternalStructs []Struct `json:"external_struct_declarations,omitempty"` |
| Tables []Table `json:"table_declarations,omitempty"` |
| Unions []Union `json:"union_declarations,omitempty"` |
| TypeAliases []TypeAlias `json:"type_alias_declarations,omitempty"` |
| DeclOrder []EncodedCompoundIdentifier `json:"declaration_order,omitempty"` |
| Decls DeclMap `json:"declarations,omitempty"` |
| Libraries []Library `json:"library_dependencies,omitempty"` |
| declarations map[EncodedCompoundIdentifier]Declaration |
| } |
| |
| func (r *Root) initializeDeclarationsMap() { |
| r.declarations = make(map[EncodedCompoundIdentifier]Declaration) |
| for i, d := range r.Consts { |
| r.declarations[d.Name] = &r.Consts[i] |
| } |
| for i, d := range r.Bits { |
| r.declarations[d.Name] = &r.Bits[i] |
| } |
| for i, d := range r.Enums { |
| r.declarations[d.Name] = &r.Enums[i] |
| } |
| for i, d := range r.Protocols { |
| r.declarations[d.Name] = &r.Protocols[i] |
| } |
| for i, d := range r.Services { |
| r.declarations[d.Name] = &r.Services[i] |
| } |
| for i, d := range r.Structs { |
| r.declarations[d.Name] = &r.Structs[i] |
| } |
| for i, d := range r.Tables { |
| r.declarations[d.Name] = &r.Tables[i] |
| } |
| for i, d := range r.Unions { |
| r.declarations[d.Name] = &r.Unions[i] |
| } |
| for i, d := range r.ExternalStructs { |
| r.declarations[d.Name] = &r.ExternalStructs[i] |
| } |
| } |
| |
| // DeclsWithDependencies returns a single DeclInfoMap containing the FIDL |
| // library's declarations and those of its dependencies. |
| func (r *Root) DeclsWithDependencies() DeclInfoMap { |
| resourceness := make(map[EncodedCompoundIdentifier]Resourceness, len(r.Structs)+len(r.Tables)+len(r.Unions)) |
| for _, v := range r.Structs { |
| resourceness[v.Name] = v.Resourceness |
| } |
| for _, v := range r.Tables { |
| resourceness[v.Name] = v.Resourceness |
| } |
| for _, v := range r.Unions { |
| resourceness[v.Name] = v.Resourceness |
| } |
| decls := DeclInfoMap{} |
| for k, v := range r.Decls { |
| ptr := new(Resourceness) |
| *ptr = resourceness[k] |
| decls[k] = DeclInfo{Type: v, Resourceness: ptr} |
| } |
| for _, l := range r.Libraries { |
| for k, v := range l.Decls { |
| decls[k] = v |
| } |
| } |
| return decls |
| } |
| |
| type EncodedCompoundIdentifierSet map[EncodedCompoundIdentifier]struct{} |
| |
| // GetMessageBodyTypeNames calculates set of ECIs that refer to types used as |
| // message bodies by this library. |
| func (r *Root) GetMessageBodyTypeNames() EncodedCompoundIdentifierSet { |
| mbtn := EncodedCompoundIdentifierSet{} |
| for _, protocol := range r.Protocols { |
| for _, method := range protocol.Methods { |
| if method.RequestPayload != nil { |
| mbtn[method.RequestPayload.Identifier] = struct{}{} |
| } |
| if method.ResponsePayload != nil { |
| mbtn[method.ResponsePayload.Identifier] = struct{}{} |
| } |
| } |
| } |
| return mbtn |
| } |
| |
| // payloadTypeNames calculates set of ECIs that refer to types used user-defined |
| // payloads by this library. Specifically, for the following FIDL method |
| // definition: |
| // |
| // MyMethod(struct{...}) -> (struct{...}) error uint32; |
| // |-----A-----| |-----B-----| |-----C-----| |
| // |------------D------------| |
| // |
| // types `A` and `B` are payloads, but `C` (the error) and `D` (the result) are |
| // not. If the `error` syntax is not used, the message body type name and |
| // payload type name sets for a method are identical. |
| func (r *Root) payloadTypeNames() EncodedCompoundIdentifierSet { |
| ptn := EncodedCompoundIdentifierSet{} |
| for _, protocol := range r.Protocols { |
| for _, method := range protocol.Methods { |
| if method.RequestPayload != nil { |
| ptn[method.RequestPayload.Identifier] = struct{}{} |
| } |
| if method.ResponsePayload != nil { |
| if method.HasError || method.HasTransportError() { |
| ptn[method.ValueType.Identifier] = struct{}{} |
| } else { |
| ptn[method.ResponsePayload.Identifier] = struct{}{} |
| } |
| } |
| } |
| } |
| return ptn |
| } |
| |
| // MethodTypeUsage is an enum that stores whether a type referenced by a payload |
| // is used only as a payload parameter list, a wire message shape, or both. For |
| // example, consider the following FIDL method: |
| // |
| // MyMethod(struct{...}) -> (struct{...}) error uint32; |
| // |-----A-----| |-----B-----| |-----C-----| |
| // |------------D------------| |
| // |
| // Types `B` is `OnlyPayloadMethodTypeUsage` (it is exposed to the user, but |
| // never sent over the wire), type `D` is `OnlyWireMethodTypeUsage` (it |
| // describes the shape of a message body sent over the wire, but the wrapper |
| // struct is never exposed to the user as a payload that they may send) and type |
| // `A` is `BothMethodTypeUsage` (it is both exposed to the user, via the |
| // signature of the request-sending function, and describes the shape of the |
| // message sent over the wire). |
| type MethodTypeUsage string |
| |
| const ( |
| UsedOnlyAsPayload MethodTypeUsage = "asOnlyPayload" |
| UsedOnlyAsMessageBody MethodTypeUsage = "asOnlyWire" |
| UsedBothAsPayloadAndMessageBody MethodTypeUsage = "asBoth" |
| ) |
| |
| // MethodTypeUsageMap maps the names of types referenced by methods (ResultType, |
| // ValueType, ResponsePayload) to the MethodTypeUsage exhibited by that type. |
| type MethodTypeUsageMap map[EncodedCompoundIdentifier]MethodTypeUsage |
| |
| // MethodTypeUsageMap creates a map from the names of all non-error types |
| // references by methods to their MethodTypeUsage. |
| func (r *Root) MethodTypeUsageMap() MethodTypeUsageMap { |
| out := MethodTypeUsageMap{} |
| mbtn := r.GetMessageBodyTypeNames() |
| ptn := r.payloadTypeNames() |
| for name := range mbtn { |
| if _, ok := ptn[name]; ok { |
| out[name] = UsedBothAsPayloadAndMessageBody |
| delete(ptn, name) |
| } else { |
| out[name] = UsedOnlyAsMessageBody |
| } |
| } |
| for name := range ptn { |
| out[name] = UsedOnlyAsPayload |
| } |
| return out |
| } |
| |
| // deniedContexts produces a list of scopedNamingContexts. Any types/methods that begin with the |
| // scopedNamingContext in that list should be denied as well when run through the isDenied() |
| // function. |
| func deniedContexts(r *Root, language string) []scopedNamingContext { |
| var denied []scopedNamingContext |
| |
| for _, v := range r.Bits { |
| if v.BindingsDenylistIncludes(language) { |
| denied = append(denied, scopedNamingContext{v.Name.LibraryName(), v.NamingContext}) |
| } |
| } |
| for _, v := range r.Enums { |
| if v.BindingsDenylistIncludes(language) { |
| denied = append(denied, scopedNamingContext{v.Name.LibraryName(), v.NamingContext}) |
| } |
| } |
| for _, v := range r.Protocols { |
| protocolName := string(v.Name.Parse().Name) |
| if v.BindingsDenylistIncludes(language) { |
| denied = append(denied, scopedNamingContext{v.Name.LibraryName(), []string{protocolName}}) |
| } else { |
| for _, m := range v.Methods { |
| if m.BindingsDenylistIncludes(language) { |
| denied = append(denied, scopedNamingContext{ |
| v.Name.LibraryName(), |
| []string{protocolName, string(m.Name)}, |
| }) |
| } |
| } |
| } |
| } |
| for _, v := range r.Structs { |
| if v.BindingsDenylistIncludes(language) { |
| denied = append(denied, scopedNamingContext{v.Name.LibraryName(), v.NamingContext}) |
| } |
| } |
| for _, v := range r.ExternalStructs { |
| if v.BindingsDenylistIncludes(language) { |
| denied = append(denied, scopedNamingContext{v.Name.LibraryName(), v.NamingContext}) |
| } |
| } |
| for _, v := range r.Tables { |
| if v.BindingsDenylistIncludes(language) { |
| denied = append(denied, scopedNamingContext{v.Name.LibraryName(), v.NamingContext}) |
| } |
| } |
| for _, v := range r.Unions { |
| if v.BindingsDenylistIncludes(language) { |
| denied = append(denied, scopedNamingContext{v.Name.LibraryName(), v.NamingContext}) |
| } |
| } |
| |
| return denied |
| } |
| |
| // ForBindings filters out declarations that should be omitted in the given |
| // language bindings based on BindingsDenylist attributes. It returns a new Root |
| // and does not modify r. |
| func (r *Root) ForBindings(language string) Root { |
| denied := deniedContexts(r, language) |
| res := Root{ |
| Name: r.Name, |
| Libraries: r.Libraries, |
| Decls: make(DeclMap, len(r.Decls)), |
| } |
| |
| for _, v := range r.Consts { |
| if !v.BindingsDenylistIncludes(language) { |
| res.Consts = append(res.Consts, v) |
| res.Decls[v.Name] = r.Decls[v.Name] |
| } |
| } |
| for _, v := range r.Bits { |
| if !v.BindingsDenylistIncludes(language) && !(scopedNamingContext{r.Name, v.NamingContext}.isDenied(denied)) { |
| newV := v |
| newV.Members = nil |
| for _, m := range v.Members { |
| if !m.BindingsDenylistIncludes(language) { |
| newV.Members = append(newV.Members, m) |
| } |
| } |
| res.Bits = append(res.Bits, newV) |
| res.Decls[v.Name] = r.Decls[v.Name] |
| } |
| } |
| for _, v := range r.Enums { |
| if !v.BindingsDenylistIncludes(language) && !(scopedNamingContext{r.Name, v.NamingContext}.isDenied(denied)) { |
| newV := v |
| newV.Members = nil |
| for _, m := range v.Members { |
| if !m.BindingsDenylistIncludes(language) { |
| newV.Members = append(newV.Members, m) |
| } |
| } |
| res.Enums = append(res.Enums, newV) |
| res.Decls[v.Name] = r.Decls[v.Name] |
| } |
| } |
| for _, v := range r.Protocols { |
| if !v.BindingsDenylistIncludes(language) { |
| newV := v |
| newV.Methods = nil |
| for _, m := range v.Methods { |
| nc := NamingContext{string(v.Name), string(m.Name)} |
| if !m.BindingsDenylistIncludes(language) && !(scopedNamingContext{r.Name, nc}.isDenied(denied)) { |
| newV.Methods = append(newV.Methods, m) |
| } |
| } |
| res.Protocols = append(res.Protocols, newV) |
| res.Decls[v.Name] = r.Decls[v.Name] |
| } |
| } |
| for _, v := range r.Services { |
| if !v.BindingsDenylistIncludes(language) { |
| newV := v |
| newV.Members = nil |
| for _, m := range v.Members { |
| if !m.BindingsDenylistIncludes(language) { |
| newV.Members = append(newV.Members, m) |
| } |
| } |
| res.Services = append(res.Services, newV) |
| res.Decls[v.Name] = r.Decls[v.Name] |
| } |
| } |
| for _, v := range r.Structs { |
| if !v.BindingsDenylistIncludes(language) && !(scopedNamingContext{r.Name, v.NamingContext}.isDenied(denied)) { |
| newV := v |
| newV.Members = nil |
| for _, m := range v.Members { |
| if !m.BindingsDenylistIncludes(language) { |
| newV.Members = append(newV.Members, m) |
| } |
| } |
| res.Structs = append(res.Structs, newV) |
| res.Decls[v.Name] = r.Decls[v.Name] |
| } |
| } |
| for _, v := range r.ExternalStructs { |
| if !v.BindingsDenylistIncludes(language) && !(scopedNamingContext{r.Name, v.NamingContext}.isDenied(denied)) { |
| newV := v |
| newV.Members = nil |
| for _, m := range v.Members { |
| if !m.BindingsDenylistIncludes(language) { |
| newV.Members = append(newV.Members, m) |
| } |
| } |
| res.ExternalStructs = append(res.ExternalStructs, newV) |
| } |
| } |
| for _, v := range r.Tables { |
| if !v.BindingsDenylistIncludes(language) && !(scopedNamingContext{r.Name, v.NamingContext}.isDenied(denied)) { |
| newV := v |
| newV.Members = nil |
| for _, m := range v.Members { |
| if !m.BindingsDenylistIncludes(language) { |
| newV.Members = append(newV.Members, m) |
| } else { |
| newV.Members = append(newV.Members, TableMember{ |
| Attributes: m.Attributes, |
| Reserved: true, |
| Name: m.Name, |
| Ordinal: m.Ordinal, |
| }) |
| } |
| } |
| res.Tables = append(res.Tables, newV) |
| res.Decls[v.Name] = r.Decls[v.Name] |
| } |
| } |
| for _, v := range r.Unions { |
| if !v.BindingsDenylistIncludes(language) && !(scopedNamingContext{r.Name, v.NamingContext}.isDenied(denied)) { |
| newV := v |
| newV.Members = nil |
| for _, m := range v.Members { |
| if !m.BindingsDenylistIncludes(language) { |
| newV.Members = append(newV.Members, m) |
| } else { |
| newV.Members = append(newV.Members, UnionMember{ |
| Attributes: m.Attributes, |
| Reserved: true, |
| Name: m.Name, |
| Ordinal: m.Ordinal, |
| }) |
| } |
| } |
| res.Unions = append(res.Unions, newV) |
| res.Decls[v.Name] = r.Decls[v.Name] |
| } |
| } |
| for _, v := range r.TypeAliases { |
| if !v.BindingsDenylistIncludes(language) { |
| res.TypeAliases = append(res.TypeAliases, v) |
| res.Decls[v.Name] = r.Decls[v.Name] |
| } |
| } |
| |
| for _, d := range r.DeclOrder { |
| if _, ok := res.Decls[d]; ok { |
| res.DeclOrder = append(res.DeclOrder, d) |
| } |
| } |
| |
| r.initializeDeclarationsMap() |
| |
| return res |
| } |
| |
| func (r *Root) LookupDecl(i EncodedCompoundIdentifier) Declaration { |
| return r.declarations[i] |
| } |
| |
| type int64OrUint64 struct { |
| i int64 |
| u uint64 |
| } |
| |
| func (n *int64OrUint64) readInt64() int64 { |
| if n.i != 0 { |
| return n.i |
| } |
| return int64(n.u) |
| } |
| |
| func (n *int64OrUint64) readUint64() uint64 { |
| if n.i != 0 { |
| return uint64(n.i) |
| } |
| return n.u |
| } |
| |
| func Int64OrUint64FromInt64ForTesting(val int64) int64OrUint64 { |
| if val >= 0 { |
| return int64OrUint64{0, uint64(val)} |
| } |
| return int64OrUint64{val, 0} |
| } |
| |
| func Int64OrUint64FromUint64ForTesting(val uint64) int64OrUint64 { |
| return int64OrUint64{0, val} |
| } |
| |
| var _ json.Unmarshaler = (*int64OrUint64)(nil) |
| |
| func (n *int64OrUint64) UnmarshalJSON(data []byte) error { |
| if u, err := strconv.ParseUint(string(data), 10, 64); err == nil { |
| n.u = u |
| return nil |
| } |
| if i, err := strconv.ParseInt(string(data), 10, 64); err == nil { |
| n.i = i |
| return nil |
| } |
| return fmt.Errorf("%s not representable as int64 or uint64", string(data)) |
| } |