1# Copyright 2013 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 C++ source files from a mojom.Module.""" 6 7import mojom.generate.generator as generator 8import mojom.generate.module as mojom 9import mojom.generate.pack as pack 10from mojom.generate.template_expander import UseJinja 11 12 13_kind_to_cpp_type = { 14 mojom.BOOL: "bool", 15 mojom.INT8: "int8_t", 16 mojom.UINT8: "uint8_t", 17 mojom.INT16: "int16_t", 18 mojom.UINT16: "uint16_t", 19 mojom.INT32: "int32_t", 20 mojom.UINT32: "uint32_t", 21 mojom.FLOAT: "float", 22 mojom.INT64: "int64_t", 23 mojom.UINT64: "uint64_t", 24 mojom.DOUBLE: "double", 25} 26 27_kind_to_cpp_literal_suffix = { 28 mojom.UINT8: "U", 29 mojom.UINT16: "U", 30 mojom.UINT32: "U", 31 mojom.FLOAT: "f", 32 mojom.UINT64: "ULL", 33} 34 35# TODO(rockot): Get rid of these globals. This requires some refactoring of the 36# generator library code so that filters can use the generator as context. 37_current_typemap = {} 38_for_blink = False 39_use_new_wrapper_types = False 40# TODO(rockot, yzshen): The variant handling is kind of a hack currently. Make 41# it right. 42_variant = None 43 44 45class _NameFormatter(object): 46 """A formatter for the names of kinds or values.""" 47 48 def __init__(self, token, variant): 49 self._token = token 50 self._variant = variant 51 52 def Format(self, separator, prefixed=False, internal=False, 53 include_variant=False, add_same_module_namespaces=False): 54 parts = [] 55 if self._ShouldIncludeNamespace(add_same_module_namespaces): 56 if prefixed: 57 parts.append("") 58 parts.extend(self._GetNamespace()) 59 if include_variant and self._variant: 60 parts.append(self._variant) 61 parts.extend(self._GetName(internal)) 62 return separator.join(parts) 63 64 def FormatForCpp(self, add_same_module_namespaces=False, internal=False): 65 return self.Format( 66 "::", prefixed=True, 67 add_same_module_namespaces=add_same_module_namespaces, 68 internal=internal, include_variant=True) 69 70 def FormatForMojom(self): 71 return self.Format(".", add_same_module_namespaces=True) 72 73 def _MapKindName(self, token, internal): 74 if not internal: 75 return token.name 76 if (mojom.IsStructKind(token) or mojom.IsUnionKind(token) or 77 mojom.IsInterfaceKind(token) or mojom.IsEnumKind(token)): 78 return token.name + "_Data" 79 return token.name 80 81 def _GetName(self, internal): 82 name = [] 83 if internal: 84 name.append("internal") 85 if self._token.parent_kind: 86 name.append(self._MapKindName(self._token.parent_kind, internal)) 87 # Both variable and enum constants are constructed like: 88 # Namespace::Struct::CONSTANT_NAME 89 # For enums, CONSTANT_NAME is EnumName::ENUM_VALUE. 90 if isinstance(self._token, mojom.EnumValue): 91 name.extend([self._token.enum.name, self._token.name]) 92 else: 93 name.append(self._MapKindName(self._token, internal)) 94 return name 95 96 def _ShouldIncludeNamespace(self, add_same_module_namespaces): 97 return add_same_module_namespaces or self._token.imported_from 98 99 def _GetNamespace(self): 100 if self._token.imported_from: 101 return NamespaceToArray(self._token.imported_from["namespace"]) 102 elif hasattr(self._token, "module"): 103 return NamespaceToArray(self._token.module.namespace) 104 return [] 105 106 107def ConstantValue(constant): 108 return ExpressionToText(constant.value, kind=constant.kind) 109 110# TODO(yzshen): Revisit the default value feature. It was designed prior to 111# custom type mapping. 112def DefaultValue(field): 113 if field.default: 114 if mojom.IsStructKind(field.kind): 115 assert field.default == "default" 116 if not IsTypemappedKind(field.kind): 117 return "%s::New()" % GetNameForKind(field.kind) 118 return ExpressionToText(field.default, kind=field.kind) 119 if not _use_new_wrapper_types: 120 if mojom.IsArrayKind(field.kind) or mojom.IsMapKind(field.kind): 121 return "nullptr"; 122 if mojom.IsStringKind(field.kind): 123 return "" if _for_blink else "nullptr" 124 return "" 125 126def NamespaceToArray(namespace): 127 return namespace.split(".") if namespace else [] 128 129def GetNameForKind(kind, internal=False): 130 return _NameFormatter(kind, _variant).FormatForCpp(internal=internal) 131 132def GetQualifiedNameForKind(kind, internal=False): 133 return _NameFormatter(kind, _variant).FormatForCpp( 134 internal=internal, add_same_module_namespaces=True) 135 136def GetFullMojomNameForKind(kind): 137 return _NameFormatter(kind, _variant).FormatForMojom() 138 139def IsTypemappedKind(kind): 140 return hasattr(kind, "name") and \ 141 GetFullMojomNameForKind(kind) in _current_typemap 142 143def IsNativeOnlyKind(kind): 144 return (mojom.IsStructKind(kind) or mojom.IsEnumKind(kind)) and \ 145 kind.native_only 146 147def GetNativeTypeName(typemapped_kind): 148 return _current_typemap[GetFullMojomNameForKind(typemapped_kind)]["typename"] 149 150def GetCppPodType(kind): 151 if mojom.IsStringKind(kind): 152 return "char*" 153 return _kind_to_cpp_type[kind] 154 155def GetCppWrapperType(kind): 156 if IsTypemappedKind(kind): 157 return GetNativeTypeName(kind) 158 if mojom.IsEnumKind(kind): 159 return GetNameForKind(kind) 160 if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): 161 return "%sPtr" % GetNameForKind(kind) 162 if mojom.IsArrayKind(kind): 163 pattern = None 164 if _use_new_wrapper_types: 165 if mojom.IsNullableKind(kind): 166 pattern = ("WTF::Optional<WTF::Vector<%s>>" if _for_blink else 167 "base::Optional<std::vector<%s>>") 168 else: 169 pattern = "WTF::Vector<%s>" if _for_blink else "std::vector<%s>" 170 else: 171 pattern = "mojo::WTFArray<%s>" if _for_blink else "mojo::Array<%s>" 172 return pattern % GetCppWrapperType(kind.kind) 173 if mojom.IsMapKind(kind): 174 pattern = None 175 if _use_new_wrapper_types: 176 if mojom.IsNullableKind(kind): 177 pattern = ("WTF::Optional<WTF::HashMap<%s, %s>>" if _for_blink else 178 "base::Optional<std::unordered_map<%s, %s>>") 179 else: 180 pattern = ("WTF::HashMap<%s, %s>" if _for_blink else 181 "std::unordered_map<%s, %s>") 182 else: 183 pattern = "mojo::WTFMap<%s, %s>" if _for_blink else "mojo::Map<%s, %s>" 184 return pattern % (GetCppWrapperType(kind.key_kind), 185 GetCppWrapperType(kind.value_kind)) 186 if mojom.IsInterfaceKind(kind): 187 return "%sPtr" % GetNameForKind(kind) 188 if mojom.IsInterfaceRequestKind(kind): 189 return "%sRequest" % GetNameForKind(kind.kind) 190 if mojom.IsAssociatedInterfaceKind(kind): 191 return "%sAssociatedPtrInfo" % GetNameForKind(kind.kind) 192 if mojom.IsAssociatedInterfaceRequestKind(kind): 193 return "%sAssociatedRequest" % GetNameForKind(kind.kind) 194 if mojom.IsStringKind(kind): 195 if _for_blink: 196 return "WTF::String" 197 if not _use_new_wrapper_types: 198 return "mojo::String" 199 return ("base::Optional<std::string>" if mojom.IsNullableKind(kind) else 200 "std::string") 201 if mojom.IsGenericHandleKind(kind): 202 return "mojo::ScopedHandle" 203 if mojom.IsDataPipeConsumerKind(kind): 204 return "mojo::ScopedDataPipeConsumerHandle" 205 if mojom.IsDataPipeProducerKind(kind): 206 return "mojo::ScopedDataPipeProducerHandle" 207 if mojom.IsMessagePipeKind(kind): 208 return "mojo::ScopedMessagePipeHandle" 209 if mojom.IsSharedBufferKind(kind): 210 return "mojo::ScopedSharedBufferHandle" 211 if not kind in _kind_to_cpp_type: 212 raise Exception("Unrecognized kind %s" % kind.spec) 213 return _kind_to_cpp_type[kind] 214 215def IsMoveOnlyKind(kind): 216 if IsTypemappedKind(kind): 217 if mojom.IsEnumKind(kind): 218 return False 219 return _current_typemap[GetFullMojomNameForKind(kind)]["move_only"] 220 if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): 221 return True 222 if mojom.IsArrayKind(kind): 223 return IsMoveOnlyKind(kind.kind) if _use_new_wrapper_types else True 224 if mojom.IsMapKind(kind): 225 return IsMoveOnlyKind(kind.value_kind) if _use_new_wrapper_types else True 226 if mojom.IsAnyHandleOrInterfaceKind(kind): 227 return True 228 return False 229 230def ShouldPassParamByValue(kind): 231 return (not mojom.IsReferenceKind(kind)) or IsMoveOnlyKind(kind) 232 233def GetCppWrapperParamType(kind): 234 cpp_wrapper_type = GetCppWrapperType(kind) 235 return (cpp_wrapper_type if ShouldPassParamByValue(kind) 236 else "const %s&" % cpp_wrapper_type) 237 238def GetCppFieldType(kind): 239 if mojom.IsStructKind(kind): 240 return ("mojo::internal::Pointer<%s>" % 241 GetNameForKind(kind, internal=True)) 242 if mojom.IsUnionKind(kind): 243 return "%s" % GetNameForKind(kind, internal=True) 244 if mojom.IsArrayKind(kind): 245 return ("mojo::internal::Pointer<mojo::internal::Array_Data<%s>>" % 246 GetCppFieldType(kind.kind)) 247 if mojom.IsMapKind(kind): 248 return ("mojo::internal::Pointer<mojo::internal::Map_Data<%s, %s>>" % 249 (GetCppFieldType(kind.key_kind), GetCppFieldType(kind.value_kind))) 250 if mojom.IsInterfaceKind(kind): 251 return "mojo::internal::Interface_Data" 252 if mojom.IsInterfaceRequestKind(kind): 253 return "mojo::internal::Handle_Data" 254 if mojom.IsAssociatedInterfaceKind(kind): 255 return "mojo::internal::AssociatedInterface_Data" 256 if mojom.IsAssociatedInterfaceRequestKind(kind): 257 return "mojo::internal::AssociatedInterfaceRequest_Data" 258 if mojom.IsEnumKind(kind): 259 return "int32_t" 260 if mojom.IsStringKind(kind): 261 return "mojo::internal::Pointer<mojo::internal::String_Data>" 262 if mojom.IsAnyHandleKind(kind): 263 return "mojo::internal::Handle_Data" 264 return _kind_to_cpp_type[kind] 265 266def GetCppUnionFieldType(kind): 267 if mojom.IsUnionKind(kind): 268 return ("mojo::internal::Pointer<%s>" % GetNameForKind(kind, internal=True)) 269 return GetCppFieldType(kind) 270 271def GetUnionGetterReturnType(kind): 272 if mojom.IsReferenceKind(kind): 273 return "%s&" % GetCppWrapperType(kind) 274 return GetCppWrapperType(kind) 275 276def GetUnmappedTypeForSerializer(kind): 277 if mojom.IsEnumKind(kind): 278 return GetQualifiedNameForKind(kind) 279 if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind): 280 return "%sPtr" % GetQualifiedNameForKind(kind) 281 if mojom.IsArrayKind(kind): 282 return "mojo::Array<%s>" % GetUnmappedTypeForSerializer(kind.kind) 283 if mojom.IsMapKind(kind): 284 return "mojo::Map<%s, %s>" % ( 285 GetUnmappedTypeForSerializer(kind.key_kind), 286 GetUnmappedTypeForSerializer(kind.value_kind)) 287 if mojom.IsInterfaceKind(kind): 288 return "%sPtr" % GetQualifiedNameForKind(kind) 289 if mojom.IsInterfaceRequestKind(kind): 290 return "%sRequest" % GetQualifiedNameForKind(kind.kind) 291 if mojom.IsAssociatedInterfaceKind(kind): 292 return "%sAssociatedPtrInfo" % GetQualifiedNameForKind(kind.kind) 293 if mojom.IsAssociatedInterfaceRequestKind(kind): 294 return "%sAssociatedRequest" % GetQualifiedNameForKind(kind.kind) 295 if mojom.IsStringKind(kind): 296 return "mojo::String" 297 if mojom.IsGenericHandleKind(kind): 298 return "mojo::ScopedHandle" 299 if mojom.IsDataPipeConsumerKind(kind): 300 return "mojo::ScopedDataPipeConsumerHandle" 301 if mojom.IsDataPipeProducerKind(kind): 302 return "mojo::ScopedDataPipeProducerHandle" 303 if mojom.IsMessagePipeKind(kind): 304 return "mojo::ScopedMessagePipeHandle" 305 if mojom.IsSharedBufferKind(kind): 306 return "mojo::ScopedSharedBufferHandle" 307 return _kind_to_cpp_type[kind] 308 309def TranslateConstants(token, kind): 310 if isinstance(token, mojom.NamedValue): 311 return _NameFormatter(token, _variant).FormatForCpp() 312 313 if isinstance(token, mojom.BuiltinValue): 314 if token.value == "double.INFINITY" or token.value == "float.INFINITY": 315 return "INFINITY"; 316 if token.value == "double.NEGATIVE_INFINITY" or \ 317 token.value == "float.NEGATIVE_INFINITY": 318 return "-INFINITY"; 319 if token.value == "double.NAN" or token.value == "float.NAN": 320 return "NAN"; 321 322 if (kind is not None and mojom.IsFloatKind(kind)): 323 return token if token.isdigit() else token + "f"; 324 325 # Per C++11, 2.14.2, the type of an integer literal is the first of the 326 # corresponding list in Table 6 in which its value can be represented. In this 327 # case, the list for decimal constants with no suffix is: 328 # int, long int, long long int 329 # The standard considers a program ill-formed if it contains an integer 330 # literal that cannot be represented by any of the allowed types. 331 # 332 # As it turns out, MSVC doesn't bother trying to fall back to long long int, 333 # so the integral constant -2147483648 causes it grief: it decides to 334 # represent 2147483648 as an unsigned integer, and then warns that the unary 335 # minus operator doesn't make sense on unsigned types. Doh! 336 if kind == mojom.INT32 and token == "-2147483648": 337 return "(-%d - 1) /* %s */" % ( 338 2**31 - 1, "Workaround for MSVC bug; see https://crbug.com/445618") 339 340 return "%s%s" % (token, _kind_to_cpp_literal_suffix.get(kind, "")) 341 342def ExpressionToText(value, kind=None): 343 return TranslateConstants(value, kind) 344 345def ShouldInlineStruct(struct): 346 # TODO(darin): Base this on the size of the wrapper class. 347 if len(struct.fields) > 4: 348 return False 349 for field in struct.fields: 350 if mojom.IsReferenceKind(field.kind) and not mojom.IsStringKind(field.kind): 351 return False 352 return True 353 354def ShouldInlineUnion(union): 355 return not any( 356 mojom.IsReferenceKind(field.kind) and not mojom.IsStringKind(field.kind) 357 for field in union.fields) 358 359def GetContainerValidateParamsCtorArgs(kind): 360 if mojom.IsStringKind(kind): 361 expected_num_elements = 0 362 element_is_nullable = False 363 key_validate_params = "nullptr" 364 element_validate_params = "nullptr" 365 enum_validate_func = "nullptr" 366 elif mojom.IsMapKind(kind): 367 expected_num_elements = 0 368 element_is_nullable = False 369 key_validate_params = GetNewContainerValidateParams(mojom.Array( 370 kind=kind.key_kind)) 371 element_validate_params = GetNewContainerValidateParams(mojom.Array( 372 kind=kind.value_kind)) 373 enum_validate_func = "nullptr" 374 else: # mojom.IsArrayKind(kind) 375 expected_num_elements = generator.ExpectedArraySize(kind) or 0 376 element_is_nullable = mojom.IsNullableKind(kind.kind) 377 key_validate_params = "nullptr" 378 element_validate_params = GetNewContainerValidateParams(kind.kind) 379 if mojom.IsEnumKind(kind.kind): 380 enum_validate_func = ("%s::Validate" % 381 GetQualifiedNameForKind(kind.kind, internal=True)) 382 else: 383 enum_validate_func = "nullptr" 384 385 if enum_validate_func == "nullptr": 386 if key_validate_params == "nullptr": 387 return "%d, %s, %s" % (expected_num_elements, 388 "true" if element_is_nullable else "false", 389 element_validate_params) 390 else: 391 return "%s, %s" % (key_validate_params, element_validate_params) 392 else: 393 return "%d, %s" % (expected_num_elements, enum_validate_func) 394 395def GetNewContainerValidateParams(kind): 396 if (not mojom.IsArrayKind(kind) and not mojom.IsMapKind(kind) and 397 not mojom.IsStringKind(kind)): 398 return "nullptr" 399 400 return "new mojo::internal::ContainerValidateParams(%s)" % ( 401 GetContainerValidateParamsCtorArgs(kind)) 402 403class Generator(generator.Generator): 404 405 cpp_filters = { 406 "constant_value": ConstantValue, 407 "cpp_wrapper_param_type": GetCppWrapperParamType, 408 "cpp_field_type": GetCppFieldType, 409 "cpp_union_field_type": GetCppUnionFieldType, 410 "cpp_pod_type": GetCppPodType, 411 "cpp_union_getter_return_type": GetUnionGetterReturnType, 412 "cpp_wrapper_type": GetCppWrapperType, 413 "default_value": DefaultValue, 414 "expression_to_text": ExpressionToText, 415 "get_container_validate_params_ctor_args": 416 GetContainerValidateParamsCtorArgs, 417 "get_name_for_kind": GetNameForKind, 418 "get_pad": pack.GetPad, 419 "get_qualified_name_for_kind": GetQualifiedNameForKind, 420 "has_callbacks": mojom.HasCallbacks, 421 "has_sync_methods": mojom.HasSyncMethods, 422 "should_inline": ShouldInlineStruct, 423 "should_inline_union": ShouldInlineUnion, 424 "is_array_kind": mojom.IsArrayKind, 425 "is_enum_kind": mojom.IsEnumKind, 426 "is_integral_kind": mojom.IsIntegralKind, 427 "is_native_only_kind": IsNativeOnlyKind, 428 "is_any_handle_or_interface_kind": mojom.IsAnyHandleOrInterfaceKind, 429 "is_associated_kind": mojom.IsAssociatedKind, 430 "is_map_kind": mojom.IsMapKind, 431 "is_nullable_kind": mojom.IsNullableKind, 432 "is_object_kind": mojom.IsObjectKind, 433 "is_string_kind": mojom.IsStringKind, 434 "is_struct_kind": mojom.IsStructKind, 435 "is_typemapped_kind": IsTypemappedKind, 436 "is_union_kind": mojom.IsUnionKind, 437 "passes_associated_kinds": mojom.PassesAssociatedKinds, 438 "struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE, 439 "stylize_method": generator.StudlyCapsToCamel, 440 "under_to_camel": generator.UnderToCamel, 441 "unmapped_type_for_serializer": GetUnmappedTypeForSerializer, 442 } 443 444 def GetExtraTraitsHeaders(self): 445 extra_headers = set() 446 for entry in self.typemap.itervalues(): 447 extra_headers.update(entry.get("traits_headers", [])) 448 return list(extra_headers) 449 450 def GetExtraPublicHeaders(self): 451 extra_headers = set() 452 for entry in self.typemap.itervalues(): 453 extra_headers.update(entry.get("public_headers", [])) 454 return list(extra_headers) 455 456 def GetJinjaExports(self): 457 return { 458 "module": self.module, 459 "namespace": self.module.namespace, 460 "namespaces_as_array": NamespaceToArray(self.module.namespace), 461 "imports": self.module.imports, 462 "kinds": self.module.kinds, 463 "enums": self.module.enums, 464 "structs": self.GetStructs(), 465 "unions": self.GetUnions(), 466 "interfaces": self.GetInterfaces(), 467 "variant": self.variant, 468 "extra_traits_headers": self.GetExtraTraitsHeaders(), 469 "extra_public_headers": self.GetExtraPublicHeaders(), 470 "for_blink": self.for_blink, 471 "use_new_wrapper_types": self.use_new_wrapper_types, 472 } 473 474 @staticmethod 475 def GetTemplatePrefix(): 476 return "cpp_templates" 477 478 @classmethod 479 def GetFilters(cls): 480 return cls.cpp_filters 481 482 @UseJinja("module.h.tmpl") 483 def GenerateModuleHeader(self): 484 return self.GetJinjaExports() 485 486 @UseJinja("module-internal.h.tmpl") 487 def GenerateModuleInternalHeader(self): 488 return self.GetJinjaExports() 489 490 @UseJinja("module.cc.tmpl") 491 def GenerateModuleSource(self): 492 return self.GetJinjaExports() 493 494 def GenerateFiles(self, args): 495 global _current_typemap 496 _current_typemap = self.typemap 497 global _for_blink 498 _for_blink = self.for_blink 499 global _use_new_wrapper_types 500 _use_new_wrapper_types = self.use_new_wrapper_types 501 global _variant 502 _variant = self.variant 503 suffix = "-%s" % self.variant if self.variant else "" 504 self.Write(self.GenerateModuleHeader(), 505 self.MatchMojomFilePath("%s%s.h" % (self.module.name, suffix))) 506 self.Write(self.GenerateModuleInternalHeader(), 507 self.MatchMojomFilePath("%s%s-internal.h" % (self.module.name, suffix))) 508 self.Write(self.GenerateModuleSource(), 509 self.MatchMojomFilePath("%s%s.cc" % (self.module.name, suffix))) 510