blob: 0b1c87ee696570e12926a6d06c0c522663cfbaa9 [file] [log] [blame]
# Copyright 2019 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.
import json
import os
import sys
import typing as t
class DeclState(object):
def __init__(self, indent: str = "", parents: t.Set[str] = frozenset()):
self.indent = indent
self.parents = parents
def nest(
self, indent: str = "", identifier: t.Optional[str] = None
) -> "DeclState":
indent = self.indent + indent
parents = self.parents
if identifier:
parents = parents.union({identifier})
return DeclState(indent, parents)
class Declaration(dict):
def __init__(self, library: "Library", value: dict):
dict.__init__(self, value)
self.library = library
@property
def name(self) -> str:
return self["name"]
@property
def attributes(self):
return dict(
(a["name"], a["value"]) for a in self.get("maybe_attributes", [])
)
class Type(dict):
def __init__(self, library, value):
self.library = library
dict.__init__(self, value)
@property
def kind(self):
return self["kind"]
def is_primitive(self) -> bool:
return self.kind == "primitive"
def is_nullable(self) -> bool:
return self.get("nullable", False)
@property
def element_type(self):
if "element_type" in self:
return Type(self.library, self["element_type"])
def decl(self, state) -> str:
if self.is_primitive():
return self["subtype"]
nullable = ""
if self.is_nullable():
nullable = "?"
if self.kind == "identifier":
if self["identifier"] in state.parents:
# a cycle
return self["identifier"] + nullable
value = self.library.libraries.find(self["identifier"])
if isinstance(value, Protocol):
return self["identifier"] + nullable
else:
return (
value.decl(state.nest(identifier=self["identifier"]))
+ nullable
)
element_count = self.get(
"element_count", self.get("maybe_element_count")
)
size = ""
if element_count is not None:
size = ":%d" % element_count
if self.kind == "string":
return "string" + size + nullable
if self.kind in ("vector", "array"):
return "%s<%s>%s%s" % (
self.kind,
Type(self.library, self["element_type"]).decl(state),
size,
nullable,
)
if self.kind == "handle":
if self["subtype"] == "handle":
return "handle"
else:
return "handle<%s>" % self["subtype"]
if self.kind == "request":
return "request<%s>" % self["subtype"]
raise Exception("unknown type %r" % self)
class Const(Declaration):
@property
def type(self) -> Type:
return Type(self.library, self["type"])
class EnumMember(Declaration):
def __init__(self, enum: "Enum", value: dict):
Declaration.__init__(self, enum.library, value)
self.enum = enum
def decl(self, state: DeclState) -> str:
return "%s = %s" % (self.name, self["value"]["literal"]["value"])
class Enum(Declaration):
@property
def type(self) -> Type:
return Type(
self.library, {"kind": "primitive", "subtype": self["type"]}
)
@property
def members(self) -> t.List[EnumMember]:
return [EnumMember(self, m) for m in self["members"]]
def decl(self, state: DeclState) -> str:
state = state.nest(identifier=self.name)
return (
("enum %s : %s {" % (self.name, self.type.decl(state)))
+ "\n "
+ state.indent
+ (",\n " + state.indent).join(m.decl(state) for m in self.members)
+ "\n"
+ state.indent
+ "}"
)
class Bits(Declaration):
pass
class Argument(Declaration):
def __init__(self, method: "Method", value: dict):
Declaration.__init__(self, method.library, value)
self.method = method
@property
def type(self) -> Type:
return Type(self.library, self["type"])
def decl(self, state: DeclState) -> str:
return "%s %s" % (self.type.decl(state), self["name"])
class Method(Declaration):
def __init__(self, protocol: "Protocol", value: dict):
Declaration.__init__(self, protocol.library, value)
self.protocol = protocol
@property
def name(self) -> str:
return "%s.%s" % (self.protocol.name, self["name"])
def is_event(self) -> bool:
return "maybe_request" not in self
def is_one_way(self) -> bool:
return "maybe_response" not in self
def request(self) -> t.Optional[t.List[Argument]]:
if self["has_request"]:
return [Argument(self, a) for a in self["maybe_request"]]
else:
return None
def response(self) -> t.Optional[t.List[Argument]]:
if self["has_response"]:
return [Argument(self, a) for a in self["maybe_response"]]
else:
return None
def decl(self, state: DeclState) -> str:
if self.is_event():
preamble = "%d: -> %s(" % (self["ordinal"], self["name"])
state = state.nest(indent=" " * len(preamble))
return (
preamble
+ ", ".join(arg.decl(state) for arg in self.response())
+ ");"
)
if self.is_one_way():
# TODO
return "adsf"
preamble = "%d: %s(" % (self["ordinal"], self["name"])
state = state.nest(indent=" " * len(preamble))
return (
preamble
+ ", ".join(arg.decl(state) for arg in self.request())
+ ")\n"
+ state.indent[:-4]
+ "-> ("
+ ", ".join(arg.decl(state) for arg in self.response())
+ ");"
)
class Protocol(Declaration):
@property
def methods(self) -> t.List[Method]:
return [Method(self, m) for m in self.get("methods", [])]
class StructMember(Declaration):
def __init__(self, struct: "Struct", value: dict):
Declaration.__init__(self, struct.library, value)
self.struct = struct
@property
def type(self) -> Type:
return Type(self.library, self["type"])
def decl(self, state: DeclState) -> str:
# TODO: defaults?
return self.type.decl(state) + " " + self.name
class Struct(Declaration):
@property
def members(self) -> t.List[StructMember]:
return [StructMember(self, m) for m in self["members"]]
def decl(self, state: DeclState) -> str:
sub_state = state.nest(indent=" ", identifier=self.name)
return (
("struct %s {" % self.name)
+ "\n"
+ sub_state.indent
+ (";\n" + sub_state.indent).join(
m.decl(sub_state) for m in self.members
)
+ ";\n"
+ state.indent
+ "}"
)
class TableMember(Declaration):
def __init__(self, table: "Table", value: dict):
Declaration.__init__(self, table.library, value)
self.table = table
@property
def name(self) -> t.Optional[str]:
return self.get("name")
@property
def type(self) -> Type:
return Type(self.library, self["type"])
def decl(self, state: DeclState) -> str:
ordinal = "%d: " % self["ordinal"]
sub_state = state.nest(indent=" " * len(ordinal))
return ordinal + self.type.decl(sub_state) + " " + self.name
class Table(Declaration):
@property
def members(self) -> t.List[TableMember]:
return [TableMember(self, m) for m in self["members"]]
def decl(self, state: DeclState) -> str:
sub_state = state.nest(indent=" ", identifier=self.name)
return (
("table %s {" % self.name)
+ "\n"
+ sub_state.indent
+ (";\n" + sub_state.indent).join(
m.decl(sub_state) for m in self.members
)
+ ";\n"
+ state.indent
+ "}"
)
class UnionMember(Declaration):
def __init__(self, union: "Union", value):
Declaration.__init__(self, union.library, value)
self.union = union
@property
def type(self) -> Type:
return Type(self.library, self["type"])
def decl(self, state: DeclState) -> str:
return self.type.decl(state) + " " + self.name
class Union(Declaration):
@property
def members(self) -> t.List[UnionMember]:
return [UnionMember(self, m) for m in self["members"]]
def decl(self, state: DeclState) -> str:
sub_state = state.nest(indent=" ", identifier=self.name)
return (
"union %s {\n" % self.name
+ sub_state.indent
+ (";\n" + sub_state.indent).join(
m.decl(sub_state) for m in self.members
)
+ ";\n"
+ state.indent
+ "}"
)
DECLARATION_TYPES = {
"const": ("const_declarations", Const),
"enum": ("enum_declarations", Enum),
"bits": ("bits_declarations", Bits),
"protocol": ("protocol_declarations", Protocol),
"struct": ("struct_declarations", Struct),
"table": ("table_declarations", Table),
"union": ("union_declarations", Union),
}
class Library(dict):
def __init__(self, libraries: "FidlLibraries", path: str):
self.libraries = libraries
self._path = path
dict.__init__(self, json.load(open(path)))
self.name = self["name"]
def __repr__(self):
return "Library<%s>" % self.name
@property
def consts(self) -> t.List[Const]:
return [Const(self, value) for value in self["const_declarations"]]
@property
def enums(self) -> t.List[Enum]:
return [Enum(self, value) for value in self["enum_declarations"]]
@property
def bits(self) -> t.List[Bits]:
return [Bits(self, value) for value in self["bits_declarations"]]
@property
def protocols(self) -> t.List[Protocol]:
return [Protocol(self, v) for v in self["protocol_declarations"]]
@property
def structs(self) -> t.List[Struct]:
return [Struct(self, value) for value in self["struct_declarations"]]
@property
def tables(self) -> t.List[Table]:
return [Table(self, value) for value in self["table_declarations"]]
@property
def unions(self) -> t.List[Union]:
return [Union(self, value) for value in self["union_declarations"]]
@property
def methods(self) -> t.List[Method]:
return [
method for protocol in self.protocols for method in protocol.methods
]
def find(
self, identifier: str
) -> t.Union[None, Const, Enum, Protocol, Struct, Table, Union]:
if identifier not in self["declarations"]:
return None
declaration_type = self["declarations"][identifier]
declarations_key, constructor = DECLARATION_TYPES[declaration_type]
return self._lookup_declaration(
identifier, declarations_key, constructor
)
def _lookup_declaration(self, identifier, declarations_key, constructor):
value = next(
s for s in self[declarations_key] if s["name"] == identifier
)
return constructor(self, value)
class Libraries(list):
def __init__(self):
build_dir = os.environ.get("FUCHSIA_BUILD_DIR")
if build_dir is None:
print("FUCHSIA_BUILD_DIR is not set.")
print("Run: fx exec %s" % " ".join(sys.argv))
sys.exit(1)
# find all the .fidl.json files
ENDING = ".fidl.json"
for root, _, files in os.walk(build_dir):
self.extend(
Library(self, os.path.join(root, f))
for f in files
if f.endswith(ENDING)
)
self.by_name = dict((l.name, l) for l in self)
@property
def consts(self) -> t.List[Const]:
return [const for library in self for const in library.consts]
@property
def enums(self) -> t.List[Enum]:
return [enum for library in self for enum in library.enums]
@property
def bits(self) -> t.List[Bits]:
return [bit for library in self for bit in library.bits]
@property
def protocols(self) -> t.List[Protocol]:
return [protocol for library in self for protocol in library.protocols]
@property
def structs(self) -> t.List[Struct]:
return [struct for library in self for struct in library.structs]
@property
def tables(self) -> t.List[Table]:
return [table for library in self for table in library.tables]
@property
def unions(self) -> t.List[Union]:
return [union for library in self for union in library.unions]
@property
def methods(self) -> t.List[Method]:
return [method for library in self for method in library.methods]
def find(
self, identifier: str
) -> t.Union[None, Const, Enum, Protocol, Struct, Table, Union]:
library_name, _ = identifier.split("/")
if library_name not in self.by_name:
return None
library = self.by_name[library_name]
return library.find(identifier)