blob: 4c632702d67844693eb266087b14a024106ed23f [file] [log] [blame]
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Generates C++ source files from a mojom.Module."""
import mojom.generate.generator as generator
import mojom.generate.module as mojom
import mojom.generate.pack as pack
from mojom.generate.template_expander import UseJinja
_kind_to_cpp_type = {
mojom.BOOL: "bool",
mojom.INT8: "int8_t",
mojom.UINT8: "uint8_t",
mojom.INT16: "int16_t",
mojom.UINT16: "uint16_t",
mojom.INT32: "int32_t",
mojom.UINT32: "uint32_t",
mojom.FLOAT: "float",
mojom.HANDLE: "mojo::Handle",
mojom.DCPIPE: "mojo::DataPipeConsumerHandle",
mojom.DPPIPE: "mojo::DataPipeProducerHandle",
mojom.MSGPIPE: "mojo::MessagePipeHandle",
mojom.SHAREDBUFFER: "mojo::SharedBufferHandle",
mojom.NULLABLE_HANDLE: "mojo::Handle",
mojom.NULLABLE_DCPIPE: "mojo::DataPipeConsumerHandle",
mojom.NULLABLE_DPPIPE: "mojo::DataPipeProducerHandle",
mojom.NULLABLE_MSGPIPE: "mojo::MessagePipeHandle",
mojom.NULLABLE_SHAREDBUFFER: "mojo::SharedBufferHandle",
mojom.INT64: "int64_t",
mojom.UINT64: "uint64_t",
mojom.DOUBLE: "double",
}
_kind_to_cpp_literal_suffix = {
mojom.UINT8: "U",
mojom.UINT16: "U",
mojom.UINT32: "U",
mojom.FLOAT: "f",
mojom.UINT64: "ULL",
}
def ConstantValue(constant):
return ExpressionToText(constant.value, kind=constant.kind)
def DefaultValue(field):
if field.default:
if mojom.IsStructKind(field.kind):
assert field.default == "default"
return "%s::New()" % GetNameForKind(field.kind)
return ExpressionToText(field.default, kind=field.kind)
return ""
def NamespaceToArray(namespace):
return namespace.split(".") if namespace else []
def GetNameForKind(kind, internal = False):
parts = []
if kind.imported_from:
parts.extend(NamespaceToArray(kind.imported_from["namespace"]))
if internal:
parts.append("internal")
if kind.parent_kind:
parts.append(kind.parent_kind.name)
parts.append(kind.name)
return "::".join(parts)
def GetCppType(kind):
if mojom.IsArrayKind(kind):
return "mojo::internal::Array_Data<%s>*" % GetCppType(kind.kind)
if mojom.IsMapKind(kind):
return "mojo::internal::Map_Data<%s, %s>*" % (
GetCppType(kind.key_kind), GetCppType(kind.value_kind))
if mojom.IsStructKind(kind):
return "%s_Data*" % GetNameForKind(kind, internal=True)
if mojom.IsUnionKind(kind):
return "%s_Data" % GetNameForKind(kind, internal=True)
if mojom.IsInterfaceKind(kind):
return "mojo::internal::Interface_Data"
if mojom.IsInterfaceRequestKind(kind):
return "mojo::MessagePipeHandle"
if mojom.IsAssociatedInterfaceKind(kind):
return "mojo::internal::AssociatedInterface_Data"
if mojom.IsAssociatedInterfaceRequestKind(kind):
return "mojo::internal::AssociatedInterfaceRequest_Data"
if mojom.IsEnumKind(kind):
return "int32_t"
if mojom.IsStringKind(kind):
return "mojo::internal::String_Data*"
return _kind_to_cpp_type[kind]
def GetCppPodType(kind):
if mojom.IsStringKind(kind):
return "char*"
return _kind_to_cpp_type[kind]
def GetCppArrayArgWrapperType(kind):
if mojom.IsEnumKind(kind):
return GetNameForKind(kind)
if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
return "%sPtr" % GetNameForKind(kind)
if mojom.IsArrayKind(kind):
return "mojo::Array<%s> " % GetCppArrayArgWrapperType(kind.kind)
if mojom.IsMapKind(kind):
return "mojo::Map<%s, %s> " % (GetCppArrayArgWrapperType(kind.key_kind),
GetCppArrayArgWrapperType(kind.value_kind))
if mojom.IsInterfaceKind(kind):
raise Exception("Arrays of interfaces not yet supported!")
if mojom.IsInterfaceRequestKind(kind):
raise Exception("Arrays of interface requests not yet supported!")
if mojom.IsAssociatedInterfaceKind(kind):
raise Exception("Arrays of associated interfaces not yet supported!")
if mojom.IsAssociatedInterfaceRequestKind(kind):
raise Exception("Arrays of associated interface requests not yet "
"supported!")
if mojom.IsStringKind(kind):
return "mojo::String"
if mojom.IsGenericHandleKind(kind):
return "mojo::ScopedHandle"
if mojom.IsDataPipeConsumerKind(kind):
return "mojo::ScopedDataPipeConsumerHandle"
if mojom.IsDataPipeProducerKind(kind):
return "mojo::ScopedDataPipeProducerHandle"
if mojom.IsMessagePipeKind(kind):
return "mojo::ScopedMessagePipeHandle"
if mojom.IsSharedBufferKind(kind):
return "mojo::ScopedSharedBufferHandle"
return _kind_to_cpp_type[kind]
def GetCppResultWrapperType(kind):
if mojom.IsEnumKind(kind):
return GetNameForKind(kind)
if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
return "%sPtr" % GetNameForKind(kind)
if mojom.IsArrayKind(kind):
return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
if mojom.IsMapKind(kind):
return "mojo::Map<%s, %s>" % (GetCppArrayArgWrapperType(kind.key_kind),
GetCppArrayArgWrapperType(kind.value_kind))
if mojom.IsInterfaceKind(kind):
return "%sPtr" % GetNameForKind(kind)
if mojom.IsInterfaceRequestKind(kind):
return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind)
if mojom.IsAssociatedInterfaceKind(kind):
return "mojo::AssociatedInterfacePtrInfo<%s>" % GetNameForKind(kind.kind)
if mojom.IsAssociatedInterfaceRequestKind(kind):
return "mojo::AssociatedInterfaceRequest<%s>" % GetNameForKind(kind.kind)
if mojom.IsStringKind(kind):
return "mojo::String"
if mojom.IsGenericHandleKind(kind):
return "mojo::ScopedHandle"
if mojom.IsDataPipeConsumerKind(kind):
return "mojo::ScopedDataPipeConsumerHandle"
if mojom.IsDataPipeProducerKind(kind):
return "mojo::ScopedDataPipeProducerHandle"
if mojom.IsMessagePipeKind(kind):
return "mojo::ScopedMessagePipeHandle"
if mojom.IsSharedBufferKind(kind):
return "mojo::ScopedSharedBufferHandle"
# TODO(rudominer) After improvements to compiler front end have landed,
# revisit strategy used below for emitting a useful error message when an
# undefined identifier is referenced.
val = _kind_to_cpp_type.get(kind)
if (val is not None):
return val
raise Exception("Unrecognized kind %s" % kind.spec)
def GetCppWrapperType(kind):
if mojom.IsEnumKind(kind):
return GetNameForKind(kind)
if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
return "%sPtr" % GetNameForKind(kind)
if mojom.IsArrayKind(kind):
return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
if mojom.IsMapKind(kind):
return "mojo::Map<%s, %s>" % (GetCppArrayArgWrapperType(kind.key_kind),
GetCppArrayArgWrapperType(kind.value_kind))
if mojom.IsInterfaceKind(kind):
return "%sPtr" % GetNameForKind(kind)
if mojom.IsInterfaceRequestKind(kind):
return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind)
if mojom.IsAssociatedInterfaceKind(kind):
return "mojo::AssociatedInterfacePtrInfo<%s>" % GetNameForKind(kind.kind)
if mojom.IsAssociatedInterfaceRequestKind(kind):
return "mojo::AssociatedInterfaceRequest<%s>" % GetNameForKind(kind.kind)
if mojom.IsStringKind(kind):
return "mojo::String"
if mojom.IsGenericHandleKind(kind):
return "mojo::ScopedHandle"
if mojom.IsDataPipeConsumerKind(kind):
return "mojo::ScopedDataPipeConsumerHandle"
if mojom.IsDataPipeProducerKind(kind):
return "mojo::ScopedDataPipeProducerHandle"
if mojom.IsMessagePipeKind(kind):
return "mojo::ScopedMessagePipeHandle"
if mojom.IsSharedBufferKind(kind):
return "mojo::ScopedSharedBufferHandle"
return _kind_to_cpp_type[kind]
def GetCppConstWrapperType(kind):
if mojom.IsStructKind(kind) or mojom.IsUnionKind(kind):
return "%sPtr" % GetNameForKind(kind)
if mojom.IsArrayKind(kind):
return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind)
if mojom.IsMapKind(kind):
return "mojo::Map<%s, %s>" % (GetCppArrayArgWrapperType(kind.key_kind),
GetCppArrayArgWrapperType(kind.value_kind))
if mojom.IsInterfaceKind(kind):
return "%sPtr" % GetNameForKind(kind)
if mojom.IsInterfaceRequestKind(kind):
return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind)
if mojom.IsAssociatedInterfaceKind(kind):
return "mojo::AssociatedInterfacePtrInfo<%s>" % GetNameForKind(kind.kind)
if mojom.IsAssociatedInterfaceRequestKind(kind):
return "mojo::AssociatedInterfaceRequest<%s>" % GetNameForKind(kind.kind)
if mojom.IsEnumKind(kind):
return GetNameForKind(kind)
if mojom.IsStringKind(kind):
return "const mojo::String&"
if mojom.IsGenericHandleKind(kind):
return "mojo::ScopedHandle"
if mojom.IsDataPipeConsumerKind(kind):
return "mojo::ScopedDataPipeConsumerHandle"
if mojom.IsDataPipeProducerKind(kind):
return "mojo::ScopedDataPipeProducerHandle"
if mojom.IsMessagePipeKind(kind):
return "mojo::ScopedMessagePipeHandle"
if mojom.IsSharedBufferKind(kind):
return "mojo::ScopedSharedBufferHandle"
if not kind in _kind_to_cpp_type:
print "missing:", kind.spec
return _kind_to_cpp_type[kind]
def GetCppFieldType(kind):
if mojom.IsStructKind(kind):
return ("mojo::internal::StructPointer<%s_Data>" %
GetNameForKind(kind, internal=True))
if mojom.IsUnionKind(kind):
return "%s_Data" % GetNameForKind(kind, internal=True)
if mojom.IsArrayKind(kind):
return "mojo::internal::ArrayPointer<%s>" % GetCppType(kind.kind)
if mojom.IsMapKind(kind):
return ("mojo::internal::StructPointer<mojo::internal::Map_Data<%s, %s>>" %
(GetCppType(kind.key_kind), GetCppType(kind.value_kind)))
if mojom.IsInterfaceKind(kind):
return "mojo::internal::Interface_Data"
if mojom.IsInterfaceRequestKind(kind):
return "mojo::MessagePipeHandle"
if mojom.IsAssociatedInterfaceKind(kind):
return "mojo::internal::AssociatedInterface_Data"
if mojom.IsAssociatedInterfaceRequestKind(kind):
return "mojo::internal::AssociatedInterfaceRequest_Data"
if mojom.IsEnumKind(kind):
return GetNameForKind(kind)
if mojom.IsStringKind(kind):
return "mojo::internal::StringPointer"
return _kind_to_cpp_type[kind]
def GetCppUnionFieldType(kind):
if mojom.IsAnyHandleKind(kind):
return "MojoHandle"
if mojom.IsInterfaceKind(kind):
return "uint64_t"
if mojom.IsEnumKind(kind):
return "int32_t"
if mojom.IsUnionKind(kind):
return ("mojo::internal::UnionPointer<%s_Data>" %
GetNameForKind(kind, internal=True))
return GetCppFieldType(kind)
def GetUnionGetterReturnType(kind):
if (mojom.IsStructKind(kind) or mojom.IsUnionKind(kind) or
mojom.IsArrayKind(kind) or mojom.IsMapKind(kind) or
mojom.IsAnyHandleKind(kind) or mojom.IsInterfaceKind(kind)
or mojom.IsAssociatedKind(kind)):
return "%s&" % GetCppWrapperType(kind)
return GetCppResultWrapperType(kind)
def TranslateConstants(token, kind):
if isinstance(token, mojom.NamedValue):
# Both variable and enum constants are constructed like:
# Namespace::Struct::CONSTANT_NAME
# For enums, CONSTANT_NAME is ENUM_NAME_ENUM_VALUE.
name = []
if token.imported_from:
name.extend(NamespaceToArray(token.namespace))
if token.parent_kind:
name.append(token.parent_kind.name)
if isinstance(token, mojom.EnumValue):
name.append(
"%s_%s" % (generator.CamelCaseToAllCaps(token.enum.name), token.name))
else:
name.append(token.name)
return "::".join(name)
if isinstance(token, mojom.BuiltinValue):
if token.value == "double.INFINITY" or token.value == "float.INFINITY":
return "INFINITY";
if token.value == "double.NEGATIVE_INFINITY" or \
token.value == "float.NEGATIVE_INFINITY":
return "-INFINITY";
if token.value == "double.NAN" or token.value == "float.NAN":
return "NAN";
if (kind is not None and mojom.IsFloatKind(kind)):
return token if token.isdigit() else token + "f";
# Per C++11, 2.14.2, the type of an integer literal is the first of the
# corresponding list in Table 6 in which its value can be represented. In this
# case, the list for decimal constants with no suffix is:
# int, long int, long long int
# The standard considers a program ill-formed if it contains an integer
# literal that cannot be represented by any of the allowed types.
#
# As it turns out, MSVC doesn't bother trying to fall back to long long int,
# so the integral constant -2147483648 causes it grief: it decides to
# represent 2147483648 as an unsigned integer, and then warns that the unary
# minus operator doesn't make sense on unsigned types. Doh!
if kind == mojom.INT32 and token == "-2147483648":
return "(-%d - 1) /* %s */" % (
2**31 - 1, "Workaround for MSVC bug; see https://crbug.com/445618")
return "%s%s" % (token, _kind_to_cpp_literal_suffix.get(kind, ""))
def ExpressionToText(value, kind=None):
return TranslateConstants(value, kind)
def ShouldInlineStruct(struct):
# TODO(darin): Base this on the size of the wrapper class.
if len(struct.fields) > 4:
return False
for field in struct.fields:
if mojom.IsMoveOnlyKind(field.kind):
return False
return True
def ShouldInlineUnion(union):
return not any(mojom.IsMoveOnlyKind(field.kind) for field in union.fields)
def GetArrayValidateParamsCtorArgs(kind):
if mojom.IsStringKind(kind):
expected_num_elements = 0
element_is_nullable = False
element_validate_params = "nullptr"
elif mojom.IsMapKind(kind):
expected_num_elements = 0
element_is_nullable = mojom.IsNullableKind(kind.value_kind)
element_validate_params = GetNewArrayValidateParams(kind.value_kind)
else:
expected_num_elements = generator.ExpectedArraySize(kind) or 0
element_is_nullable = mojom.IsNullableKind(kind.kind)
element_validate_params = GetNewArrayValidateParams(kind.kind)
return "%d, %s, %s" % (expected_num_elements,
"true" if element_is_nullable else "false",
element_validate_params)
def GetNewArrayValidateParams(kind):
if (not mojom.IsArrayKind(kind) and not mojom.IsMapKind(kind) and
not mojom.IsStringKind(kind)):
return "nullptr"
return "new mojo::internal::ArrayValidateParams(%s)" % (
GetArrayValidateParamsCtorArgs(kind))
def GetMapValidateParamsCtorArgs(value_kind):
# Unlike GetArrayValidateParams, we are given the wrapped kind, instead of
# the raw array kind. So we wrap the return value of GetArrayValidateParams.
element_is_nullable = mojom.IsNullableKind(value_kind)
return "0, %s, %s" % ("true" if element_is_nullable else "false",
GetNewArrayValidateParams(value_kind))
class Generator(generator.Generator):
cpp_filters = {
"constant_value": ConstantValue,
"cpp_const_wrapper_type": GetCppConstWrapperType,
"cpp_field_type": GetCppFieldType,
"cpp_union_field_type": GetCppUnionFieldType,
"cpp_pod_type": GetCppPodType,
"cpp_result_type": GetCppResultWrapperType,
"cpp_type": GetCppType,
"cpp_union_getter_return_type": GetUnionGetterReturnType,
"cpp_wrapper_type": GetCppWrapperType,
"default_value": DefaultValue,
"expression_to_text": ExpressionToText,
"get_array_validate_params_ctor_args": GetArrayValidateParamsCtorArgs,
"get_map_validate_params_ctor_args": GetMapValidateParamsCtorArgs,
"get_name_for_kind": GetNameForKind,
"get_pad": pack.GetPad,
"has_callbacks": mojom.HasCallbacks,
"should_inline": ShouldInlineStruct,
"should_inline_union": ShouldInlineUnion,
"is_array_kind": mojom.IsArrayKind,
"is_cloneable_kind": mojom.IsCloneableKind,
"is_enum_kind": mojom.IsEnumKind,
"is_integral_kind": mojom.IsIntegralKind,
"is_move_only_kind": mojom.IsMoveOnlyKind,
"is_any_handle_kind": mojom.IsAnyHandleKind,
"is_interface_kind": mojom.IsInterfaceKind,
"is_interface_request_kind": mojom.IsInterfaceRequestKind,
"is_associated_interface_kind": mojom.IsAssociatedInterfaceKind,
"is_associated_interface_request_kind":
mojom.IsAssociatedInterfaceRequestKind,
"is_associated_kind": mojom.IsAssociatedKind,
"is_map_kind": mojom.IsMapKind,
"is_nullable_kind": mojom.IsNullableKind,
"is_object_kind": mojom.IsObjectKind,
"is_string_kind": mojom.IsStringKind,
"is_struct_kind": mojom.IsStructKind,
"is_union_kind": mojom.IsUnionKind,
"passes_associated_kinds": mojom.PassesAssociatedKinds,
"struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE,
"stylize_method": generator.StudlyCapsToCamel,
"to_all_caps": generator.CamelCaseToAllCaps,
"under_to_camel": generator.UnderToCamel,
}
def GetJinjaExports(self):
return {
"module": self.module,
"namespace": self.module.namespace,
"namespaces_as_array": NamespaceToArray(self.module.namespace),
"imports": self.module.imports,
"kinds": self.module.kinds,
"enums": self.module.enums,
"structs": self.GetStructs(),
"unions": self.GetUnions(),
"interfaces": self.GetInterfaces(),
"variant": self.variant,
}
@UseJinja("cpp_templates/module.h.tmpl", filters=cpp_filters)
def GenerateModuleHeader(self):
return self.GetJinjaExports()
@UseJinja("cpp_templates/module-internal.h.tmpl", filters=cpp_filters)
def GenerateModuleInternalHeader(self):
return self.GetJinjaExports()
@UseJinja("cpp_templates/module.cc.tmpl", filters=cpp_filters)
def GenerateModuleSource(self):
return self.GetJinjaExports()
def GenerateFiles(self, args):
suffix = "-%s" % self.variant if self.variant else ""
self.Write(self.GenerateModuleHeader(),
self.MatchMojomFilePath("%s%s.h" % (self.module.name, suffix)))
self.Write(self.GenerateModuleInternalHeader(),
self.MatchMojomFilePath("%s%s-internal.h" % (self.module.name, suffix)))
self.Write(self.GenerateModuleSource(),
self.MatchMojomFilePath("%s%s.cc" % (self.module.name, suffix)))