1# Copyright 2014 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5"""Translates parse tree to Mojom IR.""" 6 7import re 8 9from . import ast 10 11 12def _DuplicateName(values): 13 """Returns the 'name' of the first entry in |values| whose 'name' has already 14 been encountered. If there are no duplicates, returns None.""" 15 names = set() 16 for value in values: 17 if value['name'] in names: 18 return value['name'] 19 names.add(value['name']) 20 return None 21 22def _MapTreeForType(func, tree, type_to_map, scope): 23 assert isinstance(type_to_map, type) 24 if not tree: 25 return [] 26 result = [func(subtree) 27 for subtree in tree if isinstance(subtree, type_to_map)] 28 duplicate_name = _DuplicateName(result) 29 if duplicate_name: 30 raise Exception('Names in mojom must be unique within a scope. The name ' 31 '"%s" is used more than once within the scope "%s".' % 32 (duplicate_name, scope)) 33 return result 34 35def _MapKind(kind): 36 map_to_kind = {'bool': 'b', 37 'int8': 'i8', 38 'int16': 'i16', 39 'int32': 'i32', 40 'int64': 'i64', 41 'uint8': 'u8', 42 'uint16': 'u16', 43 'uint32': 'u32', 44 'uint64': 'u64', 45 'float': 'f', 46 'double': 'd', 47 'string': 's', 48 'handle': 'h', 49 'handle<data_pipe_consumer>': 'h:d:c', 50 'handle<data_pipe_producer>': 'h:d:p', 51 'handle<message_pipe>': 'h:m', 52 'handle<shared_buffer>': 'h:s'} 53 if kind.endswith('?'): 54 base_kind = _MapKind(kind[0:-1]) 55 # NOTE: This doesn't rule out enum types. Those will be detected later, when 56 # cross-reference is established. 57 reference_kinds = ('m', 's', 'h', 'a', 'r', 'x', 'asso') 58 if re.split('[^a-z]', base_kind, 1)[0] not in reference_kinds: 59 raise Exception( 60 'A type (spec "%s") cannot be made nullable' % base_kind) 61 return '?' + base_kind 62 if kind.endswith('}'): 63 lbracket = kind.rfind('{') 64 value = kind[0:lbracket] 65 return 'm[' + _MapKind(kind[lbracket+1:-1]) + '][' + _MapKind(value) + ']' 66 if kind.endswith(']'): 67 lbracket = kind.rfind('[') 68 typename = kind[0:lbracket] 69 return 'a' + kind[lbracket+1:-1] + ':' + _MapKind(typename) 70 if kind.endswith('&'): 71 return 'r:' + _MapKind(kind[0:-1]) 72 if kind.startswith('asso<'): 73 assert kind.endswith('>') 74 return 'asso:' + _MapKind(kind[5:-1]) 75 if kind in map_to_kind: 76 return map_to_kind[kind] 77 return 'x:' + kind 78 79def _AddOptional(dictionary, key, value): 80 if value is not None: 81 dictionary[key] = value; 82 83def _AttributeListToDict(attribute_list): 84 if attribute_list is None: 85 return None 86 assert isinstance(attribute_list, ast.AttributeList) 87 # TODO(vtl): Check for duplicate keys here. 88 return dict([(attribute.key, attribute.value) 89 for attribute in attribute_list]) 90 91def _EnumToDict(enum): 92 def EnumValueToDict(enum_value): 93 assert isinstance(enum_value, ast.EnumValue) 94 data = {'name': enum_value.name} 95 _AddOptional(data, 'value', enum_value.value) 96 _AddOptional(data, 'attributes', 97 _AttributeListToDict(enum_value.attribute_list)) 98 return data 99 100 assert isinstance(enum, ast.Enum) 101 data = {'name': enum.name, 102 'native_only': enum.enum_value_list is None } 103 if not data['native_only']: 104 data.update({'fields': map(EnumValueToDict, enum.enum_value_list)}) 105 _AddOptional(data, 'attributes', _AttributeListToDict(enum.attribute_list)) 106 return data 107 108def _ConstToDict(const): 109 assert isinstance(const, ast.Const) 110 return {'name': const.name, 111 'kind': _MapKind(const.typename), 112 'value': const.value} 113 114 115class _MojomBuilder(object): 116 def __init__(self): 117 self.mojom = {} 118 119 def Build(self, tree, name): 120 def StructToDict(struct): 121 def StructFieldToDict(struct_field): 122 assert isinstance(struct_field, ast.StructField) 123 data = {'name': struct_field.name, 124 'kind': _MapKind(struct_field.typename)} 125 _AddOptional(data, 'ordinal', 126 struct_field.ordinal.value 127 if struct_field.ordinal else None) 128 _AddOptional(data, 'default', struct_field.default_value) 129 _AddOptional(data, 'attributes', 130 _AttributeListToDict(struct_field.attribute_list)) 131 return data 132 133 assert isinstance(struct, ast.Struct) 134 data = {'name': struct.name, 135 'native_only': struct.body is None} 136 if not data['native_only']: 137 data.update({ 138 'fields': _MapTreeForType(StructFieldToDict, struct.body, 139 ast.StructField, struct.name), 140 'enums': _MapTreeForType(_EnumToDict, struct.body, ast.Enum, 141 struct.name), 142 'constants': _MapTreeForType(_ConstToDict, struct.body, 143 ast.Const, struct.name)}) 144 _AddOptional(data, 'attributes', 145 _AttributeListToDict(struct.attribute_list)) 146 return data 147 148 def UnionToDict(union): 149 def UnionFieldToDict(union_field): 150 assert isinstance(union_field, ast.UnionField) 151 data = {'name': union_field.name, 152 'kind': _MapKind(union_field.typename)} 153 _AddOptional(data, 'ordinal', 154 union_field.ordinal.value 155 if union_field.ordinal else None) 156 _AddOptional(data, 'attributes', 157 _AttributeListToDict(union_field.attribute_list)) 158 return data 159 160 assert isinstance(union, ast.Union) 161 data = {'name': union.name, 162 'fields': _MapTreeForType(UnionFieldToDict, union.body, 163 ast.UnionField, union.name)} 164 _AddOptional(data, 'attributes', 165 _AttributeListToDict(union.attribute_list)) 166 return data 167 168 def InterfaceToDict(interface): 169 def MethodToDict(method): 170 def ParameterToDict(param): 171 assert isinstance(param, ast.Parameter) 172 data = {'name': param.name, 173 'kind': _MapKind(param.typename)} 174 _AddOptional(data, 'ordinal', 175 param.ordinal.value if param.ordinal else None) 176 _AddOptional(data, 'attributes', 177 _AttributeListToDict(param.attribute_list)) 178 return data 179 180 assert isinstance(method, ast.Method) 181 data = {'name': method.name, 182 'parameters': map(ParameterToDict, method.parameter_list)} 183 if method.response_parameter_list is not None: 184 data['response_parameters'] = map(ParameterToDict, 185 method.response_parameter_list) 186 _AddOptional(data, 'ordinal', 187 method.ordinal.value if method.ordinal else None) 188 _AddOptional(data, 'attributes', 189 _AttributeListToDict(method.attribute_list)) 190 return data 191 192 assert isinstance(interface, ast.Interface) 193 data = {'name': interface.name, 194 'methods': _MapTreeForType(MethodToDict, interface.body, 195 ast.Method, interface.name), 196 'enums': _MapTreeForType(_EnumToDict, interface.body, ast.Enum, 197 interface.name), 198 'constants': _MapTreeForType(_ConstToDict, interface.body, 199 ast.Const, interface.name)} 200 _AddOptional(data, 'attributes', 201 _AttributeListToDict(interface.attribute_list)) 202 return data 203 204 assert isinstance(tree, ast.Mojom) 205 self.mojom['name'] = name 206 self.mojom['namespace'] = tree.module.name[1] if tree.module else '' 207 self.mojom['imports'] = \ 208 [{'filename': imp.import_filename} for imp in tree.import_list] 209 self.mojom['structs'] = \ 210 _MapTreeForType(StructToDict, tree.definition_list, ast.Struct, name) 211 self.mojom['unions'] = \ 212 _MapTreeForType(UnionToDict, tree.definition_list, ast.Union, name) 213 self.mojom['interfaces'] = \ 214 _MapTreeForType(InterfaceToDict, tree.definition_list, ast.Interface, 215 name) 216 self.mojom['enums'] = \ 217 _MapTreeForType(_EnumToDict, tree.definition_list, ast.Enum, name) 218 self.mojom['constants'] = \ 219 _MapTreeForType(_ConstToDict, tree.definition_list, ast.Const, name) 220 _AddOptional(self.mojom, 'attributes', 221 _AttributeListToDict(tree.module.attribute_list) 222 if tree.module else None) 223 return self.mojom 224 225 226def Translate(tree, name): 227 """Translate AST to Mojom IR. 228 229 Args: 230 tree: The AST as a mojom.parse.ast.Mojom object. 231 name: The filename as a str. 232 233 Returns: 234 The Mojom IR as a dict. 235 """ 236 return _MojomBuilder().Build(tree, name) 237