blob: ccc95e00506c78488dabf77be60ff4c1835ab185 [file] [log] [blame] [edit]
// Copyright 2025 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 codegen
import (
"fmt"
"log"
"sort"
"strings"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
)
type PythonType struct {
fidlgen.Type
PythonName string
}
type PythonConst struct {
fidlgen.Const
PythonName string
PythonValue string
PythonType PythonType
}
type PythonBits struct {
fidlgen.Bits
PythonName string
PythonMembers []PythonBitsMember
Empty bool
}
type PythonBitsMember struct {
fidlgen.BitsMember
PythonName string
PythonValue string
}
type PythonEnum struct {
fidlgen.Enum
PythonName string
PythonMembers []PythonEnumMember
Empty bool
HasZero bool
}
type PythonEnumMember struct {
fidlgen.EnumMember
PythonName string
PythonValue string
}
type PythonTable struct {
fidlgen.Table
Library string
PythonName string
PythonMembers []PythonTableMember
}
type PythonTableMember struct {
fidlgen.TableMember
PythonType PythonType
PythonName string
}
type PythonStruct struct {
fidlgen.Struct
Library string
PythonName string
PythonMembers []PythonStructMember
}
type PythonStructMember struct {
fidlgen.StructMember
PythonType PythonType
PythonName string
}
type PythonUnion struct {
fidlgen.Union
Library string
PythonName string
PythonMembers []PythonUnionMember
PythonSuccessType *PythonType
}
type PythonUnionMember struct {
fidlgen.UnionMember
PythonType PythonType
PythonName string
}
type PythonAlias struct {
fidlgen.Alias
PythonAliasedName string
PythonName string
}
type UnsupportedMessage string
type PythonUnsupported struct {
Identifier fidlgen.EncodedCompoundIdentifier
PythonName string
Message []string
}
type PythonProtocol struct {
fidlgen.Protocol
Discoverable bool
Marker string
Library string
PythonName string
PythonMarkerName string
PythonClientName string
PythonServerName string
PythonEventHandlerName string
PythonMethods []PythonMethod
}
type PythonMethod struct {
fidlgen.Method
HasFrameworkError bool
PythonName string
PythonResponseAlias string
PythonRequestPayload *PythonPayload
PythonRequestIdent fidlgen.EncodedCompoundIdentifier
PythonResponsePayload *PythonPayload
PythonResponseSuccessPayload *PythonPayload
PythonResponseIdentifier fidlgen.EncodedCompoundIdentifier
EmptyResponse bool
}
type PythonPayload struct {
fidlgen.Type
DeclType fidlgen.DeclType
PythonType PythonType
PythonParameters []PythonParameter
}
type PythonParameter struct {
PythonType PythonType
PythonName string
PythonNoneDefault bool
}
type PythonRoot struct {
fidlgen.Root
PythonModuleName string
EmptySuccessStructs map[fidlgen.EncodedCompoundIdentifier]fidlgen.Struct
PythonTables map[fidlgen.EncodedCompoundIdentifier]PythonTable
PythonStructs map[fidlgen.EncodedCompoundIdentifier]PythonStruct
ExternalPythonStructs map[fidlgen.EncodedCompoundIdentifier]PythonStruct
PythonUnions map[fidlgen.EncodedCompoundIdentifier]PythonUnion
PythonAliases []PythonAlias
PythonConsts []PythonConst
PythonBits []PythonBits
PythonEnums []PythonEnum
PythonProtocols []PythonProtocol
PythonExternalModules []string
PythonUnsupportedTypes []PythonUnsupported
}
type compiler struct {
decls fidlgen.DeclInfoMap
library fidlgen.EncodedLibraryIdentifier
externalModules map[string]struct{}
PythonRoot PythonRoot
}
func (c *compiler) lookupDeclInfo(val fidlgen.EncodedCompoundIdentifier) *fidlgen.DeclInfo {
if info, ok := c.decls[val]; ok {
return &info
}
log.Fatalf("Identifier missing from DeclInfoMap: %v", val)
return nil
}
func compileCamelIdentifier(val fidlgen.Identifier) string {
return fidlgen.ToUpperCamelCase(string(val))
}
func (c *compiler) inExternalLibrary(eci fidlgen.EncodedCompoundIdentifier) bool {
return eci.LibraryName() != c.library
}
func ToPythonModuleName(eli fidlgen.EncodedLibraryIdentifier) string {
return fmt.Sprintf("fidl_%s", strings.Join(eli.Parts(), "_"))
}
func (c *compiler) compileDeclIdentifier(val fidlgen.EncodedCompoundIdentifier) *string {
ci := val.Parse()
if ci.Member != "" {
log.Fatalf("Non-empty Member implies this is not a declaration: %v", val)
}
var name string
if c.lookupDeclInfo(val).Type == fidlgen.ConstDeclType {
name = compileScreamingSnakeIdentifier(ci.Name)
} else {
name = compileCamelIdentifier(ci.Name)
}
if val.LibraryName() == c.library {
return &name
}
externalModule := ToPythonModuleName(val.LibraryName())
c.externalModules[externalModule] = struct{}{}
externalName := fmt.Sprintf("%s.%s", externalModule, name)
return &externalName
}
func compileSnakeIdentifier(val fidlgen.Identifier) string {
return fidlgen.ToSnakeCase(string(val))
}
func compileScreamingSnakeIdentifier(val fidlgen.Identifier) string {
return fidlgen.ConstNameToAllCapsSnake(string(val))
}
func (c *compiler) compileType(val fidlgen.Type, maybeAlias *fidlgen.PartialTypeConstructor) *PythonType {
if _, ok := c.PythonRoot.EmptySuccessStructs[val.Identifier]; ok {
return &PythonType{
Type: val,
PythonName: "None",
}
}
name := ""
if val.Nullable {
name = "typing.Optional["
}
switch val.Kind {
case fidlgen.IdentifierType:
// TODO(https://fxbug.dev/394421154): This should be changed to use the enum type itself
// when we start making breaking changes for these bindings.
identifier := *c.compileDeclIdentifier(val.Identifier)
switch c.decls[val.Identifier].Type {
case fidlgen.BitsDeclType, fidlgen.EnumDeclType:
name += fmt.Sprintf("int | %s", identifier)
default:
name += identifier
}
case fidlgen.PrimitiveType:
subtype := string(val.PrimitiveSubtype)
if strings.HasPrefix(subtype, "int") || strings.HasPrefix(subtype, "uint") {
name += "int"
} else if strings.HasPrefix(subtype, "float") {
name += "float"
} else if subtype == "bool" {
name += "bool"
} else {
log.Fatalf("Unknown primitive subtype: %v", val)
}
case fidlgen.StringType:
name += "str"
case fidlgen.HandleType:
name += "int"
case fidlgen.VectorType, fidlgen.ArrayType:
element_type_ptr := c.compileType(*val.ElementType, val.MaybeFromAlias)
if element_type_ptr == nil {
log.Fatalf("Element type not supported")
}
element_type := *element_type_ptr
name += fmt.Sprintf("typing.Sequence[%s]", element_type.PythonName)
case fidlgen.EndpointType:
switch val.Role {
case fidlgen.ClientRole, fidlgen.ServerRole:
name += "int"
default:
log.Fatalf("Unsupported endpoint role: %v", val)
}
case fidlgen.InternalType:
// TODO(https://fxbug.dev/42061151): Remove "transport_error".
switch val.InternalSubtype {
case "framework_error", "transport_error":
name += "fidl.FrameworkError"
default:
log.Fatalf("Unrecognized internal type: %v", val)
}
default:
log.Fatalf("Unknown kind: %v", val)
}
if val.Nullable {
name += "]"
}
return &PythonType{
Type: val,
PythonName: name,
}
}
func (c *compiler) compileAlias(val fidlgen.Alias) PythonAlias {
t := c.compileType(val.Type, val.MaybeFromAlias)
if t == nil {
log.Fatalf("Type not supported")
}
return PythonAlias{
Alias: val,
PythonAliasedName: t.PythonName,
PythonName: *c.compileDeclIdentifier(val.Name),
}
}
// TODO(https://fxbug.dev/396552135): Add literal tests to conformance test suite.
func (c *compiler) compileLiteral(val fidlgen.Literal) *string {
var r string
switch val.Kind {
case fidlgen.NumericLiteral:
r = val.Value
case fidlgen.BoolLiteral:
if val.Value == "true" {
r = "True"
return &r
} else if val.Value == "false" {
r = "False"
} else {
log.Fatalf("Unknown bool value: %v", val)
}
case fidlgen.StringLiteral:
r = strings.ReplaceAll(val.Value, "\\", "\\\\")
r = strings.ReplaceAll(r, "\"", "\\\"")
r = strings.ReplaceAll(r, "'", "\\'")
r = strings.ReplaceAll(r, "\x00", "\\x00")
r = fmt.Sprintf("\"\"\"%s\"\"\"", r)
default:
log.Fatalf("Unknown literal kind: %v", val)
}
return &r
}
func (c *compiler) compileMemberIdentifier(val fidlgen.EncodedCompoundIdentifier) *string {
ci := val.Parse()
if ci.Member == "" {
log.Fatalf("expected a member: %s", val)
}
decl := val.DeclName()
declType := c.lookupDeclInfo(decl).Type
var member string
switch declType {
case fidlgen.BitsDeclType, fidlgen.EnumDeclType:
member = compileScreamingSnakeIdentifier(ci.Member)
default:
log.Fatalf("unexpected decl type: %s", declType)
}
member_identifier := fmt.Sprintf("%s.%s", *c.compileDeclIdentifier(decl), member)
return &member_identifier
}
func (c *compiler) compileConstant(val fidlgen.Constant, typ fidlgen.Type) *string {
switch val.Kind {
case fidlgen.LiteralConstant:
return c.compileLiteral(*val.Literal)
case fidlgen.BinaryOperator, fidlgen.IdentifierConstant:
return &val.Value
}
log.Fatalf("Failed to compile constant: %v, %v", val, typ)
return nil
}
func (c *compiler) compileConst(val fidlgen.Const) PythonConst {
return PythonConst{
Const: val,
PythonName: *c.compileDeclIdentifier(val.Name),
PythonValue: *c.compileConstant(val.Value, val.Type),
PythonType: *c.compileType(val.Type, nil),
}
}
func (c *compiler) compileBits(val fidlgen.Bits) PythonBits {
e := PythonBits{
Bits: val,
PythonName: *c.compileDeclIdentifier(val.Name),
PythonMembers: []PythonBitsMember{},
Empty: len(val.Members) == 0,
}
for _, member_val := range val.Members {
e.PythonMembers = append(e.PythonMembers, PythonBitsMember{
BitsMember: member_val,
PythonName: changeIfReserved(compileScreamingSnakeIdentifier(member_val.Name)),
PythonValue: *c.compileConstant(member_val.Value, val.Type),
})
}
return e
}
func (c *compiler) compileEnum(val fidlgen.Enum) PythonEnum {
e := PythonEnum{
Enum: val,
PythonName: *c.compileDeclIdentifier(val.Name),
PythonMembers: []PythonEnumMember{},
Empty: len(val.Members) == 0,
HasZero: false,
}
for _, member_val := range val.Members {
var value string
switch member_val.Value.Kind {
case fidlgen.LiteralConstant:
value = *c.compileLiteral(*member_val.Value.Literal)
e.HasZero = e.HasZero || (value == "0")
case fidlgen.BinaryOperator, fidlgen.IdentifierConstant:
value = member_val.Value.Value
default:
log.Fatalf("Unknown enum member kind: %v", member_val)
}
e.PythonMembers = append(e.PythonMembers, PythonEnumMember{
EnumMember: member_val,
PythonName: changeIfReserved(compileScreamingSnakeIdentifier(member_val.Name)),
PythonValue: value,
})
}
return e
}
func (c *compiler) compileTableMember(val fidlgen.TableMember) PythonTableMember {
t := c.compileType(val.Type, val.MaybeFromAlias)
if t == nil {
log.Fatalf("Type not supported")
}
return PythonTableMember{
TableMember: val,
PythonType: *t,
PythonName: changeIfReserved(compileSnakeIdentifier(val.Name)),
}
}
func (c *compiler) compileTable(val fidlgen.Table) PythonTable {
name := *c.compileDeclIdentifier(val.Name)
python_table := PythonTable{
Table: val,
Library: string(val.Name.LibraryName()),
PythonName: name,
PythonMembers: []PythonTableMember{},
}
for _, v := range val.Members {
member := c.compileTableMember(v)
python_table.PythonMembers = append(python_table.PythonMembers, member)
}
return python_table
}
func (c *compiler) compileStructMember(val fidlgen.StructMember) (PythonStructMember, *UnsupportedMessage) {
if _, ok := val.Attributes.LookupAttribute("allow_deprecated_struct_defaults"); ok {
message := UnsupportedMessage(fmt.Sprintf("%s annotated with allow_deprecated_struct_defaults", val.Name))
return PythonStructMember{}, &message
}
t := c.compileType(val.Type, val.MaybeFromAlias)
if t == nil {
message := UnsupportedMessage(fmt.Sprintf("Failed to compile type of %s", val.Name))
return PythonStructMember{}, &message
}
return PythonStructMember{
StructMember: val,
PythonType: *t,
PythonName: changeIfReserved(compileSnakeIdentifier(val.Name)),
}, nil
}
func (c *compiler) compileStruct(val fidlgen.Struct) (PythonStruct, *PythonUnsupported) {
name := *c.compileDeclIdentifier(val.Name)
python_struct := PythonStruct{
Struct: val,
Library: string(val.Name.LibraryName()),
PythonName: name,
PythonMembers: []PythonStructMember{},
}
unsupported_message := ""
for _, v := range val.Members {
member, message := c.compileStructMember(v)
if message != nil {
if unsupported_message != "" {
unsupported_message += "\n"
}
unsupported_message = fmt.Sprintf("%s- %s", unsupported_message, *message)
continue
}
python_struct.PythonMembers = append(python_struct.PythonMembers, member)
}
if unsupported_message != "" {
unsupported := PythonUnsupported{
Identifier: val.Name,
PythonName: name,
Message: strings.Split(unsupported_message, "\n"),
}
return PythonStruct{}, &unsupported
}
return python_struct, nil
}
var pythonReservedWords = map[string]struct{}{
// LINT.IfChange
// keep-sorted start
"ArithmeticError": {}, //
"AssertionError": {}, //
"AttributeError": {}, //
"BaseException": {}, //
"BaseExceptionGroup": {}, //
"BlockingIOError": {}, //
"BrokenPipeError": {}, //
"BufferError": {}, //
"BytesWarning": {}, //
"ChildProcessError": {}, //
"ConnectionAbortedError": {}, //
"ConnectionError": {}, //
"ConnectionRefusedError": {}, //
"ConnectionResetError": {}, //
"DeprecationWarning": {}, //
"EOFError": {}, //
"Ellipsis": {}, //
"EncodingWarning": {}, //
"EnvironmentError": {}, //
"Exception": {}, //
"ExceptionGroup": {}, //
"False": {}, //
"FileExistsError": {}, //
"FileNotFoundError": {}, //
"FloatingPointError": {}, //
"FutureWarning": {}, //
"GeneratorExit": {}, //
"IOError": {}, //
"ImportError": {}, //
"ImportWarning": {}, //
"IndentationError": {}, //
"IndexError": {}, //
"InterruptedError": {}, //
"IsADirectoryError": {}, //
"KeyError": {}, //
"KeyboardInterrupt": {}, //
"LookupError": {}, //
"MemoryError": {}, //
"ModuleNotFoundError": {}, //
"NameError": {}, //
"None": {}, //
"NotADirectoryError": {}, //
"NotImplemented": {}, //
"NotImplementedError": {}, //
"OSError": {}, //
"OverflowError": {}, //
"PendingDeprecationWarning": {}, //
"PermissionError": {}, //
"ProcessLookupError": {}, //
"RecursionError": {}, //
"ReferenceError": {}, //
"ResourceWarning": {}, //
"RuntimeError": {}, //
"RuntimeWarning": {}, //
"StopAsyncIteration": {}, //
"StopIteration": {}, //
"SyntaxError": {}, //
"SyntaxWarning": {}, //
"SystemError": {}, //
"SystemExit": {}, //
"TabError": {}, //
"TimeoutError": {}, //
"True": {}, //
"TypeError": {}, //
"UnboundLocalError": {}, //
"UnicodeDecodeError": {}, //
"UnicodeEncodeError": {}, //
"UnicodeError": {}, //
"UnicodeTranslateError": {}, //
"UnicodeWarning": {}, //
"UserWarning": {}, //
"ValueError": {}, //
"Warning": {}, //
"ZeroDivisionError": {}, //
"abs": {}, //
"aiter": {}, //
"all": {}, //
"and": {}, //
"anext": {}, //
"any": {}, //
"as": {}, //
"ascii": {}, //
"assert": {}, //
"async": {}, //
"await": {}, //
"bin": {}, //
"bool": {}, //
"break": {}, //
"breakpoint": {}, //
"bytearray": {}, //
"bytes": {}, //
"callable": {}, //
"case": {}, //
"chr": {}, //
"class": {}, //
"classmethod": {}, //
"compile": {}, //
"complex": {}, //
"continue": {}, //
"copyright": {}, //
"credits": {}, //
"def": {}, //
"del": {}, //
"delattr": {}, //
"dict": {}, //
"dir": {}, //
"divmod": {}, //
"elif": {}, //
"else": {}, //
"enumerate": {}, //
"eval": {}, //
"except": {}, //
"exec": {}, //
"exit": {}, //
"filter": {}, //
"finally": {}, //
"float": {}, //
"for": {}, //
"format": {}, //
"from": {}, //
"frozenset": {}, //
"getattr": {}, //
"global": {}, //
"globals": {}, //
"hasattr": {}, //
"hash": {}, //
"help": {}, //
"hex": {}, //
"id": {}, //
"if": {}, //
"import": {}, //
"in": {}, //
"input": {}, //
"int": {}, //
"is": {}, //
"isinstance": {}, //
"issubclass": {}, //
"iter": {}, //
"lambda": {}, //
"len": {}, //
"license": {}, //
"list": {}, //
"locals": {}, //
"map": {}, //
"match": {}, //
"max": {}, //
"memoryview": {}, //
"min": {}, //
"next": {}, //
"nonlocal": {}, //
"not": {}, //
"object": {}, //
"oct": {}, //
"open": {}, //
"or": {}, //
"ord": {}, //
"pass": {}, //
"pow": {}, //
"print": {}, //
"property": {}, //
"quit": {}, //
"raise": {}, //
"range": {}, //
"repr": {}, //
"return": {}, //
"reversed": {}, //
"round": {}, //
"self": {}, //
"set": {}, //
"setattr": {}, //
"slice": {}, //
"sorted": {}, //
"staticmethod": {}, //
"str": {}, //
"sum": {}, //
"super": {}, //
"try": {}, //
"tuple": {}, //
"type": {}, //
"vars": {}, //
"while": {}, //
"with": {}, //
"yield": {}, //
"zip": {}, //
// keep-sorted end
// LINT.ThenChange(//src/developer/ffx/lib/fuchsia-controller/cpp/fidl_codec/utils.h, //tools/fidl/fidlgen_python/codegen/ir.go, //tools/fidl/gidl/backend/python/conformance.go)
}
func changeIfReserved(s string) string {
if _, ok := pythonReservedWords[s]; ok {
return s + "_"
}
return s
}
func (c *compiler) compileUnionMember(val fidlgen.UnionMember) PythonUnionMember {
t := c.compileType(val.Type, val.MaybeFromAlias)
if t == nil {
log.Fatalf("Type not supported")
}
return PythonUnionMember{
UnionMember: val,
PythonType: *t,
PythonName: changeIfReserved(compileSnakeIdentifier(val.Name)),
}
}
func (c *compiler) compileUnion(val fidlgen.Union) PythonUnion {
name := *c.compileDeclIdentifier(val.Name)
python_union := PythonUnion{
Union: val,
Library: string(val.Name.LibraryName()),
PythonName: name,
PythonMembers: []PythonUnionMember{},
}
for _, v := range val.Members {
member := c.compileUnionMember(v)
python_union.PythonMembers = append(python_union.PythonMembers, member)
if python_union.IsResult && member.PythonName == "response" {
python_union.PythonSuccessType = &member.PythonType
}
}
return python_union
}
func (c *compiler) compilePayload(payload fidlgen.Type) *PythonPayload {
r := PythonPayload{
Type: payload,
DeclType: c.decls[payload.Identifier].Type,
PythonType: *c.compileType(payload, nil),
PythonParameters: []PythonParameter{},
}
switch r.DeclType {
case fidlgen.StructDeclType:
if s, ok := c.PythonRoot.PythonStructs[payload.Identifier]; ok {
for _, field := range s.PythonMembers {
r.PythonParameters = append(r.PythonParameters, PythonParameter{
PythonType: field.PythonType,
PythonName: field.PythonName,
PythonNoneDefault: false,
})
}
} else if s, ok := c.PythonRoot.ExternalPythonStructs[payload.Identifier]; ok {
for _, field := range s.PythonMembers {
r.PythonParameters = append(r.PythonParameters, PythonParameter{
PythonType: field.PythonType,
PythonName: field.PythonName,
PythonNoneDefault: false,
})
}
}
case fidlgen.UnionDeclType:
if u, ok := c.PythonRoot.PythonUnions[payload.Identifier]; ok {
for _, field := range u.PythonMembers {
r.PythonParameters = append(r.PythonParameters, PythonParameter{
PythonType: field.PythonType,
PythonName: field.PythonName,
PythonNoneDefault: true,
})
}
}
case fidlgen.TableDeclType:
if t, ok := c.PythonRoot.PythonTables[payload.Identifier]; ok {
for _, field := range t.PythonMembers {
r.PythonParameters = append(r.PythonParameters, PythonParameter{
PythonType: field.PythonType,
PythonName: field.PythonName,
PythonNoneDefault: true,
})
}
}
default:
log.Fatalf("Unable to compile payload: %v", payload)
}
return &r
}
func (c *compiler) compileProtocol(val fidlgen.Protocol) PythonProtocol {
name := *c.compileDeclIdentifier(val.Name)
r := PythonProtocol{
Protocol: val,
PythonName: name,
Library: string(c.library),
PythonMarkerName: name + "Marker",
PythonClientName: name + "Client",
PythonServerName: name + "Server",
PythonEventHandlerName: name + "EventHandler",
}
// Compile the marker for discovering a protocol, if it's discoverable,
// e.g. "fuchsia.developer.ffx.Echo".
if _, r.Discoverable = val.LookupAttribute("discoverable"); r.Discoverable {
r.Marker = strings.Trim(val.GetProtocolName(), "\"")
} else {
r.Marker = fmt.Sprintf("(nondiscoverable) %s", val.Name)
}
for _, v := range val.Methods {
m := PythonMethod{
Method: v,
HasFrameworkError: v.HasFrameworkError(),
PythonName: changeIfReserved(compileSnakeIdentifier(v.Name)),
PythonResponseAlias: changeIfReserved(fmt.Sprintf("%sResponse", compileCamelIdentifier(v.Name))),
}
if v.RequestPayload == nil {
m.PythonRequestPayload = nil
} else {
m.PythonRequestPayload = c.compilePayload(*v.RequestPayload)
m.PythonRequestIdent = m.PythonRequestPayload.PythonType.Identifier
}
if v.ResponsePayload == nil {
m.PythonResponsePayload = nil
} else {
m.PythonResponsePayload = c.compilePayload(*v.ResponsePayload)
m.PythonResponseIdentifier = m.PythonResponsePayload.PythonType.Identifier
}
if v.HasError || m.HasFrameworkError {
m.PythonResponseSuccessPayload = c.compilePayload(*v.ValueType)
}
if m.HasResponse &&
(m.PythonResponsePayload == nil ||
(m.PythonResponseSuccessPayload != nil &&
m.PythonResponseSuccessPayload.PythonType.PythonName == "None")) {
m.EmptyResponse = true
} else {
m.EmptyResponse = false
}
r.PythonMethods = append(r.PythonMethods, m)
}
return r
}
func Compile(root fidlgen.Root) PythonRoot {
root = root.ForBindings("python")
root = root.ForTransports([]string{"Channel"})
c := compiler{
decls: root.DeclInfo(),
library: root.Name,
externalModules: map[string]struct{}{},
PythonRoot: PythonRoot{
Root: root,
PythonModuleName: ToPythonModuleName(root.Name),
EmptySuccessStructs: map[fidlgen.EncodedCompoundIdentifier]fidlgen.Struct{},
PythonStructs: map[fidlgen.EncodedCompoundIdentifier]PythonStruct{},
ExternalPythonStructs: map[fidlgen.EncodedCompoundIdentifier]PythonStruct{},
PythonUnions: map[fidlgen.EncodedCompoundIdentifier]PythonUnion{},
PythonTables: map[fidlgen.EncodedCompoundIdentifier]PythonTable{},
},
}
// Collect all empty success structs so that all calls to compileType() will return None as
// their type.
for _, v := range root.Structs {
if v.IsEmptySuccessStruct {
c.PythonRoot.EmptySuccessStructs[v.Name] = v
}
}
// The only known uses of ExternalStructs are from fuchsia.unknown which provides some empty
// success structs. Otherwise, it seems safe to ignore types in ExternalStructs.
for _, v := range root.ExternalStructs {
if v.IsEmptySuccessStruct {
c.PythonRoot.EmptySuccessStructs[v.Name] = v
}
python_struct, python_unsupported := c.compileStruct(v)
if python_unsupported != nil {
c.PythonRoot.PythonUnsupportedTypes = append(c.PythonRoot.PythonUnsupportedTypes, *python_unsupported)
} else {
c.PythonRoot.ExternalPythonStructs[v.Name] = python_struct
}
}
for _, v := range root.Tables {
c.PythonRoot.PythonTables[v.Name] = c.compileTable(v)
}
for _, v := range root.Structs {
if v.IsEmptySuccessStruct {
c.PythonRoot.EmptySuccessStructs[v.Name] = v
}
python_struct, python_unsupported := c.compileStruct(v)
if python_unsupported != nil {
c.PythonRoot.PythonUnsupportedTypes = append(c.PythonRoot.PythonUnsupportedTypes, *python_unsupported)
} else {
c.PythonRoot.PythonStructs[v.Name] = python_struct
}
}
for _, v := range root.Aliases {
c.PythonRoot.PythonAliases = append(c.PythonRoot.PythonAliases, c.compileAlias(v))
}
// TODO(https://fxbug.dev/396552135): Extend the test suite to cover the different variations
// of bits declarations.
for _, v := range root.Bits {
c.PythonRoot.PythonBits = append(c.PythonRoot.PythonBits, c.compileBits(v))
}
for _, v := range root.Consts {
c.PythonRoot.PythonConsts = append(c.PythonRoot.PythonConsts, c.compileConst(v))
}
for _, v := range root.Enums {
c.PythonRoot.PythonEnums = append(c.PythonRoot.PythonEnums, c.compileEnum(v))
}
for _, v := range root.Unions {
// TODO(https://fxbug.dev/394421154): If v is a result, then its creation should
// be deferred to when the corresponding protocol method is compiled.
c.PythonRoot.PythonUnions[v.Name] = c.compileUnion(v)
}
for _, v := range root.Protocols {
c.PythonRoot.PythonProtocols = append(c.PythonRoot.PythonProtocols, c.compileProtocol(v))
}
// Sort the external modules to make sure the generated file is
// consistent across builds.
var externalModules []string
for k := range c.externalModules {
externalModules = append(externalModules, k)
}
sort.Strings(externalModules)
for _, k := range externalModules {
c.PythonRoot.PythonExternalModules = append(c.PythonRoot.PythonExternalModules, k)
}
return c.PythonRoot
}