| OLD | NEW |
| (Empty) |
| 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 """Generates dart source files from a mojom.Module.""" | |
| 6 | |
| 7 import re | |
| 8 | |
| 9 import mojom.generate.generator as generator | |
| 10 import mojom.generate.module as mojom | |
| 11 import mojom.generate.pack as pack | |
| 12 from mojom.generate.template_expander import UseJinja | |
| 13 | |
| 14 GENERATOR_PREFIX = 'dart' | |
| 15 | |
| 16 _HEADER_SIZE = 8 | |
| 17 | |
| 18 _kind_to_dart_default_value = { | |
| 19 mojom.BOOL: "false", | |
| 20 mojom.INT8: "0", | |
| 21 mojom.UINT8: "0", | |
| 22 mojom.INT16: "0", | |
| 23 mojom.UINT16: "0", | |
| 24 mojom.INT32: "0", | |
| 25 mojom.UINT32: "0", | |
| 26 mojom.FLOAT: "0.0", | |
| 27 mojom.HANDLE: "null", | |
| 28 mojom.DCPIPE: "null", | |
| 29 mojom.DPPIPE: "null", | |
| 30 mojom.MSGPIPE: "null", | |
| 31 mojom.SHAREDBUFFER: "null", | |
| 32 mojom.NULLABLE_HANDLE: "null", | |
| 33 mojom.NULLABLE_DCPIPE: "null", | |
| 34 mojom.NULLABLE_DPPIPE: "null", | |
| 35 mojom.NULLABLE_MSGPIPE: "null", | |
| 36 mojom.NULLABLE_SHAREDBUFFER: "null", | |
| 37 mojom.INT64: "0", | |
| 38 mojom.UINT64: "0", | |
| 39 mojom.DOUBLE: "0.0", | |
| 40 mojom.STRING: "null", | |
| 41 mojom.NULLABLE_STRING: "null" | |
| 42 } | |
| 43 | |
| 44 _kind_to_dart_decl_type = { | |
| 45 mojom.BOOL: "bool", | |
| 46 mojom.INT8: "int", | |
| 47 mojom.UINT8: "int", | |
| 48 mojom.INT16: "int", | |
| 49 mojom.UINT16: "int", | |
| 50 mojom.INT32: "int", | |
| 51 mojom.UINT32: "int", | |
| 52 mojom.FLOAT: "double", | |
| 53 mojom.HANDLE: "core.MojoHandle", | |
| 54 mojom.DCPIPE: "core.MojoDataPipeConsumer", | |
| 55 mojom.DPPIPE: "core.MojoDataPipeProducer", | |
| 56 mojom.MSGPIPE: "core.MojoMessagePipeEndpoint", | |
| 57 mojom.SHAREDBUFFER: "core.MojoSharedBuffer", | |
| 58 mojom.NULLABLE_HANDLE: "core.MojoHandle", | |
| 59 mojom.NULLABLE_DCPIPE: "core.MojoDataPipeConsumer", | |
| 60 mojom.NULLABLE_DPPIPE: "core.MojoDataPipeProducer", | |
| 61 mojom.NULLABLE_MSGPIPE: "core.MojoMessagePipeEndpoint", | |
| 62 mojom.NULLABLE_SHAREDBUFFER: "core.MojoSharedBuffer", | |
| 63 mojom.INT64: "int", | |
| 64 mojom.UINT64: "int", | |
| 65 mojom.DOUBLE: "double", | |
| 66 mojom.STRING: "String", | |
| 67 mojom.NULLABLE_STRING: "String" | |
| 68 } | |
| 69 | |
| 70 _spec_to_decode_method = { | |
| 71 mojom.BOOL.spec: 'decodeBool', | |
| 72 mojom.DCPIPE.spec: 'decodeHandle', | |
| 73 mojom.DOUBLE.spec: 'decodeDouble', | |
| 74 mojom.DPPIPE.spec: 'decodeHandle', | |
| 75 mojom.FLOAT.spec: 'decodeFloat', | |
| 76 mojom.HANDLE.spec: 'decodeHandle', | |
| 77 mojom.INT16.spec: 'decodeInt16', | |
| 78 mojom.INT32.spec: 'decodeInt32', | |
| 79 mojom.INT64.spec: 'decodeInt64', | |
| 80 mojom.INT8.spec: 'decodeInt8', | |
| 81 mojom.MSGPIPE.spec: 'decodeMessagePipeHandle', | |
| 82 mojom.NULLABLE_DCPIPE.spec: 'decodeConsumerHandle', | |
| 83 mojom.NULLABLE_DPPIPE.spec: 'decodeProducerHandle', | |
| 84 mojom.NULLABLE_HANDLE.spec: 'decodeHandle', | |
| 85 mojom.NULLABLE_MSGPIPE.spec: 'decodeMessagePipeHandle', | |
| 86 mojom.NULLABLE_SHAREDBUFFER.spec: 'decodeSharedBufferHandle', | |
| 87 mojom.NULLABLE_STRING.spec: 'decodeString', | |
| 88 mojom.SHAREDBUFFER.spec: 'decodeSharedBufferHandle', | |
| 89 mojom.STRING.spec: 'decodeString', | |
| 90 mojom.UINT16.spec: 'decodeUint16', | |
| 91 mojom.UINT32.spec: 'decodeUint32', | |
| 92 mojom.UINT64.spec: 'decodeUint64', | |
| 93 mojom.UINT8.spec: 'decodeUint8', | |
| 94 } | |
| 95 | |
| 96 _spec_to_encode_method = { | |
| 97 mojom.BOOL.spec: 'encodeBool', | |
| 98 mojom.DCPIPE.spec: 'encodeHandle', | |
| 99 mojom.DOUBLE.spec: 'encodeDouble', | |
| 100 mojom.DPPIPE.spec: 'encodeHandle', | |
| 101 mojom.FLOAT.spec: 'encodeFloat', | |
| 102 mojom.HANDLE.spec: 'encodeHandle', | |
| 103 mojom.INT16.spec: 'encodeInt16', | |
| 104 mojom.INT32.spec: 'encodeInt32', | |
| 105 mojom.INT64.spec: 'encodeInt64', | |
| 106 mojom.INT8.spec: 'encodeInt8', | |
| 107 mojom.MSGPIPE.spec: 'encodeMessagePipeHandle', | |
| 108 mojom.NULLABLE_DCPIPE.spec: 'encodeConsumerHandle', | |
| 109 mojom.NULLABLE_DPPIPE.spec: 'encodeProducerHandle', | |
| 110 mojom.NULLABLE_HANDLE.spec: 'encodeHandle', | |
| 111 mojom.NULLABLE_MSGPIPE.spec: 'encodeMessagePipeHandle', | |
| 112 mojom.NULLABLE_SHAREDBUFFER.spec: 'encodeSharedBufferHandle', | |
| 113 mojom.NULLABLE_STRING.spec: 'encodeString', | |
| 114 mojom.SHAREDBUFFER.spec: 'encodeSharedBufferHandle', | |
| 115 mojom.STRING.spec: 'encodeString', | |
| 116 mojom.UINT16.spec: 'encodeUint16', | |
| 117 mojom.UINT32.spec: 'encodeUint32', | |
| 118 mojom.UINT64.spec: 'encodeUint64', | |
| 119 mojom.UINT8.spec: 'encodeUint8', | |
| 120 } | |
| 121 | |
| 122 def GetDartType(kind): | |
| 123 if kind.imported_from: | |
| 124 return kind.imported_from["unique_name"] + "." + kind.name | |
| 125 return kind.name | |
| 126 | |
| 127 def DartDefaultValue(field): | |
| 128 if field.default: | |
| 129 if mojom.IsStructKind(field.kind): | |
| 130 assert field.default == "default" | |
| 131 return "new %s()" % GetDartType(field.kind) | |
| 132 return ExpressionToText(field.default) | |
| 133 if field.kind in mojom.PRIMITIVES: | |
| 134 return _kind_to_dart_default_value[field.kind] | |
| 135 if mojom.IsStructKind(field.kind): | |
| 136 return "null" | |
| 137 if mojom.IsArrayKind(field.kind): | |
| 138 return "null" | |
| 139 if mojom.IsMapKind(field.kind): | |
| 140 return "null" | |
| 141 if mojom.IsInterfaceKind(field.kind) or \ | |
| 142 mojom.IsInterfaceRequestKind(field.kind): | |
| 143 return "null" | |
| 144 if mojom.IsEnumKind(field.kind): | |
| 145 return "0" | |
| 146 | |
| 147 def DartDeclType(kind): | |
| 148 if kind in mojom.PRIMITIVES: | |
| 149 return _kind_to_dart_decl_type[kind] | |
| 150 if mojom.IsStructKind(kind): | |
| 151 return GetDartType(kind) | |
| 152 if mojom.IsArrayKind(kind): | |
| 153 array_type = DartDeclType(kind.kind) | |
| 154 return "List<" + array_type + ">" | |
| 155 if mojom.IsMapKind(kind): | |
| 156 key_type = DartDeclType(kind.key_kind) | |
| 157 value_type = DartDeclType(kind.value_kind) | |
| 158 return "Map<"+ key_type + ", " + value_type + ">" | |
| 159 if mojom.IsInterfaceKind(kind) or \ | |
| 160 mojom.IsInterfaceRequestKind(kind): | |
| 161 return "Object" | |
| 162 if mojom.IsEnumKind(kind): | |
| 163 return "int" | |
| 164 | |
| 165 def NameToComponent(name): | |
| 166 # insert '_' between anything and a Title name (e.g, HTTPEntry2FooBar -> | |
| 167 # HTTP_Entry2_FooBar) | |
| 168 name = re.sub('([^_])([A-Z][^A-Z_]+)', r'\1_\2', name) | |
| 169 # insert '_' between non upper and start of upper blocks (e.g., | |
| 170 # HTTP_Entry2_FooBar -> HTTP_Entry2_Foo_Bar) | |
| 171 name = re.sub('([^A-Z_])([A-Z])', r'\1_\2', name) | |
| 172 return [x.lower() for x in name.split('_')] | |
| 173 | |
| 174 def UpperCamelCase(name): | |
| 175 return ''.join([x.capitalize() for x in NameToComponent(name)]) | |
| 176 | |
| 177 def CamelCase(name): | |
| 178 uccc = UpperCamelCase(name) | |
| 179 return uccc[0].lower() + uccc[1:] | |
| 180 | |
| 181 def ConstantStyle(name): | |
| 182 components = NameToComponent(name) | |
| 183 if components[0] == 'k' and len(components) > 1: | |
| 184 components = components[1:] | |
| 185 # variable cannot starts with a digit. | |
| 186 if components[0][0].isdigit(): | |
| 187 components[0] = '_' + components[0] | |
| 188 return '_'.join([x.upper() for x in components]) | |
| 189 | |
| 190 def GetNameForElement(element): | |
| 191 if (mojom.IsEnumKind(element) or mojom.IsInterfaceKind(element) or | |
| 192 mojom.IsStructKind(element)): | |
| 193 return UpperCamelCase(element.name) | |
| 194 if mojom.IsInterfaceRequestKind(element): | |
| 195 return GetNameForElement(element.kind) | |
| 196 if isinstance(element, (mojom.Method, | |
| 197 mojom.Parameter, | |
| 198 mojom.Field)): | |
| 199 return CamelCase(element.name) | |
| 200 if isinstance(element, mojom.EnumValue): | |
| 201 return (GetNameForElement(element.enum) + '.' + | |
| 202 ConstantStyle(element.name)) | |
| 203 if isinstance(element, (mojom.NamedValue, | |
| 204 mojom.Constant, | |
| 205 mojom.EnumField)): | |
| 206 return ConstantStyle(element.name) | |
| 207 raise Exception('Unexpected element: %s' % element) | |
| 208 | |
| 209 def GetInterfaceResponseName(method): | |
| 210 return UpperCamelCase(method.name + 'Response') | |
| 211 | |
| 212 def GetResponseStructFromMethod(method): | |
| 213 return generator.GetDataHeader( | |
| 214 False, generator.GetResponseStructFromMethod(method)) | |
| 215 | |
| 216 def GetStructFromMethod(method): | |
| 217 return generator.GetDataHeader( | |
| 218 False, generator.GetStructFromMethod(method)) | |
| 219 | |
| 220 def GetDartTrueFalse(value): | |
| 221 return 'true' if value else 'false' | |
| 222 | |
| 223 def GetArrayNullabilityFlags(kind): | |
| 224 """Returns nullability flags for an array type, see codec.dart. | |
| 225 | |
| 226 As we have dedicated decoding functions for arrays, we have to pass | |
| 227 nullability information about both the array itself, as well as the array | |
| 228 element type there. | |
| 229 """ | |
| 230 assert mojom.IsArrayKind(kind) | |
| 231 ARRAY_NULLABLE = 'bindings.kArrayNullable' | |
| 232 ELEMENT_NULLABLE = 'bindings.kElementNullable' | |
| 233 NOTHING_NULLABLE = 'bindings.kNothingNullable' | |
| 234 | |
| 235 flags_to_set = [] | |
| 236 if mojom.IsNullableKind(kind): | |
| 237 flags_to_set.append(ARRAY_NULLABLE) | |
| 238 if mojom.IsNullableKind(kind.kind): | |
| 239 flags_to_set.append(ELEMENT_NULLABLE) | |
| 240 | |
| 241 if not flags_to_set: | |
| 242 flags_to_set = [NOTHING_NULLABLE] | |
| 243 return ' | '.join(flags_to_set) | |
| 244 | |
| 245 def AppendDecodeParams(initial_params, kind, bit): | |
| 246 """ Appends standard parameters for decode calls. """ | |
| 247 params = list(initial_params) | |
| 248 if (kind == mojom.BOOL): | |
| 249 params.append(str(bit)) | |
| 250 if mojom.IsReferenceKind(kind): | |
| 251 if mojom.IsArrayKind(kind): | |
| 252 params.append(GetArrayNullabilityFlags(kind)) | |
| 253 else: | |
| 254 params.append(GetDartTrueFalse(mojom.IsNullableKind(kind))) | |
| 255 if mojom.IsInterfaceKind(kind): | |
| 256 params.append('%sClient.newFromEndpoint' % GetDartType(kind)) | |
| 257 if mojom.IsArrayKind(kind) and mojom.IsInterfaceKind(kind.kind): | |
| 258 params.append('%sClient.newFromEndpoint' % GetDartType(kind.kind)) | |
| 259 if mojom.IsInterfaceRequestKind(kind): | |
| 260 params.append('%sInterface.newFromEndpoint' % GetDartType(kind.kind)) | |
| 261 if mojom.IsArrayKind(kind) and mojom.IsInterfaceRequestKind(kind.kind): | |
| 262 params.append('%sInterface.newFromEndpoint' % GetDartType(kind.kind.kind)) | |
| 263 if mojom.IsArrayKind(kind): | |
| 264 params.append(GetArrayExpectedLength(kind)) | |
| 265 return params | |
| 266 | |
| 267 def AppendEncodeParams(initial_params, kind, bit): | |
| 268 """ Appends standard parameters shared between encode and decode calls. """ | |
| 269 params = list(initial_params) | |
| 270 if (kind == mojom.BOOL): | |
| 271 params.append(str(bit)) | |
| 272 if mojom.IsReferenceKind(kind): | |
| 273 if mojom.IsArrayKind(kind): | |
| 274 params.append(GetArrayNullabilityFlags(kind)) | |
| 275 else: | |
| 276 params.append(GetDartTrueFalse(mojom.IsNullableKind(kind))) | |
| 277 if mojom.IsArrayKind(kind): | |
| 278 params.append(GetArrayExpectedLength(kind)) | |
| 279 return params | |
| 280 | |
| 281 def DecodeMethod(kind, offset, bit): | |
| 282 def _DecodeMethodName(kind): | |
| 283 if mojom.IsArrayKind(kind): | |
| 284 return _DecodeMethodName(kind.kind) + 'Array' | |
| 285 if mojom.IsEnumKind(kind): | |
| 286 return _DecodeMethodName(mojom.INT32) | |
| 287 if mojom.IsInterfaceRequestKind(kind): | |
| 288 return 'decodeInterfaceRequest' | |
| 289 if mojom.IsInterfaceKind(kind): | |
| 290 return 'decodeServiceInterface' | |
| 291 return _spec_to_decode_method[kind.spec] | |
| 292 methodName = _DecodeMethodName(kind) | |
| 293 params = AppendDecodeParams([ str(offset) ], kind, bit) | |
| 294 return '%s(%s)' % (methodName, ', '.join(params)) | |
| 295 | |
| 296 def EncodeMethod(kind, variable, offset, bit): | |
| 297 def _EncodeMethodName(kind): | |
| 298 if mojom.IsStructKind(kind): | |
| 299 return 'encodeStruct' | |
| 300 if mojom.IsArrayKind(kind): | |
| 301 return _EncodeMethodName(kind.kind) + 'Array' | |
| 302 if mojom.IsEnumKind(kind): | |
| 303 return _EncodeMethodName(mojom.INT32) | |
| 304 if mojom.IsInterfaceRequestKind(kind): | |
| 305 return 'encodeInterfaceRequest' | |
| 306 if mojom.IsInterfaceKind(kind): | |
| 307 return 'encodeInterface' | |
| 308 return _spec_to_encode_method[kind.spec] | |
| 309 methodName = _EncodeMethodName(kind) | |
| 310 params = AppendEncodeParams([ variable, str(offset) ], kind, bit) | |
| 311 return '%s(%s)' % (methodName, ', '.join(params)) | |
| 312 | |
| 313 def TranslateConstants(token): | |
| 314 if isinstance(token, (mojom.EnumValue, mojom.NamedValue)): | |
| 315 # Both variable and enum constants are constructed like: | |
| 316 # NamespaceUid.Struct.Enum_CONSTANT_NAME | |
| 317 name = "" | |
| 318 if token.imported_from: | |
| 319 name = token.imported_from["unique_name"] + "." | |
| 320 if token.parent_kind: | |
| 321 name = name + token.parent_kind.name + "." | |
| 322 if isinstance(token, mojom.EnumValue): | |
| 323 name = name + token.enum.name + "_" | |
| 324 return name + token.name | |
| 325 | |
| 326 if isinstance(token, mojom.BuiltinValue): | |
| 327 if token.value == "double.INFINITY" or token.value == "float.INFINITY": | |
| 328 return "double.INFINITY"; | |
| 329 if token.value == "double.NEGATIVE_INFINITY" or \ | |
| 330 token.value == "float.NEGATIVE_INFINITY": | |
| 331 return "double.NEGATIVE_INFINITY"; | |
| 332 if token.value == "double.NAN" or token.value == "float.NAN": | |
| 333 return "double.NAN"; | |
| 334 | |
| 335 # Strip leading '+'. | |
| 336 if token[0] == '+': | |
| 337 token = token[1:] | |
| 338 | |
| 339 return token | |
| 340 | |
| 341 def ExpressionToText(value): | |
| 342 return TranslateConstants(value) | |
| 343 | |
| 344 def GetArrayKind(kind, size = None): | |
| 345 if size is None: | |
| 346 return mojom.Array(kind) | |
| 347 else: | |
| 348 array = mojom.Array(kind, 0) | |
| 349 array.dart_map_size = size | |
| 350 return array | |
| 351 | |
| 352 def GetArrayExpectedLength(kind): | |
| 353 if mojom.IsArrayKind(kind) and kind.length is not None: | |
| 354 return getattr(kind, 'dart_map_size', str(kind.length)) | |
| 355 else: | |
| 356 return 'bindings.kUnspecifiedArrayLength' | |
| 357 | |
| 358 def IsPointerArrayKind(kind): | |
| 359 if not mojom.IsArrayKind(kind): | |
| 360 return False | |
| 361 sub_kind = kind.kind | |
| 362 return mojom.IsObjectKind(sub_kind) | |
| 363 | |
| 364 class Generator(generator.Generator): | |
| 365 | |
| 366 dart_filters = { | |
| 367 'array_expected_length': GetArrayExpectedLength, | |
| 368 'array': GetArrayKind, | |
| 369 'decode_method': DecodeMethod, | |
| 370 'default_value': DartDefaultValue, | |
| 371 'encode_method': EncodeMethod, | |
| 372 'expression_to_text': ExpressionToText, | |
| 373 'is_handle': mojom.IsNonInterfaceHandleKind, | |
| 374 'is_map_kind': mojom.IsMapKind, | |
| 375 'is_nullable_kind': mojom.IsNullableKind, | |
| 376 'is_pointer_array_kind': IsPointerArrayKind, | |
| 377 'is_struct_kind': mojom.IsStructKind, | |
| 378 'dart_true_false': GetDartTrueFalse, | |
| 379 'dart_type': DartDeclType, | |
| 380 'name': GetNameForElement, | |
| 381 'interface_response_name': GetInterfaceResponseName, | |
| 382 'response_struct_from_method': GetResponseStructFromMethod, | |
| 383 'struct_from_method': GetStructFromMethod, | |
| 384 'upper_camel_case': UpperCamelCase, | |
| 385 'struct_size': lambda ps: ps.GetTotalSize() + _HEADER_SIZE, | |
| 386 } | |
| 387 | |
| 388 def GetParameters(self): | |
| 389 return { | |
| 390 "namespace": self.module.namespace, | |
| 391 "imports": self.GetImports(), | |
| 392 "kinds": self.module.kinds, | |
| 393 "enums": self.module.enums, | |
| 394 "module": self.module, | |
| 395 "structs": self.GetStructs() + self.GetStructsFromMethods(), | |
| 396 "interfaces": self.module.interfaces, | |
| 397 "imported_interfaces": self.GetImportedInterfaces(), | |
| 398 "imported_from": self.ImportedFrom(), | |
| 399 } | |
| 400 | |
| 401 @UseJinja("dart_templates/module.lib.tmpl", filters=dart_filters) | |
| 402 def GenerateLibModule(self): | |
| 403 return self.GetParameters() | |
| 404 | |
| 405 def GenerateFiles(self, args): | |
| 406 self.Write(self.GenerateLibModule(), | |
| 407 self.MatchMojomFilePath("%s.dart" % self.module.name)) | |
| 408 | |
| 409 def GetImports(self): | |
| 410 used_names = set() | |
| 411 for each_import in self.module.imports: | |
| 412 simple_name = each_import["module_name"].split(".")[0] | |
| 413 | |
| 414 # Since each import is assigned a library in Dart, they need to have | |
| 415 # unique names. | |
| 416 unique_name = simple_name | |
| 417 counter = 0 | |
| 418 while unique_name in used_names: | |
| 419 counter += 1 | |
| 420 unique_name = simple_name + str(counter) | |
| 421 | |
| 422 used_names.add(unique_name) | |
| 423 each_import["unique_name"] = unique_name | |
| 424 counter += 1 | |
| 425 return self.module.imports | |
| 426 | |
| 427 def GetImportedInterfaces(self): | |
| 428 interface_to_import = {} | |
| 429 for each_import in self.module.imports: | |
| 430 for each_interface in each_import["module"].interfaces: | |
| 431 name = each_interface.name | |
| 432 interface_to_import[name] = each_import["unique_name"] + "." + name | |
| 433 return interface_to_import | |
| 434 | |
| 435 def ImportedFrom(self): | |
| 436 interface_to_import = {} | |
| 437 for each_import in self.module.imports: | |
| 438 for each_interface in each_import["module"].interfaces: | |
| 439 name = each_interface.name | |
| 440 interface_to_import[name] = each_import["unique_name"] + "." | |
| 441 return interface_to_import | |
| OLD | NEW |