blob: 4e82e12396fff26f9ededb9ddb4dd8bcd00646de [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 types
import (
"encoding/json"
"fmt"
"strings"
)
/*
This file contains types which describe FIDL interfaces.
These types are intended to be directly deserialized from the FIDL interface
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.
*/
type Identifier string
type LibraryIdentifier []Identifier
type CompoundIdentifier struct {
Library LibraryIdentifier
Name 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 := Identifier(raw_name)
return CompoundIdentifier{library, name}
}
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 Ordinal uint32
type PrimitiveSubtype string
const (
Bool PrimitiveSubtype = "bool"
Int8 = "int8"
Int16 = "int16"
Int32 = "int32"
Int64 = "int64"
Uint8 = "uint8"
Uint16 = "uint16"
Uint32 = "uint32"
Uint64 = "uint64"
Float32 = "float32"
Float64 = "float64"
)
type HandleSubtype string
const (
Handle HandleSubtype = "handle"
Process = "process"
Thread = "thread"
Vmo = "vmo"
Channel = "channel"
Event = "event"
Port = "port"
Interrupt = "interrupt"
Log = "debuglog"
Socket = "socket"
Resource = "resource"
Eventpair = "eventpair"
Job = "job"
Vmar = "vmar"
Fifo = "fifo"
Guest = "guest"
Time = "timer"
)
type LiteralKind string
const (
StringLiteral LiteralKind = "string"
NumericLiteral = "numeric"
TrueLiteral = "true"
FalseLiteral = "false"
DefaultLiteral = "default"
)
type Literal struct {
Kind LiteralKind `json:"kind"`
Value string `json:"value,omitempty"`
}
type ConstantKind string
const (
IdentifierConstant ConstantKind = "identifier"
LiteralConstant = "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 = "vector"
StringType = "string"
HandleType = "handle"
RequestType = "request"
PrimitiveType = "primitive"
IdentifierType = "identifier"
)
type Type struct {
Kind TypeKind
ElementType *Type
ElementCount *int
HandleSubtype HandleSubtype
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["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` interface, such that it can be embedded into other ndoe 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) 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")
}
// Union represents the declaration of a FIDL union.
type Union struct {
Attributes
Name EncodedCompoundIdentifier `json:"name"`
Members []UnionMember `json:"members"`
Size int `json:"size"`
Alignment int `json:"alignment"`
MaxHandles int `json:"max_handles"`
MaxOutOfLine int `json:"max_out_of_line"`
}
// UnionMember represents the declaration of a field in a FIDL union.
type UnionMember struct {
Attributes
Type Type `json:"type"`
Name Identifier `json:"name"`
Offset int `json:"offset"`
MaxOutOfLine int `json:"max_out_of_line"`
}
// XUnion represents the declaration of a FIDL extensible union.
type XUnion struct {
Attributes
Name EncodedCompoundIdentifier `json:"name"`
Members []XUnionMember `json:"members"`
Size int `json:"size"`
Alignment int `json:"alignment"`
MaxHandles int `json:"max_handles"`
MaxOutOfLine int `json:"max_out_of_line"`
}
// XUnionMember represents the declaration of a field in a FIDL extensible
// xunion.
type XUnionMember struct {
Attributes
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"`
Size int `json:"size"`
Alignment int `json:"alignment"`
MaxHandles int `json:"max_handles"`
MaxOutOfLine int `json:"max_out_of_line"`
}
// 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"`
}
// Struct represents a declaration of a FIDL struct.
type Struct struct {
Attributes
Name EncodedCompoundIdentifier `json:"name"`
Members []StructMember `json:"members"`
Size int `json:"size"`
Alignment int `json:"alignment"`
MaxHandles int `json:"max_handles"`
MaxOutOfLine int `json:"max_out_of_line"`
}
// StructMember represents the declaration of a field in a FIDL struct.
type StructMember struct {
Attributes
Type Type `json:"type"`
Name Identifier `json:"name"`
Offset int `json:"offset"`
MaybeDefaultValue *Constant `json:"maybe_default_value,omitempty"`
MaxHandles int `json:"max_handles"`
MaxOutOfLine int `json:"max_out_of_line"`
}
// 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",
},
},
}
}
// Interface represents the declaration of a FIDL interface.
type Interface struct {
Attributes
Name EncodedCompoundIdentifier `json:"name"`
Methods []Method `json:"methods"`
}
func (d *Interface) 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 "\"" + strings.Join(parts, ".") + "\""
}
return ""
}
// Method represents the declaration of a FIDL method.
type Method struct {
Attributes
Ordinal Ordinal `json:"ordinal"`
GenOrdinal Ordinal `json:"generated_ordinal"`
Name Identifier `json:"name"`
HasRequest bool `json:"has_request"`
Request []Parameter `json:"maybe_request,omitempty"`
RequestSize int `json:"maybe_request_size,omitempty"`
HasResponse bool `json:"has_response"`
Response []Parameter `json:"maybe_response,omitempty"`
ResponseSize int `json:"maybe_response_size,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"`
Offset int `json:"offset"`
MaxHandles int `json:"max_handles"`
MaxOutOfLine int `json:"max_out_of_line"`
}
// Enum represents a FIDL declaration of an enum.
type Enum struct {
Attributes
Type PrimitiveSubtype `json:"type"`
Name EncodedCompoundIdentifier `json:"name"`
Members []EnumMember `json:"members"`
}
// EnumMember represents a single variant in a FIDL enum.
type EnumMember 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"`
}
type DeclType string
const (
ConstDeclType DeclType = "const"
EnumDeclType = "enum"
InterfaceDeclType = "interface"
StructDeclType = "struct"
TableDeclType = "table"
UnionDeclType = "union"
XUnionDeclType = "xunion"
)
type DeclMap map[EncodedCompoundIdentifier]DeclType
// Library represents a FIDL dependency on a separate library.
type Library struct {
Name EncodedLibraryIdentifier `json:"name,omitempty"`
Decls DeclMap `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"`
Enums []Enum `json:"enum_declarations,omitempty"`
Interfaces []Interface `json:"interface_declarations,omitempty"`
Structs []Struct `json:"struct_declarations,omitempty"`
Tables []Table `json:"table_declarations,omitempty"`
Unions []Union `json:"union_declarations,omitempty"`
XUnions []XUnion `json:"xunion_declarations,omitempty"`
DeclOrder []EncodedCompoundIdentifier `json:"declaration_order,omitempty"`
Decls DeclMap `json:"declarations,omitempty"`
Libraries []Library `json:"library_dependencies,omitempty"`
}