| # 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 reserved(self) -> bool: |
| return self['reserved'] |
| |
| @property |
| def type(self) -> Type: |
| return Type(self.library, self['type']) |
| |
| def decl(self, state: DeclState) -> str: |
| ordinal = '%d: ' % self['ordinal'] |
| if self.reserved: |
| return ordinal + 'reserved' |
| else: |
| 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) |