blob: 50914a35282a932f5650eaeb6e6d62d20a863ec7 [file] [log] [blame] [edit]
# 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
__all__ = [
'Declaration',
'Type',
'PrimitiveType',
'NullableType',
'HandleType',
'RequestType',
'ArrayType',
'VectorType',
'StringType',
'IdentifierType',
'EnumIdentifierType',
'BitsIdentifierType',
'StructIdentifierType',
'ProtocolIdentifierType',
'TableIdentifierType',
'UnionIdentifierType',
'XUnionIdentifierType',
'Const',
'EnumMember',
'Enum',
'StructMember',
'Struct',
'Argument',
'Method',
'Protocol',
'TableMember',
'Table',
'UnionMember',
'Union',
'XUnion',
'Library',
'Libraries',
]
class Declaration(dict):
def __init__(self, library: 'Library', value: dict):
dict.__init__(self, value)
self.library = library
@property
def inline_size(self) -> int:
return self['size']
@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', []))
@property
def location(self) -> str:
return '%s:%d' % (self['location']['filename'],
self['location']['line'])
@property
def filename(self) -> str:
return self['location']['filename']
@property
def line(self) -> int:
return self['location']['line']
def construct_type(library: 'Library', value: dict) -> 'Type':
if value['kind'] == 'primitive':
return PrimitiveType(library, value)
elif value['kind'] == 'handle':
return HandleType(library, value)
elif value['kind'] == 'request':
return RequestType(library, value)
elif value['kind'] == 'array':
return ArrayType(library, value)
elif value['kind'] == 'vector':
return VectorType(library, value)
elif value['kind'] == 'string':
return StringType(library, value)
elif value['kind'] == 'identifier':
return _construct_identifier_type(library, value)
else:
raise Exception('Unknown type: %r' % value)
class Type(dict):
def __init__(self, library: 'Library', value: dict, inline_size: int):
self.library = library
self.inline_size = inline_size
dict.__init__(self, value)
@property
def kind(self):
return self['kind']
class PrimitiveType(Type):
@staticmethod
def from_subtype(subtype: str, library: 'Library'):
return PrimitiveType(library, {
'kind': 'primitive',
'subtype': subtype,
})
def __init__(self, library: 'Library', value: dict):
assert value['kind'] == 'primitive'
if isinstance(value['subtype'], dict):
# TODO: why do we get this here?
value = value['subtype']
subtype = value['subtype']
if subtype in {'bool', 'int8', 'uint8'}:
inline_size = 1
elif subtype in {'int16', 'uint16'}:
inline_size = 2
elif subtype in {'int32', 'uint32', 'float32'}:
inline_size = 4
elif subtype in {'int64', 'uint64', 'float64'}:
inline_size = 8
else:
raise Exception('Unknown primitive subtype %r' % subtype)
super().__init__(library, value, inline_size)
self.subtype = subtype
self.is_float = self.subtype in ('float32', 'float64')
self.is_signed = self.subtype in ('int8', 'int16', 'int32',
'int64') or self.is_float
class NullableType(Type):
def __init__(self, library: 'Library', value: dict, inline_size: int):
assert 'nullable' in value
super().__init__(library, value, inline_size)
self.is_nullable = self['nullable']
class HandleType(NullableType):
def __init__(self, library: 'Library', value: dict):
assert value['kind'] == 'handle'
super().__init__(library, value, 4)
self.handle_type = self['subtype']
class RequestType(NullableType):
def __init__(self, library: 'Library', value: dict):
assert value['kind'] == 'request'
super().__init__(library, value, 4)
self.protocol = self['subtype']
class ArrayType(Type):
def __init__(self, library: 'Library', value: dict):
assert value['kind'] == 'array'
self.element_type = construct_type(library, value['element_type'])
super().__init__(
library, value,
value['element_count'] * self.element_type.inline_size)
self.count: int = self['element_count']
class VectorType(NullableType):
def __init__(self, library: 'Library', value: dict):
assert value['kind'] == 'vector'
super().__init__(library, value, 16)
self.element_type = construct_type(library, value['element_type'])
self.limit: t.Optional[int] = self.get('maybe_element_count')
class StringType(NullableType):
def __init__(self, library: 'Library', value: dict):
assert value['kind'] == 'string'
super().__init__(library, value, 16)
self.element_type = PrimitiveType.from_subtype('uint8', library)
self.limit: t.Optional[int] = self.get('maybe_element_count')
def _construct_identifier_type(library: 'Library',
value: dict) -> 'IdentifierType':
assert value['kind'] == 'identifier'
declaration = library.libraries.find(value['identifier'])
if isinstance(declaration, Enum):
return EnumIdentifierType(library, value, declaration)
if isinstance(declaration, Bits):
return BitsIdentifierType(library, value, declaration)
if isinstance(declaration, Struct):
return StructIdentifierType(library, value, declaration)
if isinstance(declaration, Protocol):
return ProtocolIdentifierType(library, value, declaration)
if isinstance(declaration, Table):
return TableIdentifierType(library, value, declaration)
if isinstance(declaration, Union):
return UnionIdentifierType(library, value, declaration)
if isinstance(declaration, XUnion):
return XUnionIdentifierType(library, value, declaration)
raise Exception("Can't construct identifier type %s for %r" %
(value['identifier'], declaration))
DECL = t.TypeVar('DECL', bound=Declaration)
class IdentifierType(NullableType):
def __init__(self, library: 'Library', value: dict, declaration: DECL):
assert value['kind'] == 'identifier'
inline_size = 8
if not value['nullable']:
inline_size = declaration.inline_size
super().__init__(library, value, inline_size)
self.identifier = self['identifier']
class EnumIdentifierType(IdentifierType):
def __init__(self, library: 'Library', value: dict, declararation: 'Enum'):
super().__init__(library, value, declararation)
self.primitive = declararation.type
self.declaration = declararation
class BitsIdentifierType(IdentifierType):
def __init__(self, library: 'Library', value: dict, declararation: 'Bits'):
super().__init__(library, value, declararation)
self.primitive = declararation.type
self.declaration = declararation
class StructIdentifierType(IdentifierType):
def __init__(self, library: 'Library', value: dict,
declararation: 'Struct'):
super().__init__(library, value, declararation)
self.declaration = declararation
class ProtocolIdentifierType(IdentifierType):
def __init__(self, library: 'Library', value: dict,
declararation: 'Protocol'):
super().__init__(library, value, declararation)
self.declaration = declararation
class TableIdentifierType(IdentifierType):
def __init__(self, library: 'Library', value: dict,
declararation: 'Table'):
super().__init__(library, value, declararation)
assert not self.is_nullable
self.declaration = declararation
class UnionIdentifierType(IdentifierType):
def __init__(self, library: 'Library', value: dict,
declararation: 'Union'):
super().__init__(library, value, declararation)
self.declaration = declararation
class XUnionIdentifierType(IdentifierType):
def __init__(self, library: 'Library', value: dict,
declararation: 'XUnion'):
super().__init__(library, value, declararation)
self.declaration = declararation
class Const(Declaration):
@property
def type(self) -> Type:
return construct_type(self.library, self['type'])
class EnumMember(Declaration):
def __init__(self, enum: 'Enum', value: dict):
Declaration.__init__(self, enum.library, value)
self.enum = enum
@property
def value(self) -> int:
literal = self['value']
assert literal['kind'] == 'literal'
assert literal['literal']['kind'] == 'numeric'
return int(literal['literal']['value'])
class Enum(Declaration):
@property
def type(self) -> PrimitiveType:
return PrimitiveType.from_subtype(self['type'], self.library)
@property
def inline_size(self) -> int:
return self.type.inline_size
@property
def members(self) -> t.List[EnumMember]:
return [EnumMember(self, m) for m in self['members']]
class BitsMember(Declaration):
def __init__(self, bits: 'Bits', value: dict):
Declaration.__init__(self, bits.library, value)
self.bits = bits
@property
def value(self) -> int:
literal = self['value']
assert literal['kind'] == 'literal'
assert literal['literal']['kind'] == 'numeric'
return int(literal['literal']['value'])
class Bits(Declaration):
def __init__(self, library: 'Library', value: dict):
if 'location' not in value:
# TODO: fix fidlc to include bits location
value['location'] = {'filename': 'nowhere', 'line': 42}
super().__init__(library, value)
self.mask = self['mask']
@property
def type(self) -> PrimitiveType:
return PrimitiveType.from_subtype(self['type'], self.library)
@property
def inline_size(self) -> int:
return self.type.inline_size
@property
def members(self) -> t.List[BitsMember]:
return [BitsMember(self, m) for m in self['members']]
class StructMember(Declaration):
def __init__(self, struct: 'Struct', value: dict):
Declaration.__init__(self, struct.library, value)
self.struct = struct
self.offset: int = self['offset']
self.size: int = self['size']
@property
def type(self) -> Type:
return construct_type(self.library, self['type'])
class Struct(Declaration):
def __init__(self,
library: 'Library',
value: dict,
method: t.Optional['Method'] = None,
request: t.Optional[bool] = None):
super().__init__(library, value)
self.method = method
self.request = request
self.size: int = self['size']
self.members = [StructMember(self, m) for m in self['members']]
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 construct_type(self.library, self['type'])
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
@property
def has_request(self) -> bool:
return 'maybe_request' in self
def request(self) -> t.Optional[Struct]:
if self['has_request']:
return self.arguments_struct('Request', self['maybe_request'],
self['maybe_request_size'])
else:
return None
def response(self) -> t.Optional[Struct]:
if self['has_response']:
return self.arguments_struct('Response', self['maybe_response'],
self['maybe_response_size'])
else:
return None
def arguments_struct(self, suffix: str, members: t.List,
size: int) -> Struct:
return Struct(
self.library, {
'name': '%s:%s' % (self.name, suffix),
'members': members,
'size': size,
'location': self['location'],
})
@property
def ordinal(self) -> int:
return int(self['ordinal'])
class Protocol(Declaration):
@property
def methods(self) -> t.List[Method]:
return [Method(self, m) for m in self.get('methods', [])]
@property
def inline_size(self) -> int:
return 4 # it's a handle
class TableMember(Declaration):
def __init__(self, table: 'Table', value: dict):
Declaration.__init__(self, table.library, value)
self.table = table
@property
def name(self) -> str:
return self.get('name', '')
@property
def ordinal(self) -> int:
return int(self['ordinal'])
@property
def reserved(self) -> bool:
return self['reserved']
@property
def type(self) -> Type:
if self.reserved:
raise Exception('Reserved table members have no type')
return construct_type(self.library, self['type'])
class Table(Declaration):
@property
def members(self) -> t.List[TableMember]:
return [TableMember(self, m) for m in self['members']]
class UnionMember(Declaration):
def __init__(self, union: 'Union', value):
Declaration.__init__(self, union.library, value)
self.union = union
@property
def type(self) -> Type:
return construct_type(self.library, self['type'])
class Union(Declaration):
@property
def members(self) -> t.List[UnionMember]:
return [UnionMember(self, m) for m in self['members']]
class XUnionMember(Declaration):
def __init__(self, xunion: 'XUnion', value):
Declaration.__init__(self, xunion.library, value)
self.xunion = xunion
self.ordinal = self['ordinal']
@property
def type(self) -> Type:
return construct_type(self.library, self['type'])
class XUnion(Declaration):
@property
def members(self) -> t.List[XUnionMember]:
return [XUnionMember(self, m) for m in self['members']]
D = t.TypeVar('D', bound=Declaration)
class Library(dict):
def __init__(self, libraries: 'Libraries', path: str):
self.libraries = libraries
self._path = path
dict.__init__(self, json.load(open(path)))
self.name = self['name']
self.declarations: t.Dict[str, Declaration] = {}
self.filenames: t.Set[str] = set()
self.consts = self._collect_declarations('const_declarations', Const)
self.enums = self._collect_declarations('enum_declarations', Enum)
self.bits = self._collect_declarations('bits_declarations', Bits)
self.protocols = self._collect_declarations('interface_declarations',
Protocol)
self.structs = self._collect_declarations('struct_declarations',
Struct)
self.tables = self._collect_declarations('table_declarations', Table)
self.unions = self._collect_declarations('union_declarations', Union)
self.xunions = self._collect_declarations('xunion_declarations',
XUnion)
def _collect_declarations(
self, key: str,
ctor: t.Callable[['Library', dict], D]) -> t.List[D]:
decls: t.List[D] = []
for json in self[key]:
decl: D = ctor(self, json)
decls.append(decl)
self.declarations[decl.name] = decl
self.filenames.add(decl.filename)
return decls
def __repr__(self):
return 'Library<%s>' % self.name
def find(self, identifier: str) -> Declaration:
return self.declarations[identifier]
@property
def declaration_order(self) -> t.List[str]:
return list(self['declarations'].keys())
class Libraries(list):
def __init__(self):
self.by_name = dict()
def load_all(self, list_path: str):
for relative_path in (line.strip()
for line in open(list_path).readlines()):
path = os.path.join(os.path.dirname(list_path), relative_path)
self.load(path)
def load(self, path: str) -> Library:
library = Library(self, path)
self.append(library)
self.by_name[library.name] = library
return library
@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 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[Const, Enum, Protocol, Struct, Table, Union]:
library_name, _ = identifier.split('/')
if library_name not in self.by_name:
raise Exception('Identifier %s not found' % identifier)
library = self.by_name[library_name]
return library.find(identifier)