| // 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 ( |
| "encoding/json" |
| "fmt" |
| "io/ioutil" |
| "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) { |
| bytes, err := ioutil.ReadFile(filename) |
| if err != nil { |
| return Root{}, fmt.Errorf("Error reading from %s: %w", filename, err) |
| } |
| return ReadJSONIrContent(bytes) |
| } |
| |
| // ReadJSONIrContent reads JSON IR content. |
| func ReadJSONIrContent(bytes []byte) (Root, error) { |
| var root Root |
| |
| if err := json.Unmarshal(bytes, &root); err != nil { |
| return root, fmt.Errorf("Error parsing JSON IR: %v", err) |
| } |
| |
| // TODO(fxbug.dev/50195): This is for backward compatibility with fidlgen_dart in |
| // Topaz, and should be removed after fidlgen_dart code has been updated. |
| root.Interfaces = root.Protocols |
| |
| return root, nil |
| } |
| |
| type Identifier string |
| |
| type LibraryIdentifier []Identifier |
| |
| type CompoundIdentifier struct { |
| Library LibraryIdentifier |
| Name Identifier |
| Member Identifier |
| } |
| |
| type EncodedLibraryIdentifier string |
| |
| type EncodedCompoundIdentifier string |
| |
| func (eli EncodedLibraryIdentifier) Parts() LibraryIdentifier { |
| return ParseLibraryName(eli) |
| } |
| |
| 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 |
| } |
| |
| func (eci EncodedCompoundIdentifier) Parts() CompoundIdentifier { |
| return ParseCompoundIdentifier(eci) |
| } |
| |
| func (eci EncodedCompoundIdentifier) LibraryName() EncodedLibraryIdentifier { |
| parts := strings.SplitN(string(eci), "/", 2) |
| raw_library := "" |
| if len(parts) == 2 { |
| raw_library = parts[0] |
| } |
| return EncodedLibraryIdentifier(raw_library) |
| } |
| |
| func ParseLibraryName(eli EncodedLibraryIdentifier) LibraryIdentifier { |
| raw_parts := strings.Split(string(eli), ".") |
| parts := make([]Identifier, len(raw_parts)) |
| for i, raw_part := range raw_parts { |
| parts[i] = Identifier(raw_part) |
| } |
| return LibraryIdentifier(parts) |
| } |
| |
| func ParseCompoundIdentifier(eci EncodedCompoundIdentifier) CompoundIdentifier { |
| parts := strings.SplitN(string(eci), "/", 2) |
| raw_library := "" |
| raw_name := parts[0] |
| if len(parts) == 2 { |
| raw_library = parts[0] |
| raw_name = parts[1] |
| } |
| library := ParseLibraryName(EncodedLibraryIdentifier(raw_library)) |
| 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} |
| } |
| |
| func EnsureLibrary(l EncodedLibraryIdentifier, eci EncodedCompoundIdentifier) EncodedCompoundIdentifier { |
| if strings.Index(string(eci), "/") != -1 { |
| return eci |
| } |
| new_eci := strings.Join([]string{string(l), "/", string(eci)}, "") |
| return EncodedCompoundIdentifier(new_eci) |
| } |
| |
| 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" |
| ) |
| |
| // Copied from third_party/go/src/syscall/zx/types.go |
| type ObjectType uint32 // zx_obj_type_t |
| 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 |
| ) |
| |
| // TODO(fxbug.dev/45998): emit the numeric value of the subtype in fidlc |
| func ObjectTypeFromHandleSubtype(val HandleSubtype) ObjectType { |
| switch val { |
| case Bti: |
| return ObjectTypeBti |
| case Channel: |
| return ObjectTypeChannel |
| case DebugLog: |
| return ObjectTypeLog |
| case Event: |
| return ObjectTypeEvent |
| case Eventpair: |
| return ObjectTypeEventPair |
| 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 |
| |
| type LiteralKind string |
| |
| const ( |
| StringLiteral LiteralKind = "string" |
| NumericLiteral LiteralKind = "numeric" |
| TrueLiteral LiteralKind = "true" |
| FalseLiteral LiteralKind = "false" |
| 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" |
| ) |
| |
| type Constant struct { |
| Kind ConstantKind `json:"kind"` |
| Identifier EncodedCompoundIdentifier `json:"identifier,omitempty"` |
| Literal Literal `json:"literal,omitempty"` |
| } |
| |
| 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 |
| } |
| |
| // 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 |
| } |
| |
| 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 |
| } |
| 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 |
| } |
| 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 |
| } |
| default: |
| return fmt.Errorf("Unknown type kind: %s", t.Kind) |
| } |
| |
| return nil |
| } |
| |
| type Attribute struct { |
| Name Identifier `json:"name"` |
| Value string `json:"value"` |
| } |
| |
| // 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 a.Name == name { |
| return a, true |
| } |
| } |
| return Attribute{}, false |
| } |
| |
| func (el Attributes) HasAttribute(name Identifier) bool { |
| _, ok := el.LookupAttribute(name) |
| return ok |
| } |
| |
| func (el Attributes) GetAttribute(name Identifier) Attribute { |
| attr, _ := el.LookupAttribute(name) |
| return attr |
| } |
| |
| func (el Attributes) DocComments() []string { |
| doc, ok := el.LookupAttribute("Doc") |
| if !ok || doc.Value == "" { |
| return nil |
| } |
| return strings.Split(doc.Value[0:len(doc.Value)-1], "\n") |
| } |
| |
| func (el Attributes) Transports() map[string]struct{} { |
| transports := make(map[string]struct{}) |
| raw, ok := el.LookupAttribute("Transport") |
| if ok && raw.Value != "" { |
| for _, transport := range strings.Split(raw.Value, ",") { |
| transports[strings.TrimSpace(transport)] = struct{}{} |
| } |
| } |
| // No transport attribute => just Channel |
| if !ok { |
| transports["Channel"] = struct{}{} |
| } |
| return transports |
| } |
| |
| // BindingsDenylistIncludes returns true if the comma-separated BindingsDenyList |
| // attribute includes targetLanguage (meaning the bindings for targetLanguage |
| // should not emit this declaration). |
| func (el Attributes) BindingsDenylistIncludes(targetLanguage string) bool { |
| raw, ok := el.LookupAttribute("BindingsDenylist") |
| if ok && raw.Value != "" { |
| for _, language := range strings.Split(raw.Value, ",") { |
| 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"` |
| 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"` |
| } |
| |
| // Union represents the declaration of a FIDL union. |
| type Union struct { |
| Attributes |
| Name EncodedCompoundIdentifier `json:"name"` |
| Members []UnionMember `json:"members"` |
| Strictness `json:"strict"` |
| Resourceness `json:"resource"` |
| TypeShapeV1 TypeShape `json:"type_shape_v1"` |
| } |
| |
| // 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 { |
| Attributes |
| Name EncodedCompoundIdentifier `json:"name"` |
| Members []TableMember `json:"members"` |
| Resourceness `json:"resource"` |
| TypeShapeV1 TypeShape `json:"type_shape_v1"` |
| } |
| |
| // 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 { |
| Attributes |
| Name EncodedCompoundIdentifier `json:"name"` |
| Anonymous bool `json:"anonymous"` |
| Members []StructMember `json:"members"` |
| Resourceness `json:"resource"` |
| TypeShapeV1 TypeShape `json:"type_shape_v1"` |
| } |
| |
| // 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"` |
| } |
| |
| // 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", |
| }, |
| }, |
| } |
| } |
| |
| // Protocol represents the declaration of a FIDL protocol. |
| type Protocol struct { |
| Attributes |
| Name EncodedCompoundIdentifier `json:"name"` |
| Methods []Method `json:"methods"` |
| } |
| |
| // TODO(fxbug.dev/50195): This is for backward compatibility with fidlgen_dart in |
| // Topaz, and should be removed after fidlgen_dart code has been updated. |
| type Interface = Protocol |
| |
| func (d *Protocol) GetServiceName() string { |
| _, found := d.LookupAttribute("Discoverable") |
| if found { |
| ci := ParseCompoundIdentifier(d.Name) |
| var parts []string |
| for _, i := range ci.Library { |
| parts = append(parts, string(i)) |
| } |
| parts = append(parts, string(ci.Name)) |
| return fmt.Sprintf("\"%s\"", strings.Join(parts, ".")) |
| } |
| return "" |
| } |
| |
| // Service represents the declaration of a FIDL service. |
| type Service struct { |
| Attributes |
| Name EncodedCompoundIdentifier `json:"name"` |
| Members []ServiceMember `json:"members"` |
| } |
| |
| func (s *Service) GetServiceName() string { |
| ci := ParseCompoundIdentifier(s.Name) |
| 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 |
| Ordinal uint64 `json:"ordinal"` |
| Name Identifier `json:"name"` |
| HasRequest bool `json:"has_request"` |
| Request []Parameter `json:"maybe_request,omitempty"` |
| RequestPayload EncodedCompoundIdentifier `json:"maybe_request_payload,omitempty"` |
| RequestTypeShapeV1 TypeShape `json:"maybe_request_type_shape_v1,omitempty"` |
| RequestPadding bool `json:"maybe_request_has_padding,omitempty"` |
| RequestFlexible bool `json:"experimental_maybe_request_has_flexible_envelope,omitempty"` |
| HasResponse bool `json:"has_response"` |
| Response []Parameter `json:"maybe_response,omitempty"` |
| ResponsePayload EncodedCompoundIdentifier `json:"maybe_response_payload,omitempty"` |
| ResponseTypeShapeV1 TypeShape `json:"maybe_response_type_shape_v1,omitempty"` |
| ResponsePadding bool `json:"maybe_response_has_padding,omitempty"` |
| ResponseFlexible bool `json:"experimental_maybe_response_has_flexible_envelope,omitempty"` |
| } |
| |
| // IsTransitional returns whether this method has the `Transitional` attribute. |
| func (m *Method) IsTransitional() bool { |
| _, transitional := m.LookupAttribute("Transitional") |
| return transitional |
| } |
| |
| // Parameter represents a parameter to a FIDL method. |
| type Parameter struct { |
| Type Type `json:"type"` |
| Name Identifier `json:"name"` |
| MaxHandles int `json:"max_handles"` |
| MaxOutOfLine int `json:"max_out_of_line"` |
| FieldShapeV1 FieldShape `json:"field_shape_v1"` |
| } |
| |
| // Enum represents a FIDL declaration of an enum. |
| type Enum struct { |
| Attributes |
| Type PrimitiveSubtype `json:"type"` |
| Name EncodedCompoundIdentifier `json:"name"` |
| 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 { |
| Attributes |
| Type Type `json:"type"` |
| Name EncodedCompoundIdentifier `json:"name"` |
| 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 { |
| Attributes |
| Type Type `json:"type"` |
| Name EncodedCompoundIdentifier `json:"name"` |
| 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 ( |
| ConstDeclType DeclType = "const" |
| BitsDeclType DeclType = "bits" |
| EnumDeclType DeclType = "enum" |
| ProtocolDeclType DeclType = "interface" |
| ServiceDeclType DeclType = "service" |
| StructDeclType DeclType = "struct" |
| TableDeclType DeclType = "table" |
| UnionDeclType DeclType = "union" |
| |
| // TODO(fxbug.dev/50195): This is for backward compatibility with fidlgen_dart in |
| // Topaz, and should be removed after fidlgen_dart code has been updated. |
| InterfaceDeclType DeclType = "interface" |
| ) |
| |
| 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:"interface_declarations,omitempty"` |
| Services []Service `json:"service_declarations,omitempty"` |
| Structs []Struct `json:"struct_declarations,omitempty"` |
| Tables []Table `json:"table_declarations,omitempty"` |
| Unions []Union `json:"union_declarations,omitempty"` |
| DeclOrder []EncodedCompoundIdentifier `json:"declaration_order,omitempty"` |
| Decls DeclMap `json:"declarations,omitempty"` |
| Libraries []Library `json:"library_dependencies,omitempty"` |
| |
| // TODO(fxbug.dev/50195): This is for backward compatibility with fidlgen_dart in |
| // Topaz, and should be removed after fidlgen_dart code has been updated. |
| Interfaces []Protocol |
| } |
| |
| // 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[EnsureLibrary(l.Name, k)] = v |
| } |
| } |
| return decls |
| } |
| |
| // 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 { |
| 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) { |
| 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) { |
| 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 { |
| if !m.BindingsDenylistIncludes(language) { |
| 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) { |
| 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.Tables { |
| if !v.BindingsDenylistIncludes(language) { |
| 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) { |
| 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 _, d := range r.DeclOrder { |
| if _, ok := res.Decls[d]; ok { |
| res.DeclOrder = append(res.DeclOrder, d) |
| } |
| } |
| |
| return res |
| } |
| |
| 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 |
| } |
| |
| 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)) |
| } |