blob: 4b2dbf8e2e7d1e0a1c1ab3643d05219198586c9f [file] [log] [blame]
// 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))
}