| #!/usr/bin/env python3 |
| # Copyright 2017 The Dawn & Tint Authors |
| # |
| # Redistribution and use in source and binary forms, with or without |
| # modification, are permitted provided that the following conditions are met: |
| # |
| # 1. Redistributions of source code must retain the above copyright notice, this |
| # list of conditions and the following disclaimer. |
| # |
| # 2. Redistributions in binary form must reproduce the above copyright notice, |
| # this list of conditions and the following disclaimer in the documentation |
| # and/or other materials provided with the distribution. |
| # |
| # 3. Neither the name of the copyright holder nor the names of its |
| # contributors may be used to endorse or promote products derived from |
| # this software without specific prior written permission. |
| # |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| import json, os, sys |
| from collections import namedtuple, defaultdict |
| |
| from generator_lib import Generator, run_generator, FileRender |
| |
| ############################################################ |
| # OBJECT MODEL |
| ############################################################ |
| |
| |
| class Metadata: |
| def __init__(self, metadata): |
| self.api = metadata['api'] |
| self.namespace = metadata['namespace'] |
| self.c_prefix = metadata.get('c_prefix', self.namespace.upper()) |
| self.proc_table_prefix = metadata['proc_table_prefix'] |
| self.impl_dir = metadata.get('impl_dir', '') |
| self.native_namespace = metadata['native_namespace'] |
| self.copyright_year = metadata.get('copyright_year', None) |
| |
| class Name: |
| def __init__(self, name, native=False): |
| self.native = native |
| self.name = name |
| if native: |
| self.chunks = [name] |
| else: |
| self.chunks = name.split(' ') |
| |
| def get(self): |
| return self.name |
| |
| def CamelChunk(self, chunk): |
| return chunk[0].upper() + chunk[1:] |
| |
| def canonical_case(self): |
| return ' '.join(self.chunks) |
| |
| def concatcase(self): |
| return ''.join(self.chunks) |
| |
| def camelCase(self): |
| return self.chunks[0] + ''.join( |
| [self.CamelChunk(chunk) for chunk in self.chunks[1:]]) |
| |
| def CamelCase(self): |
| return ''.join([self.CamelChunk(chunk) for chunk in self.chunks]) |
| |
| def SNAKE_CASE(self): |
| return '_'.join([chunk.upper() for chunk in self.chunks]) |
| |
| def snake_case(self): |
| return '_'.join(self.chunks) |
| |
| def namespace_case(self): |
| return '::'.join(self.chunks) |
| |
| def Dirs(self): |
| return '/'.join(self.chunks) |
| |
| def js_enum_case(self): |
| result = self.chunks[0].lower() |
| for chunk in self.chunks[1:]: |
| if not result[-1].isdigit(): |
| result += '-' |
| result += chunk.lower() |
| return result |
| |
| def concat_names(*names): |
| return ' '.join([name.canonical_case() for name in names]) |
| |
| |
| class Type: |
| def __init__(self, name, json_data, native=False): |
| self.json_data = json_data |
| self.dict_name = name |
| self.name = Name(name, native=native) |
| self.category = json_data['category'] |
| self.is_wire_transparent = False |
| |
| |
| EnumValue = namedtuple('EnumValue', ['name', 'value', 'valid', 'json_data']) |
| |
| |
| class EnumType(Type): |
| def __init__(self, is_enabled, name, json_data): |
| Type.__init__(self, name, json_data) |
| |
| self.values = [] |
| self.hasUndefined = False |
| self.contiguousFromZero = True |
| lastValue = -1 |
| for m in self.json_data['values']: |
| if not is_enabled(m): |
| continue |
| value = m['value'] |
| value_name = m['name'] |
| tags = m.get('tags', []) |
| |
| prefix = 0 |
| if 'compat' in tags: |
| assert prefix == 0 |
| prefix = 0x0002_0000 |
| |
| if 'emscripten' in tags: |
| assert prefix == 0 |
| prefix = 0x0004_0000 |
| |
| if 'dawn' in tags: |
| assert prefix == 0 |
| prefix = 0x0005_0000 |
| |
| if prefix == 0 and 'native' in tags: |
| prefix = 0x0001_0000 |
| |
| value += prefix |
| |
| if value_name == "undefined": |
| assert value == 0 |
| self.hasUndefined = True |
| if value != lastValue + 1: |
| self.contiguousFromZero = False |
| lastValue = value |
| self.values.append( |
| EnumValue(Name(value_name), value, m.get('valid', True), m)) |
| |
| # Assert that all values are unique in enums |
| all_values = set() |
| for value in self.values: |
| if value.value in all_values: |
| raise Exception("Duplicate value {} in enum {}".format( |
| value.value, name)) |
| all_values.add(value.value) |
| self.is_wire_transparent = True |
| |
| |
| BitmaskValue = namedtuple('BitmaskValue', ['name', 'value', 'json_data']) |
| |
| |
| class BitmaskType(Type): |
| def __init__(self, is_enabled, name, json_data): |
| Type.__init__(self, name, json_data) |
| self.values = [ |
| BitmaskValue(Name(m['name']), m['value'], m) |
| for m in self.json_data['values'] if is_enabled(m) |
| ] |
| self.full_mask = 0 |
| for value in self.values: |
| self.full_mask = self.full_mask | value.value |
| self.is_wire_transparent = True |
| |
| |
| class CallbackFunctionType(Type): |
| |
| def __init__(self, is_enabled, name, json_data): |
| Type.__init__(self, name, json_data) |
| self.return_type = None |
| self.arguments = [] |
| |
| |
| class FunctionPointerType(Type): |
| def __init__(self, is_enabled, name, json_data): |
| Type.__init__(self, name, json_data) |
| self.return_type = None |
| self.arguments = [] |
| |
| |
| class TypedefType(Type): |
| def __init__(self, is_enabled, name, json_data): |
| Type.__init__(self, name, json_data) |
| self.type = None |
| |
| |
| class NativeType(Type): |
| def __init__(self, is_enabled, name, json_data): |
| Type.__init__(self, name, json_data, native=True) |
| self.is_wire_transparent = json_data.get('wire transparent', True) |
| |
| |
| # Methods and structures are both "records", so record members correspond to |
| # method arguments or structure members. |
| class RecordMember: |
| def __init__(self, |
| name, |
| typ, |
| annotation, |
| json_data, |
| optional=False, |
| is_return_value=False, |
| default_value=None, |
| skip_serialize=False): |
| self.name = name |
| self.type = typ |
| self.annotation = annotation |
| self.json_data = json_data |
| self.length = None |
| self.optional = optional |
| self.is_return_value = is_return_value |
| self.handle_type = None |
| self.id_type = None |
| self.default_value = default_value |
| self.skip_serialize = skip_serialize |
| |
| def set_handle_type(self, handle_type): |
| assert self.type.dict_name == "ObjectHandle" |
| self.handle_type = handle_type |
| |
| def set_id_type(self, id_type): |
| assert self.type.dict_name == "ObjectId" |
| self.id_type = id_type |
| |
| @property |
| def requires_struct_defaulting(self): |
| if self.annotation != "value": |
| return False |
| |
| if self.type.category == "structure": |
| return self.type.any_member_requires_struct_defaulting |
| elif self.type.category == "enum": |
| return (self.type.hasUndefined |
| and self.default_value not in [None, "undefined"]) |
| else: |
| return False |
| |
| |
| Method = namedtuple( |
| 'Method', ['name', 'return_type', 'arguments', 'autolock', 'json_data']) |
| |
| |
| class ObjectType(Type): |
| def __init__(self, is_enabled, name, json_data): |
| json_data_override = {'methods': []} |
| if 'methods' in json_data: |
| json_data_override['methods'] = [ |
| m for m in json_data['methods'] if is_enabled(m) |
| ] |
| Type.__init__(self, name, dict(json_data, **json_data_override)) |
| |
| |
| class Record: |
| def __init__(self, name): |
| self.name = Name(name) |
| self.members = [] |
| self.may_have_dawn_object = False |
| |
| def update_metadata(self): |
| def may_have_dawn_object(member): |
| if isinstance(member.type, ObjectType): |
| return True |
| elif isinstance(member.type, StructureType): |
| return member.type.may_have_dawn_object |
| else: |
| return False |
| |
| self.may_have_dawn_object = any( |
| may_have_dawn_object(member) for member in self.members) |
| |
| # Set may_have_dawn_object to true if the type is chained or |
| # extensible. Chained structs may contain a Dawn object. |
| if isinstance(self, StructureType): |
| self.may_have_dawn_object = (self.may_have_dawn_object |
| or self.chained or self.extensible) |
| |
| |
| class StructureType(Record, Type): |
| def __init__(self, is_enabled, name, json_data): |
| Record.__init__(self, name) |
| json_data_override = {} |
| if 'members' in json_data: |
| json_data_override['members'] = [ |
| m for m in json_data['members'] if is_enabled(m) |
| ] |
| Type.__init__(self, name, dict(json_data, **json_data_override)) |
| self.chained = json_data.get('chained', None) |
| self.extensible = json_data.get('extensible', None) |
| if self.chained: |
| assert self.chained == 'in' or self.chained == 'out' |
| assert 'chain roots' in json_data |
| self.chain_roots = [] |
| if self.extensible: |
| assert self.extensible == 'in' or self.extensible == 'out' |
| # Chained structs inherit from wgpu::ChainedStruct, which has |
| # nextInChain, so setting both extensible and chained would result in |
| # two nextInChain members. |
| assert not (self.extensible and self.chained) |
| self.extensions = [] |
| |
| def update_metadata(self): |
| Record.update_metadata(self) |
| |
| if self.may_have_dawn_object: |
| self.is_wire_transparent = False |
| return |
| |
| assert not (self.chained or self.extensible) |
| |
| def get_is_wire_transparent(member): |
| return member.type.is_wire_transparent and member.annotation == 'value' |
| |
| self.is_wire_transparent = all( |
| get_is_wire_transparent(m) for m in self.members) |
| |
| @property |
| def output(self): |
| return self.chained == "out" or self.extensible == "out" |
| |
| @property |
| def has_free_members_function(self): |
| if not self.output: |
| return False |
| for m in self.members: |
| if m.annotation != 'value': |
| return True |
| return False |
| |
| @property |
| def any_member_requires_struct_defaulting(self): |
| return any(member.requires_struct_defaulting |
| for member in self.members) |
| |
| @property |
| # Returns True if the structure can be created with no parameters, e.g. all of its members have |
| # defaults or are optional, |
| def has_basic_constructor(self): |
| return all((member.optional or member.default_value) |
| for member in self.members) |
| |
| |
| class CallbackInfoType(StructureType): |
| |
| def __init__(self, is_enabled, name, json_data): |
| StructureType.__init__(self, is_enabled, name, json_data) |
| self.extensible = 'in' |
| |
| |
| class ConstantDefinition(): |
| def __init__(self, is_enabled, name, json_data): |
| self.type = None |
| self.value = json_data['value'] |
| self.json_data = json_data |
| self.name = Name(name) |
| |
| |
| class FunctionDeclaration(): |
| def __init__(self, is_enabled, name, json_data, no_cpp=False): |
| self.return_type = None |
| self.arguments = [] |
| self.json_data = json_data |
| self.name = Name(name) |
| self.no_cpp = no_cpp |
| |
| |
| class Command(Record): |
| def __init__(self, name, members=None): |
| Record.__init__(self, name) |
| self.members = members or [] |
| self.derived_object = None |
| self.derived_method = None |
| |
| |
| def linked_record_members(json_data, types): |
| members = [] |
| members_by_name = {} |
| for m in json_data: |
| member = RecordMember(Name(m['name']), |
| types[m['type']], |
| m.get('annotation', 'value'), |
| m, |
| optional=m.get('optional', False), |
| is_return_value=m.get('is_return_value', False), |
| default_value=m.get('default', None), |
| skip_serialize=m.get('skip_serialize', False)) |
| handle_type = m.get('handle_type') |
| if handle_type: |
| member.set_handle_type(types[handle_type]) |
| id_type = m.get('id_type') |
| if id_type: |
| member.set_id_type(types[id_type]) |
| members.append(member) |
| members_by_name[member.name.canonical_case()] = member |
| |
| for (member, m) in zip(members, json_data): |
| if member.annotation != 'value': |
| if not 'length' in m: |
| if member.type.category != 'object': |
| member.length = "constant" |
| member.constant_length = 1 |
| else: |
| assert False |
| elif m['length'] == 'strlen': |
| member.length = 'strlen' |
| elif isinstance(m['length'], int): |
| assert m['length'] > 0 |
| member.length = "constant" |
| member.constant_length = m['length'] |
| else: |
| member.length = members_by_name[m['length']] |
| |
| return members |
| |
| |
| def mark_lengths_non_serializable_lpm(record_members): |
| # Remove member length values from command metadata, |
| # these are set to the length of the protobuf array. |
| for record_member in record_members: |
| lengths = set() |
| for member in record_member.members: |
| lengths.add(member.length) |
| |
| for member in record_member.members: |
| if member in lengths: |
| member.skip_serialize = True |
| |
| ############################################################ |
| # PARSE |
| ############################################################ |
| |
| |
| def link_object(obj, types): |
| # Disable method's autolock if obj's "no autolock" = True |
| obj_scoped_autolock_enabled = not obj.json_data.get('no autolock', False) |
| |
| def make_method(json_data): |
| arguments = linked_record_members(json_data.get('args', []), types) |
| autolock_enabled = obj_scoped_autolock_enabled and not json_data.get( |
| 'no autolock', False) |
| return Method(Name(json_data['name']), |
| types[json_data.get('returns', 'void')], arguments, |
| autolock_enabled, json_data) |
| |
| obj.methods = [make_method(m) for m in obj.json_data.get('methods', [])] |
| obj.methods.sort(key=lambda method: method.name.canonical_case()) |
| |
| |
| def link_structure(struct, types): |
| struct.members = linked_record_members(struct.json_data['members'], types) |
| for root in struct.json_data.get('chain roots', []): |
| struct.chain_roots.append(types[root]) |
| types[root].extensions.append(struct) |
| struct.chain_roots = [types[root] for root in struct.json_data.get('chain roots', [])] |
| assert all((root.category == 'structure' for root in struct.chain_roots)) |
| |
| |
| def link_function_pointer(function_pointer, types): |
| link_function(function_pointer, types) |
| |
| |
| def link_typedef(typedef, types): |
| typedef.type = types[typedef.json_data['type']] |
| |
| |
| def link_constant(constant, types): |
| constant.type = types[constant.json_data['type']] |
| assert constant.type.name.native |
| |
| |
| def link_function(function, types): |
| function.return_type = types[function.json_data.get('returns', 'void')] |
| function.arguments = linked_record_members(function.json_data['args'], |
| types) |
| |
| # Sort structures so that if struct A has struct B as a member, then B is |
| # listed before A. |
| # |
| # This is a form of topological sort where we try to keep the order reasonably |
| # similar to the original order (though the sort isn't technically stable). |
| # |
| # It works by computing for each struct type what is the depth of its DAG of |
| # dependents, then re-sorting based on that depth using Python's stable sort. |
| # This makes a toposort because if A depends on B then its depth will be bigger |
| # than B's. It is also nice because all nodes with the same depth are kept in |
| # the input order. |
| def topo_sort_structure(structs): |
| for struct in structs: |
| struct.visited = False |
| struct.subdag_depth = 0 |
| |
| def compute_depth(struct): |
| if struct.visited: |
| return struct.subdag_depth |
| |
| max_dependent_depth = 0 |
| for member in struct.members: |
| if member.type.category == 'structure': |
| max_dependent_depth = max(max_dependent_depth, |
| compute_depth(member.type) + 1) |
| |
| struct.subdag_depth = max_dependent_depth |
| struct.visited = True |
| return struct.subdag_depth |
| |
| for struct in structs: |
| compute_depth(struct) |
| |
| result = sorted(structs, key=lambda struct: struct.subdag_depth) |
| |
| for struct in structs: |
| del struct.visited |
| del struct.subdag_depth |
| |
| return result |
| |
| |
| def parse_json(json, enabled_tags, disabled_tags=None): |
| is_enabled = lambda json_data: item_is_enabled( |
| enabled_tags, json_data) and not item_is_disabled( |
| disabled_tags, json_data) |
| category_to_parser = { |
| 'bitmask': BitmaskType, |
| 'callback function': CallbackFunctionType, |
| 'callback info': CallbackInfoType, |
| 'enum': EnumType, |
| 'native': NativeType, |
| 'function pointer': FunctionPointerType, |
| 'object': ObjectType, |
| 'structure': StructureType, |
| 'typedef': TypedefType, |
| 'constant': ConstantDefinition, |
| 'function': FunctionDeclaration |
| } |
| |
| types = {} |
| |
| by_category = {} |
| for name in category_to_parser.keys(): |
| by_category[name] = [] |
| |
| for (name, json_data) in json.items(): |
| if name[0] == '_' or not is_enabled(json_data): |
| continue |
| category = json_data['category'] |
| parsed = category_to_parser[category](is_enabled, name, json_data) |
| by_category[category].append(parsed) |
| types[name] = parsed |
| |
| for obj in by_category['object']: |
| link_object(obj, types) |
| |
| for struct in by_category['structure']: |
| link_structure(struct, types) |
| |
| if struct.has_free_members_function: |
| name = struct.name.get() + " free members" |
| func_decl = FunctionDeclaration( |
| True, |
| name, { |
| "returns": |
| "void", |
| "args": [{ |
| "name": "value", |
| "type": struct.name.get(), |
| "annotation": "value", |
| }] |
| }, |
| no_cpp=True) |
| types[name] = func_decl |
| by_category['function'].append(func_decl) |
| |
| for callback_info in by_category['callback info']: |
| link_structure(callback_info, types) |
| |
| for callback_function in by_category['callback function']: |
| link_function_pointer(callback_function, types) |
| |
| for function_pointer in by_category['function pointer']: |
| link_function_pointer(function_pointer, types) |
| |
| for typedef in by_category['typedef']: |
| link_typedef(typedef, types) |
| |
| for constant in by_category['constant']: |
| link_constant(constant, types) |
| |
| for function in by_category['function']: |
| link_function(function, types) |
| |
| for category in by_category.keys(): |
| by_category[category] = sorted( |
| by_category[category], key=lambda typ: typ.name.canonical_case()) |
| |
| by_category['structure'] = topo_sort_structure(by_category['structure']) |
| |
| for struct in by_category['structure']: |
| struct.update_metadata() |
| |
| api_params = { |
| 'types': types, |
| 'by_category': by_category, |
| 'enabled_tags': enabled_tags, |
| 'disabled_tags': disabled_tags, |
| } |
| return { |
| 'metadata': Metadata(json['_metadata']), |
| 'types': types, |
| 'by_category': by_category, |
| 'enabled_tags': enabled_tags, |
| 'disabled_tags': disabled_tags, |
| 'c_methods': lambda typ: c_methods(api_params, typ), |
| 'c_methods_sorted_by_name': get_c_methods_sorted_by_name(api_params), |
| } |
| |
| |
| ############################################################ |
| # WIRE STUFF |
| ############################################################ |
| |
| |
| # Create wire commands from api methods |
| def compute_wire_params(api_params, wire_json): |
| wire_params = api_params.copy() |
| types = wire_params['types'] |
| |
| commands = [] |
| return_commands = [] |
| |
| wire_json['special items']['client_handwritten_commands'] += wire_json[ |
| 'special items']['client_side_commands'] |
| |
| # Generate commands from object methods |
| for api_object in wire_params['by_category']['object']: |
| for method in api_object.methods: |
| command_name = concat_names(api_object.name, method.name) |
| command_suffix = Name(command_name).CamelCase() |
| |
| # Only object return values or void are supported. |
| # Other methods must be handwritten. |
| is_object = method.return_type.category == 'object' |
| is_void = method.return_type.name.canonical_case() == 'void' |
| if not (is_object or is_void): |
| assert command_suffix in ( |
| wire_json['special items']['client_handwritten_commands'] |
| ), command_suffix |
| continue |
| |
| if command_suffix in ( |
| wire_json['special items']['client_side_commands']): |
| continue |
| |
| # Create object method commands by prepending "self" |
| members = [ |
| RecordMember(Name('self'), types[api_object.dict_name], |
| 'value', {}) |
| ] |
| members += method.arguments |
| |
| # Client->Server commands that return an object return the |
| # result object handle |
| if method.return_type.category == 'object': |
| result = RecordMember(Name('result'), |
| types['ObjectHandle'], |
| 'value', {}, |
| is_return_value=True) |
| result.set_handle_type(method.return_type) |
| members.append(result) |
| |
| command = Command(command_name, members) |
| command.derived_object = api_object |
| command.derived_method = method |
| commands.append(command) |
| |
| for (name, json_data) in wire_json['commands'].items(): |
| commands.append(Command(name, linked_record_members(json_data, types))) |
| |
| for (name, json_data) in wire_json['return commands'].items(): |
| return_commands.append( |
| Command(name, linked_record_members(json_data, types))) |
| |
| wire_params['cmd_records'] = { |
| 'command': commands, |
| 'return command': return_commands |
| } |
| |
| for commands in wire_params['cmd_records'].values(): |
| for command in commands: |
| command.update_metadata() |
| commands.sort(key=lambda c: c.name.canonical_case()) |
| |
| wire_params.update(wire_json.get('special items', {})) |
| |
| return wire_params |
| |
| ############################################################ |
| # DAWN LPM FUZZ STUFF |
| ############################################################ |
| |
| |
| def compute_lpm_params(api_and_wire_params, lpm_json): |
| # Start with all commands in dawn.json and dawn_wire.json |
| lpm_params = api_and_wire_params.copy() |
| |
| # Commands that are built through codegen |
| generated_commands = [] |
| |
| # All commands, including hand written commands that we can't generate |
| # through codegen |
| all_commands = [] |
| |
| # Remove blocklisted commands from protobuf generation params |
| blocklisted_cmds_proto = lpm_json.get('blocklisted_cmds') |
| custom_cmds_proto = lpm_json.get('custom_cmds') |
| for command in lpm_params['cmd_records']['command']: |
| blocklisted = command.name.get() in blocklisted_cmds_proto |
| custom = command.name.get() in custom_cmds_proto |
| |
| if blocklisted: |
| continue |
| |
| if not custom: |
| generated_commands.append(command) |
| all_commands.append(command) |
| |
| # Set all fields that are marked as the "length" of another field to |
| # skip_serialize. The values passed by libprotobuf-mutator will cause |
| # an instant crash during serialization if these don't match the length |
| # of the data they are passing. These values aren't used in |
| # deserialization. |
| mark_lengths_non_serializable_lpm( |
| api_and_wire_params['cmd_records']['command']) |
| mark_lengths_non_serializable_lpm( |
| api_and_wire_params['by_category']['structure']) |
| |
| lpm_params['cmd_records'] = { |
| 'proto_generated_commands': generated_commands, |
| 'proto_all_commands': all_commands, |
| 'cpp_generated_commands': generated_commands, |
| 'lpm_info': lpm_json.get("lpm_info") |
| } |
| |
| return lpm_params |
| |
| |
| def as_protobufTypeLPM(member): |
| assert 'type' in member.json_data |
| |
| if member.type.name.native: |
| typ = member.json_data['type'] |
| cpp_to_protobuf_type = { |
| "bool": "bool", |
| "float": "float", |
| "double": "double", |
| "int8_t": "int32", |
| "int16_t": "int32", |
| "int32_t": "int32", |
| "int64_t": "int64", |
| "uint8_t": "uint32", |
| "uint16_t": "uint32", |
| "uint32_t": "uint32", |
| "uint64_t": "uint64", |
| "size_t": "uint64", |
| } |
| |
| assert typ in cpp_to_protobuf_type |
| |
| return cpp_to_protobuf_type[typ] |
| |
| return member.type.name.CamelCase() |
| |
| |
| # Helper that generates names for protobuf grammars from contents |
| # of dawn*.json like files. example: membera |
| def as_protobufNameLPM(*names): |
| # `descriptor` is a reserved keyword in lib-protobuf-mutator |
| if (names[0].concatcase() == "descriptor"): |
| return "desc" |
| return as_varName(*names) |
| |
| |
| # Helper to generate member accesses within C++ of protobuf objects |
| # example: cmd.membera().memberb() |
| def as_protobufMemberNameLPM(*names): |
| # `descriptor` is a reserved keyword in lib-protobuf-mutator |
| if (names[0].concatcase() == "descriptor"): |
| return "desc" |
| return ''.join([name.concatcase().lower() for name in names]) |
| |
| |
| def unreachable_code(): |
| assert False |
| |
| |
| ############################################################ |
| # KOTLIN STUFF |
| ############################################################ |
| |
| |
| def compute_kotlin_params(loaded_json, kotlin_json): |
| params_kotlin = parse_json(loaded_json, enabled_tags=['art']) |
| params_kotlin['kotlin_package'] = kotlin_json['kotlin_package'] |
| params_kotlin['jni_primitives'] = kotlin_json['jni_primitives'] |
| kt_file_path = params_kotlin['kotlin_package'].replace('.', '/') |
| |
| # The 'length' members are removed as Kotlin can infer that from the container. |
| # 'length' members are identified when some *other* member specifies its name as the |
| # length parameter. Void pointer members refer to binary structures that cannot be used by |
| # clients without conversion to ART types in handwritten code, so we don't convert those. |
| def include_structure_member(structure, member): |
| if member.type.category in ['callback info', 'function pointer']: |
| return False |
| if member.type.name.get() in ['void *', 'void const *']: |
| return False |
| for other in structure.members: |
| if other.length == member: |
| return False |
| return True |
| |
| def filter_arguments(arguments): |
| # TODO(b/352047733): Replace methods that require special handling with an exceptions list. |
| for argument in arguments: |
| # length parameters are omitted because Kotlin containers have 'length'. |
| if argument in [arg.length for arg in arguments]: |
| continue |
| |
| # userdata parameter omitted because Kotlin clients can achieve the same with closures. |
| if argument.name.get() == 'userdata': |
| continue |
| |
| # Dawn uses 'annotation = *' for output parameters, for example to return arrays. |
| # We convert the return type and strip out the parameters. |
| if argument.annotation == '*': |
| continue |
| |
| yield argument |
| |
| def kotlin_return(method): |
| for argument in method.arguments: |
| if argument.annotation == '*': |
| # TODO(b/352048981): Use handwritten methods for container returns to avoid the need |
| # for special casing logic. |
| if method.return_type.name.get() == 'size_t': |
| return argument |
| |
| return {"type": method.return_type} |
| |
| def include_method(method): |
| if method.return_type.category == 'function pointer': |
| # Kotlin doesn't support returning functions. |
| return False |
| for argument in method.arguments: |
| if (argument.annotation == '*' |
| and method.return_type.name.get() != 'size_t'): |
| # Dawn uses 'annotation = *' for output parameters, for example to return arrays. |
| # Kotlin doesn't support that at the moment, unless a container is returned. |
| return False |
| if argument.type.category == 'callback info': |
| # We don't handle this yet. |
| return False |
| if argument.annotation == 'value' and argument.type.category == 'structure': |
| # Passing structures by value is not supported at the moment. |
| return False |
| if argument.type.category == 'function pointer': |
| # Currently returning structures in callbacks is not supported. |
| for callback_arg in argument.type.arguments: |
| if callback_arg.type.category == 'structure': |
| return False |
| return True |
| |
| def jni_name(type): |
| return kt_file_path + '/' + type.name.CamelCase() |
| |
| # We assume that if the final two parameters are named 'userdata' and 'callback' respectively |
| # that this is an async method that uses function pointer based callbacks. |
| def is_async_method(method): |
| if len(method.arguments) < 3: |
| return False # Not enough parameters to be an async method. |
| if method.arguments[-1].name.get() != 'userdata': |
| return False |
| if method.arguments[-2].name.get() != 'callback': |
| return False |
| return True |
| |
| # A structure may need to know which other structures listed it as a chain root, e.g. |
| # to know whether to mark the generated class 'open'. |
| chain_children = defaultdict(list) |
| for structure in params_kotlin['by_category']['structure']: |
| for chain_root in structure.chain_roots: |
| chain_children[chain_root.name.get()].append(structure) |
| params_kotlin['chain_children'] = chain_children |
| params_kotlin['filter_arguments'] = filter_arguments |
| params_kotlin['include_structure_member'] = include_structure_member |
| params_kotlin['kotlin_return'] = kotlin_return |
| params_kotlin['include_method'] = include_method |
| params_kotlin['jni_name'] = jni_name |
| params_kotlin['is_async_method'] = is_async_method |
| return params_kotlin |
| |
| |
| ############################################################# |
| # Generator |
| ############################################################# |
| |
| |
| def as_varName(*names): |
| return names[0].camelCase() + ''.join( |
| [name.CamelCase() for name in names[1:]]) |
| |
| |
| def as_cType(c_prefix, name): |
| # Special case for 'bool' because it has a typedef for compatibility. |
| if name.native and name.get() != 'bool': |
| return name.concatcase() |
| else: |
| return c_prefix + name.CamelCase() |
| |
| def as_cppType(name): |
| # Special case for 'bool' because it has a typedef for compatibility. |
| if name.native and name.get() != 'bool': |
| return name.concatcase() |
| else: |
| return name.CamelCase() |
| |
| |
| def as_ktName(name): |
| return '_' + name if '0' <= name[0] <= '9' else name |
| |
| |
| def as_jsEnumValue(value): |
| if 'jsrepr' in value.json_data: return value.json_data['jsrepr'] |
| return "'" + value.name.js_enum_case() + "'" |
| |
| |
| def has_wasmType(return_type, args): |
| return all(map(lambda x: len(as_wasmType(x)) == 1, [return_type] + args)) |
| |
| |
| # Returns a single character wasm type (v/p/i/j/f/d) if valid, a "(longer string)" if not |
| def as_wasmType(x): |
| if isinstance(x, RecordMember): |
| if x.annotation == 'value': |
| x = x.type |
| elif '*' in x.annotation: |
| return 'p' |
| else: |
| return f'({x})' |
| |
| if isinstance(x, Type): |
| if x.category == 'enum': |
| return 'i' |
| elif x.category == 'bitmask': |
| # TODO(crbug.com/347732150): Change to 'j' when bitmasks are 64-bit |
| return 'i' |
| elif x.category in ['object', 'function pointer']: |
| return 'p' |
| elif x.category == 'native': |
| return x.json_data.get('wasm type', f'({x.name.name})') |
| elif x.category in ['structure', 'callback info']: |
| return f'({x.name.name})' # Invalid |
| else: |
| assert False, 'Type -> ' + x.category |
| assert False, x |
| |
| |
| def convert_cType_to_cppType(typ, annotation, arg, indent=0): |
| if typ.category == 'native': |
| return arg |
| if annotation == 'value': |
| if typ.category == 'object': |
| return '{}::Acquire({})'.format(as_cppType(typ.name), arg) |
| elif typ.category == 'structure': |
| converted_members = [ |
| convert_cType_to_cppType( |
| member.type, member.annotation, |
| '{}.{}'.format(arg, as_varName(member.name)), indent + 1) |
| for member in typ.members |
| ] |
| |
| converted_members = [(' ' * 4) + m for m in converted_members] |
| converted_members = ',\n'.join(converted_members) |
| |
| return as_cppType(typ.name) + ' {\n' + converted_members + '\n}' |
| elif typ.category == 'function pointer': |
| return 'reinterpret_cast<{}>({})'.format(as_cppType(typ.name), arg) |
| else: |
| return 'static_cast<{}>({})'.format(as_cppType(typ.name), arg) |
| else: |
| return 'reinterpret_cast<{} {}>({})'.format(as_cppType(typ.name), |
| annotation, arg) |
| |
| |
| def decorate(name, typ, arg, make_const=False): |
| maybe_const = ' const ' if make_const else ' ' |
| if arg.annotation == 'value': |
| return typ + maybe_const + name |
| elif arg.annotation == '*': |
| return typ + ' *' + maybe_const + name |
| elif arg.annotation == 'const*': |
| return typ + ' const *' + maybe_const + name |
| elif arg.annotation == 'const*const*': |
| return 'const ' + typ + '* const *' + maybe_const + name |
| else: |
| assert False |
| |
| |
| def annotated(typ, arg, make_const=False): |
| name = as_varName(arg.name) |
| return decorate(name, typ, arg, make_const) |
| |
| |
| def item_is_enabled(enabled_tags, json_data): |
| tags = json_data.get('tags') |
| if tags is None: return True |
| return any(tag in enabled_tags for tag in tags) |
| |
| |
| def item_is_disabled(disabled_tags, json_data): |
| if disabled_tags is None: return False |
| tags = json_data.get('tags') |
| if tags is None: return False |
| |
| return any(tag in disabled_tags for tag in tags) |
| |
| |
| def as_cppEnum(value_name): |
| assert not value_name.native |
| if value_name.concatcase()[0].isdigit(): |
| return "e" + value_name.CamelCase() |
| return value_name.CamelCase() |
| |
| |
| def as_MethodSuffix(type_name, method_name): |
| assert not type_name.native and not method_name.native |
| return type_name.CamelCase() + method_name.CamelCase() |
| |
| |
| def as_CppMethodSuffix(type_name, method_name): |
| assert not type_name.native and not method_name.native |
| original_method_name_str = method_name.CamelCase() |
| if method_name.chunks[-1] == 'f': |
| return type_name.CamelCase() + original_method_name_str[:-1] |
| return type_name.CamelCase() + original_method_name_str |
| |
| |
| def as_frontendType(metadata, typ): |
| if typ.category == 'object': |
| return typ.name.CamelCase() + 'Base*' |
| elif typ.category in ['bitmask', 'enum'] or typ.name.get() == 'bool': |
| return metadata.namespace + '::' + typ.name.CamelCase() |
| elif typ.category == 'structure': |
| return as_cppType(typ.name) |
| else: |
| return as_cType(metadata.c_prefix, typ.name) |
| |
| |
| def as_wireType(metadata, typ): |
| if typ.category == 'object': |
| return typ.name.CamelCase() + '*' |
| elif typ.category in ['bitmask', 'enum', 'structure']: |
| return metadata.c_prefix + typ.name.CamelCase() |
| else: |
| return as_cppType(typ.name) |
| |
| |
| def c_methods(params, typ): |
| return typ.methods + [ |
| Method(Name('add ref'), params['types']['void'], [], False, {}), |
| Method(Name('release'), params['types']['void'], [], False, {}), |
| ] |
| |
| def get_c_methods_sorted_by_name(api_params): |
| unsorted = [(as_MethodSuffix(typ.name, method.name), typ, method) \ |
| for typ in api_params['by_category']['object'] \ |
| for method in c_methods(api_params, typ) ] |
| return [(typ, method) for (_, typ, method) in sorted(unsorted)] |
| |
| |
| def find_by_name(members, name): |
| for member in members: |
| if member.name.get() == name: |
| return member |
| assert False |
| |
| |
| def has_callback_arguments(method): |
| return any(arg.type.category == 'function pointer' for arg in method.arguments) |
| |
| |
| # TODO: crbug.com/dawn/2509 - Remove this helper when once we deprecate older APIs. |
| def has_callback_info(method): |
| return method.return_type.name.get() == 'future' and any( |
| arg.name.get() == 'callback info' |
| and arg.type.category != 'callback info' for arg in method.arguments) |
| |
| |
| def has_callbackInfoStruct(method): |
| return any(arg.type.category == 'callback info' |
| for arg in method.arguments) |
| |
| |
| def is_wire_serializable(type): |
| # Function pointers, callback functions, and "void *" types (i.e. userdata) cannot |
| # be serialized. |
| return (type.category != 'function pointer' |
| and type.category != 'callback info' |
| and type.category != 'callback function' |
| and type.name.get() != 'void *') |
| |
| |
| def make_base_render_params(metadata): |
| c_prefix = metadata.c_prefix |
| |
| def as_cTypeEnumSpecialCase(typ): |
| return as_cType(c_prefix, typ.name) |
| |
| def as_cEnum(type_name, value_name): |
| assert not type_name.native and not value_name.native |
| return c_prefix + type_name.CamelCase() + '_' + value_name.CamelCase() |
| |
| def as_cMethodNamespaced(type_name, method_name, namespace=None): |
| c_method = c_prefix.lower() |
| if namespace is not None: |
| c_method += namespace.CamelCase() |
| if type_name is not None: |
| assert not type_name.native |
| c_method += type_name.CamelCase() |
| assert not method_name.native |
| c_method += method_name.CamelCase() |
| return c_method |
| |
| def as_cMethod(type_name, method_name): |
| return as_cMethodNamespaced(type_name, method_name) |
| |
| def as_cProc(type_name, method_name): |
| c_proc = c_prefix + 'Proc' |
| if type_name != None: |
| assert not type_name.native |
| c_proc += type_name.CamelCase() |
| assert not method_name.native |
| c_proc += method_name.CamelCase() |
| return c_proc |
| |
| return { |
| 'Name': lambda name: Name(name), |
| 'as_annotated_cType': \ |
| lambda arg, make_const=False: annotated(as_cTypeEnumSpecialCase(arg.type), arg, make_const), |
| 'as_annotated_cppType': \ |
| lambda arg, make_const=False: annotated(as_cppType(arg.type.name), arg, make_const), |
| 'as_cEnum': as_cEnum, |
| 'as_cppEnum': as_cppEnum, |
| 'as_cMethod': as_cMethod, |
| 'as_cMethodNamespaced': as_cMethodNamespaced, |
| 'as_MethodSuffix': as_MethodSuffix, |
| 'as_CppMethodSuffix': as_CppMethodSuffix, |
| 'as_cProc': as_cProc, |
| 'as_cType': lambda name: as_cType(c_prefix, name), |
| 'as_cppType': as_cppType, |
| 'as_jsEnumValue': as_jsEnumValue, |
| 'has_wasmType': has_wasmType, |
| 'as_wasmType': as_wasmType, |
| 'convert_cType_to_cppType': convert_cType_to_cppType, |
| 'as_varName': as_varName, |
| 'decorate': decorate, |
| 'as_ktName': as_ktName, |
| 'has_callbackInfoStruct': has_callbackInfoStruct, |
| 'find_by_name': find_by_name, |
| 'unreachable_code': unreachable_code |
| } |
| |
| |
| class MultiGeneratorFromDawnJSON(Generator): |
| def get_description(self): |
| return 'Generates code for various target from Dawn.json.' |
| |
| def add_commandline_arguments(self, parser): |
| allowed_targets = [ |
| 'dawn_headers', 'cpp_headers', 'cpp', 'proc', 'mock_api', 'wire', |
| 'native_utils', 'dawn_lpmfuzz_cpp', 'dawn_lpmfuzz_proto', 'kotlin' |
| ] |
| |
| parser.add_argument('--dawn-json', |
| required=True, |
| type=str, |
| help='The DAWN JSON definition to use.') |
| parser.add_argument('--wire-json', |
| default=None, |
| type=str, |
| help='The DAWN WIRE JSON definition to use.') |
| parser.add_argument('--kotlin-json', |
| default=None, |
| type=str, |
| help='The KOTLIN JSON definition to use.') |
| parser.add_argument("--lpm-json", |
| default=None, |
| type=str, |
| help='The DAWN LPM FUZZER definitions to use.') |
| parser.add_argument( |
| '--targets', |
| required=True, |
| type=str, |
| help= |
| 'Comma-separated subset of targets to output. Available targets: ' |
| + ', '.join(allowed_targets)) |
| |
| def get_file_renders(self, args): |
| with open(args.dawn_json) as f: |
| loaded_json = json.loads(f.read()) |
| |
| targets = args.targets.split(',') |
| |
| wire_json = None |
| if args.wire_json: |
| with open(args.wire_json) as f: |
| wire_json = json.loads(f.read()) |
| |
| kotlin_json = None |
| if args.kotlin_json: |
| with open(args.kotlin_json) as f: |
| kotlin_json = json.loads(f.read()) |
| |
| lpm_json = None |
| if args.lpm_json: |
| with open(args.lpm_json) as f: |
| lpm_json = json.loads(f.read()) |
| |
| renders = [] |
| |
| params_dawn = parse_json( |
| loaded_json, |
| enabled_tags=['compat', 'dawn', 'native', 'deprecated']) |
| |
| params_all = parse_json(loaded_json, |
| enabled_tags=[ |
| 'compat', 'dawn', 'emscripten', 'native', |
| 'deprecated' |
| ]) |
| |
| metadata = params_dawn['metadata'] |
| RENDER_PARAMS_BASE = make_base_render_params(metadata) |
| |
| api = metadata.api.lower() |
| prefix = metadata.proc_table_prefix.lower() |
| if 'headers' in targets: |
| renders.append( |
| FileRender('api.h', 'include/dawn/' + api + '.h', |
| [RENDER_PARAMS_BASE, params_all])) |
| renders.append( |
| FileRender('dawn/wire/client/api.h', |
| 'include/dawn/wire/client/' + api + '.h', |
| [RENDER_PARAMS_BASE, params_dawn])) |
| renders.append( |
| FileRender('dawn_proc_table.h', |
| 'include/dawn/' + prefix + '_proc_table.h', |
| [RENDER_PARAMS_BASE, params_dawn])) |
| |
| if 'cpp_headers' in targets: |
| renders.append( |
| FileRender('api_cpp.h', 'include/dawn/' + api + '_cpp.h', [ |
| RENDER_PARAMS_BASE, params_all, { |
| 'c_header': api + '/' + api + '.h', |
| 'c_namespace': None, |
| } |
| ])) |
| |
| renders.append( |
| FileRender( |
| 'api_cpp.h', 'include/dawn/wire/client/' + api + '_cpp.h', |
| [ |
| RENDER_PARAMS_BASE, params_dawn, { |
| 'c_header': 'dawn/wire/client/' + api + '.h', |
| 'c_namespace': Name('dawn wire client'), |
| } |
| ])) |
| |
| renders.append( |
| FileRender('api_cpp_print.h', |
| 'include/dawn/' + api + '_cpp_print.h', |
| [RENDER_PARAMS_BASE, params_dawn])) |
| |
| renders.append( |
| FileRender('api_cpp_chained_struct.h', |
| 'include/webgpu/' + api + '_cpp_chained_struct.h', |
| [RENDER_PARAMS_BASE, params_dawn])) |
| |
| if 'proc' in targets: |
| renders.append( |
| FileRender('dawn_proc.cpp', 'src/dawn/' + prefix + '_proc.cpp', |
| [RENDER_PARAMS_BASE, params_dawn])) |
| renders.append( |
| FileRender('dawn_thread_dispatch_proc.cpp', |
| 'src/dawn/' + prefix + '_thread_dispatch_proc.cpp', |
| [RENDER_PARAMS_BASE, params_dawn])) |
| |
| if 'webgpu_dawn_native_proc' in targets: |
| renders.append( |
| FileRender('dawn/native/api_dawn_native_proc.cpp', |
| 'src/dawn/native/webgpu_dawn_native_proc.cpp', |
| [RENDER_PARAMS_BASE, params_dawn])) |
| |
| if 'webgpu_headers' in targets: |
| params_upstream = parse_json( |
| loaded_json, |
| enabled_tags=['compat', 'upstream', 'native'], |
| disabled_tags=['dawn']) |
| renders.append( |
| FileRender('api.h', 'webgpu-headers/' + api + '.h', |
| [RENDER_PARAMS_BASE, params_upstream])) |
| |
| if 'emdawnwebgpu_headers' in targets: |
| assert api == 'webgpu' |
| params_emscripten = parse_json( |
| loaded_json, enabled_tags=['compat', 'emscripten']) |
| # system/include/webgpu |
| renders.append( |
| FileRender('api.h', 'src/emdawnwebgpu/include/webgpu/webgpu.h', |
| [RENDER_PARAMS_BASE, params_emscripten])) |
| renders.append( |
| FileRender('api_cpp.h', |
| 'src/emdawnwebgpu/include/webgpu/webgpu_cpp.h', [ |
| RENDER_PARAMS_BASE, params_emscripten, { |
| 'c_header': api + '/' + api + '.h', |
| 'c_namespace': None, |
| } |
| ])) |
| renders.append( |
| FileRender( |
| 'api_cpp_chained_struct.h', |
| 'src/emdawnwebgpu/include/webgpu/webgpu_cpp_chained_struct.h', |
| [RENDER_PARAMS_BASE, params_emscripten])) |
| |
| if 'emdawnwebgpu_js' in targets: |
| assert api == 'webgpu' |
| params_emscripten = parse_json( |
| loaded_json, enabled_tags=['compat', 'emscripten']) |
| renders.append( |
| FileRender('emdawnwebgpu/webgpu_struct_info.json', |
| 'src/emdawnwebgpu/webgpu_struct_info.json', |
| [RENDER_PARAMS_BASE, params_emscripten])) |
| renders.append( |
| FileRender('emdawnwebgpu/library_webgpu_enum_tables.js', |
| 'src/emdawnwebgpu/library_webgpu_enum_tables.js', |
| [RENDER_PARAMS_BASE, params_emscripten])) |
| renders.append( |
| FileRender( |
| 'emdawnwebgpu/library_webgpu_generated_sig_info.js', |
| 'src/emdawnwebgpu/library_webgpu_generated_sig_info.js', |
| [RENDER_PARAMS_BASE, params_emscripten])) |
| |
| if 'mock_api' in targets: |
| mock_params = [ |
| RENDER_PARAMS_BASE, params_dawn, { |
| 'has_callback_arguments': has_callback_arguments, |
| "has_callback_info": has_callback_info |
| } |
| ] |
| renders.append( |
| FileRender('mock_api.h', 'src/dawn/mock_' + api + '.h', |
| mock_params)) |
| renders.append( |
| FileRender('mock_api.cpp', 'src/dawn/mock_' + api + '.cpp', |
| mock_params)) |
| |
| if 'native_utils' in targets: |
| frontend_params = [ |
| RENDER_PARAMS_BASE, |
| params_dawn, |
| { |
| # TODO: as_frontendType and co. take a Type, not a Name :( |
| 'as_frontendType': lambda typ: as_frontendType(metadata, typ), |
| 'as_annotated_frontendType': \ |
| lambda arg: annotated(as_frontendType(metadata, arg.type), arg), |
| } |
| ] |
| |
| impl_dir = metadata.impl_dir + '/' if metadata.impl_dir else '' |
| native_dir = impl_dir + Name(metadata.native_namespace).Dirs() |
| namespace = metadata.namespace |
| renders.append( |
| FileRender('dawn/native/ValidationUtils.h', |
| 'src/' + native_dir + '/ValidationUtils_autogen.h', |
| frontend_params)) |
| renders.append( |
| FileRender('dawn/native/ValidationUtils.cpp', |
| 'src/' + native_dir + '/ValidationUtils_autogen.cpp', |
| frontend_params)) |
| renders.append( |
| FileRender('dawn/native/dawn_platform.h', |
| 'src/' + native_dir + '/' + prefix + '_platform_autogen.h', |
| frontend_params)) |
| renders.append( |
| FileRender('dawn/native/api_structs.h', |
| 'src/' + native_dir + '/' + namespace + '_structs_autogen.h', |
| frontend_params)) |
| renders.append( |
| FileRender('dawn/native/api_structs.cpp', |
| 'src/' + native_dir + '/' + namespace + '_structs_autogen.cpp', |
| frontend_params)) |
| renders.append( |
| FileRender('dawn/native/ProcTable.cpp', |
| 'src/' + native_dir + '/ProcTable.cpp', frontend_params)) |
| renders.append( |
| FileRender('dawn/native/ChainUtils.h', |
| 'src/' + native_dir + '/ChainUtils_autogen.h', |
| frontend_params)) |
| renders.append( |
| FileRender('dawn/native/ChainUtils.cpp', |
| 'src/' + native_dir + '/ChainUtils_autogen.cpp', |
| frontend_params)) |
| renders.append( |
| FileRender('dawn/native/Features.h', |
| 'src/' + native_dir + '/Features_autogen.h', |
| frontend_params)) |
| renders.append( |
| FileRender('dawn/native/Features.inl', |
| 'src/' + native_dir + '/Features_autogen.inl', |
| frontend_params)) |
| renders.append( |
| FileRender('dawn/native/api_absl_format.h', |
| 'src/' + native_dir + '/' + api + '_absl_format_autogen.h', |
| frontend_params)) |
| renders.append( |
| FileRender('dawn/native/api_absl_format.cpp', |
| 'src/' + native_dir + '/' + api + '_absl_format_autogen.cpp', |
| frontend_params)) |
| renders.append( |
| FileRender( |
| 'dawn/native/api_StreamImpl.cpp', 'src/' + native_dir + |
| '/' + api + '_StreamImpl_autogen.cpp', frontend_params)) |
| renders.append( |
| FileRender('dawn/native/ObjectType.h', |
| 'src/' + native_dir + '/ObjectType_autogen.h', |
| frontend_params)) |
| renders.append( |
| FileRender('dawn/native/ObjectType.cpp', |
| 'src/' + native_dir + '/ObjectType_autogen.cpp', |
| frontend_params)) |
| |
| if 'wire' in targets: |
| params_dawn_wire = parse_json( |
| loaded_json, |
| enabled_tags=['compat', 'dawn', 'deprecated'], |
| disabled_tags=['native']) |
| additional_params = compute_wire_params(params_dawn_wire, |
| wire_json) |
| |
| wire_params = [ |
| RENDER_PARAMS_BASE, params_dawn_wire, { |
| 'as_wireType': lambda type : as_wireType(metadata, type), |
| 'as_annotated_wireType': \ |
| lambda arg: annotated(as_wireType(metadata, arg.type), arg), |
| 'is_wire_serializable': lambda type : is_wire_serializable(type), |
| }, additional_params |
| ] |
| renders.append( |
| FileRender('dawn/wire/ObjectType.h', |
| 'src/dawn/wire/ObjectType_autogen.h', wire_params)) |
| renders.append( |
| FileRender('dawn/wire/WireCmd.h', |
| 'src/dawn/wire/WireCmd_autogen.h', wire_params)) |
| renders.append( |
| FileRender('dawn/wire/WireCmd.cpp', |
| 'src/dawn/wire/WireCmd_autogen.cpp', wire_params)) |
| renders.append( |
| FileRender('dawn/wire/client/ApiObjects.h', |
| 'src/dawn/wire/client/ApiObjects_autogen.h', |
| wire_params)) |
| renders.append( |
| FileRender('dawn/wire/client/ApiProcs.cpp', |
| 'src/dawn/wire/client/ApiProcs_autogen.cpp', |
| wire_params)) |
| renders.append( |
| FileRender('dawn/wire/client/ClientBase.h', |
| 'src/dawn/wire/client/ClientBase_autogen.h', |
| wire_params)) |
| renders.append( |
| FileRender('dawn/wire/client/ClientHandlers.cpp', |
| 'src/dawn/wire/client/ClientHandlers_autogen.cpp', |
| wire_params)) |
| renders.append( |
| FileRender( |
| 'dawn/wire/client/ClientPrototypes.inc', |
| 'src/dawn/wire/client/ClientPrototypes_autogen.inc', |
| wire_params)) |
| renders.append( |
| FileRender('dawn/wire/server/ServerBase.h', |
| 'src/dawn/wire/server/ServerBase_autogen.h', |
| wire_params)) |
| renders.append( |
| FileRender('dawn/wire/server/ServerDoers.cpp', |
| 'src/dawn/wire/server/ServerDoers_autogen.cpp', |
| wire_params)) |
| renders.append( |
| FileRender('dawn/wire/server/ServerHandlers.cpp', |
| 'src/dawn/wire/server/ServerHandlers_autogen.cpp', |
| wire_params)) |
| renders.append( |
| FileRender( |
| 'dawn/wire/server/ServerPrototypes.inc', |
| 'src/dawn/wire/server/ServerPrototypes_autogen.inc', |
| wire_params)) |
| renders.append( |
| FileRender('dawn/wire/server/WGPUTraits.h', |
| 'src/dawn/wire/server/WGPUTraits_autogen.h', |
| wire_params)) |
| |
| |
| if 'dawn_lpmfuzz_proto' in targets: |
| params_dawn_wire = parse_json( |
| loaded_json, |
| enabled_tags=['compat', 'dawn', 'deprecated'], |
| disabled_tags=['native']) |
| api_and_wire_params = compute_wire_params(params_dawn_wire, |
| wire_json) |
| |
| fuzzer_params = compute_lpm_params(api_and_wire_params, lpm_json) |
| |
| lpm_params = [ |
| RENDER_PARAMS_BASE, params_dawn_wire, { |
| 'as_protobufTypeLPM': as_protobufTypeLPM, |
| 'as_protobufNameLPM': as_protobufNameLPM, |
| 'unreachable': unreachable_code |
| }, api_and_wire_params, fuzzer_params |
| ] |
| |
| renders.append( |
| FileRender('dawn/fuzzers/lpmfuzz/dawn_lpm.proto', |
| 'src/dawn/fuzzers/lpmfuzz/dawn_lpm_autogen.proto', |
| lpm_params)) |
| |
| renders.append( |
| FileRender( |
| 'dawn/fuzzers/lpmfuzz/dawn_object_types_lpm.proto', |
| 'src/dawn/fuzzers/lpmfuzz/dawn_object_types_lpm_autogen.proto', |
| lpm_params)) |
| |
| if 'dawn_lpmfuzz_cpp' in targets: |
| params_dawn_wire = parse_json( |
| loaded_json, |
| enabled_tags=['compat', 'dawn', 'deprecated'], |
| disabled_tags=['native']) |
| api_and_wire_params = compute_wire_params(params_dawn_wire, |
| wire_json) |
| |
| fuzzer_params = compute_lpm_params(api_and_wire_params, lpm_json) |
| |
| lpm_params = [ |
| RENDER_PARAMS_BASE, params_dawn_wire, { |
| 'as_protobufMemberName': as_protobufMemberNameLPM |
| }, api_and_wire_params, fuzzer_params |
| ] |
| |
| renders.append( |
| FileRender( |
| 'dawn/fuzzers/lpmfuzz/DawnLPMSerializer.cpp', |
| 'src/dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.cpp', |
| lpm_params)) |
| |
| renders.append( |
| FileRender( |
| 'dawn/fuzzers/lpmfuzz/DawnLPMSerializer.h', |
| 'src/dawn/fuzzers/lpmfuzz/DawnLPMSerializer_autogen.h', |
| lpm_params)) |
| |
| renders.append( |
| FileRender( |
| 'dawn/fuzzers/lpmfuzz/DawnLPMConstants.h', |
| 'src/dawn/fuzzers/lpmfuzz/DawnLPMConstants_autogen.h', |
| lpm_params)) |
| |
| if 'kotlin' in targets: |
| params_kotlin = compute_kotlin_params(loaded_json, kotlin_json) |
| kt_file_path = params_kotlin['kotlin_package'].replace('.', '/') |
| jni_name = params_kotlin['jni_name'] |
| by_category = params_kotlin['by_category'] |
| for structure in by_category['structure']: |
| renders.append( |
| FileRender('art/api_kotlin_structure.kt', |
| 'java/' + jni_name(structure) + '.kt', [ |
| RENDER_PARAMS_BASE, params_kotlin, { |
| 'structure': structure |
| } |
| ])) |
| for obj in by_category['object']: |
| renders.append( |
| FileRender( |
| 'art/api_kotlin_object.kt', |
| 'java/' + jni_name(obj) + '.kt', |
| [RENDER_PARAMS_BASE, params_kotlin, { |
| 'obj': obj |
| }])) |
| for function_pointer in by_category['function pointer']: |
| renders.append( |
| FileRender('art/api_kotlin_function_pointer.kt', |
| 'java/' + jni_name(function_pointer) + '.kt', [ |
| RENDER_PARAMS_BASE, params_kotlin, { |
| 'function_pointer': function_pointer |
| } |
| ])) |
| renders.append( |
| FileRender('art/api_kotlin_functions.kt', |
| 'java/' + kt_file_path + '/Functions.kt', |
| [RENDER_PARAMS_BASE, params_kotlin])) |
| renders.append( |
| FileRender('art/api_kotlin_async_helpers.kt', |
| 'java/' + kt_file_path + '/AsyncHelpers.kt', |
| [RENDER_PARAMS_BASE, params_kotlin])) |
| |
| for enum in (params_kotlin['by_category']['bitmask'] + |
| params_kotlin['by_category']['enum']): |
| renders.append( |
| FileRender( |
| 'art/api_kotlin_enum.kt', |
| 'java/' + jni_name(enum) + '.kt', |
| [RENDER_PARAMS_BASE, params_kotlin, { |
| 'enum': enum |
| }])) |
| |
| renders.append( |
| FileRender('art/api_kotlin_constants.kt', |
| 'java/' + kt_file_path + '/Constants.kt', |
| [RENDER_PARAMS_BASE, params_kotlin])) |
| |
| if "jni" in targets: |
| params_kotlin = compute_kotlin_params(loaded_json, kotlin_json) |
| renders.append( |
| FileRender('art/structures.h', 'cpp/structures.h', |
| [RENDER_PARAMS_BASE, params_kotlin])) |
| renders.append( |
| FileRender('art/structures.cpp', 'cpp/structures.cpp', |
| [RENDER_PARAMS_BASE, params_kotlin])) |
| renders.append( |
| FileRender('art/methods.cpp', 'cpp/methods.cpp', |
| [RENDER_PARAMS_BASE, params_kotlin])) |
| |
| return renders |
| |
| def get_dependencies(self, args): |
| deps = [os.path.abspath(args.dawn_json)] |
| if args.wire_json != None: |
| deps += [os.path.abspath(args.wire_json)] |
| if args.kotlin_json != None: |
| deps += [os.path.abspath(args.kotlin_json)] |
| if args.lpm_json != None: |
| deps += [os.path.abspath(args.lpm_json)] |
| return deps |
| |
| |
| if __name__ == '__main__': |
| sys.exit(run_generator(MultiGeneratorFromDawnJSON())) |