blob: 6184f6dfe483b61b7c1d21ff667c06140e448c28 [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 JavaScript 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_javascript_default_value = {
mojom.BOOL: "false",
mojom.INT8: "0",
mojom.UINT8: "0",
mojom.INT16: "0",
mojom.UINT16: "0",
mojom.INT32: "0",
mojom.UINT32: "0",
mojom.FLOAT: "0",
mojom.HANDLE: "null",
mojom.DCPIPE: "null",
mojom.DPPIPE: "null",
mojom.MSGPIPE: "null",
mojom.SHAREDBUFFER: "null",
mojom.NULLABLE_HANDLE: "null",
mojom.NULLABLE_DCPIPE: "null",
mojom.NULLABLE_DPPIPE: "null",
mojom.NULLABLE_MSGPIPE: "null",
mojom.NULLABLE_SHAREDBUFFER: "null",
mojom.INT64: "0",
mojom.UINT64: "0",
mojom.DOUBLE: "0",
mojom.STRING: "null",
mojom.NULLABLE_STRING: "null"
}
def JavaScriptType(kind):
name = []
if kind.imported_from:
name.append(kind.imported_from["unique_name"])
if kind.parent_kind:
name.append(kind.parent_kind.name)
name.append(kind.name)
return ".".join(name)
def JavaScriptDefaultValue(field):
if field.default:
if mojom.IsStructKind(field.kind):
assert field.default == "default"
return "new %s()" % JavaScriptType(field.kind)
return ExpressionToText(field.default)
if field.kind in mojom.PRIMITIVES:
return _kind_to_javascript_default_value[field.kind]
if mojom.IsStructKind(field.kind):
return "null"
if mojom.IsUnionKind(field.kind):
return "null"
if mojom.IsArrayKind(field.kind):
return "null"
if mojom.IsMapKind(field.kind):
return "null"
if mojom.IsInterfaceKind(field.kind):
return "new %sPtr()" % JavaScriptType(field.kind)
if mojom.IsInterfaceRequestKind(field.kind):
return "new bindings.InterfaceRequest()"
if mojom.IsAssociatedKind(field.kind):
return "null"
if mojom.IsEnumKind(field.kind):
return "0"
raise Exception("No valid default: %s" % field)
def JavaScriptPayloadSize(packed):
packed_fields = packed.packed_fields
if not packed_fields:
return 0
last_field = packed_fields[-1]
offset = last_field.offset + last_field.size
pad = pack.GetPad(offset, 8)
return offset + pad
_kind_to_codec_type = {
mojom.BOOL: "codec.Uint8",
mojom.INT8: "codec.Int8",
mojom.UINT8: "codec.Uint8",
mojom.INT16: "codec.Int16",
mojom.UINT16: "codec.Uint16",
mojom.INT32: "codec.Int32",
mojom.UINT32: "codec.Uint32",
mojom.FLOAT: "codec.Float",
mojom.HANDLE: "codec.Handle",
mojom.DCPIPE: "codec.Handle",
mojom.DPPIPE: "codec.Handle",
mojom.MSGPIPE: "codec.Handle",
mojom.SHAREDBUFFER: "codec.Handle",
mojom.NULLABLE_HANDLE: "codec.NullableHandle",
mojom.NULLABLE_DCPIPE: "codec.NullableHandle",
mojom.NULLABLE_DPPIPE: "codec.NullableHandle",
mojom.NULLABLE_MSGPIPE: "codec.NullableHandle",
mojom.NULLABLE_SHAREDBUFFER: "codec.NullableHandle",
mojom.INT64: "codec.Int64",
mojom.UINT64: "codec.Uint64",
mojom.DOUBLE: "codec.Double",
mojom.STRING: "codec.String",
mojom.NULLABLE_STRING: "codec.NullableString",
}
def CodecType(kind):
if kind in mojom.PRIMITIVES:
return _kind_to_codec_type[kind]
if mojom.IsStructKind(kind):
pointer_type = "NullablePointerTo" if mojom.IsNullableKind(kind) \
else "PointerTo"
return "new codec.%s(%s)" % (pointer_type, JavaScriptType(kind))
if mojom.IsUnionKind(kind):
return JavaScriptType(kind)
if mojom.IsArrayKind(kind):
array_type = "NullableArrayOf" if mojom.IsNullableKind(kind) else "ArrayOf"
array_length = "" if kind.length is None else ", %d" % kind.length
element_type = ElementCodecType(kind.kind)
return "new codec.%s(%s%s)" % (array_type, element_type, array_length)
if mojom.IsInterfaceKind(kind):
return "new codec.%s(%sPtr)" % (
"NullableInterface" if mojom.IsNullableKind(kind) else "Interface",
JavaScriptType(kind))
if mojom.IsInterfaceRequestKind(kind):
return "codec.%s" % (
"NullableInterfaceRequest" if mojom.IsNullableKind(kind)
else "InterfaceRequest")
if mojom.IsAssociatedInterfaceKind(kind):
return "codec.AssociatedInterfaceNotSupported"
if mojom.IsAssociatedInterfaceRequestKind(kind):
return "codec.AssociatedInterfaceRequestNotSupported"
if mojom.IsEnumKind(kind):
return "new codec.Enum(%s)" % JavaScriptType(kind)
if mojom.IsMapKind(kind):
map_type = "NullableMapOf" if mojom.IsNullableKind(kind) else "MapOf"
key_type = ElementCodecType(kind.key_kind)
value_type = ElementCodecType(kind.value_kind)
return "new codec.%s(%s, %s)" % (map_type, key_type, value_type)
raise Exception("No codec type for %s" % kind)
def ElementCodecType(kind):
return "codec.PackedBool" if mojom.IsBoolKind(kind) else CodecType(kind)
def JavaScriptDecodeSnippet(kind):
if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or
mojom.IsAnyInterfaceKind(kind)):
return "decodeStruct(%s)" % CodecType(kind)
if mojom.IsStructKind(kind):
return "decodeStructPointer(%s)" % JavaScriptType(kind)
if mojom.IsMapKind(kind):
return "decodeMapPointer(%s, %s)" % \
(ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind))
if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind):
return "decodeArrayPointer(codec.PackedBool)"
if mojom.IsArrayKind(kind):
return "decodeArrayPointer(%s)" % CodecType(kind.kind)
if mojom.IsUnionKind(kind):
return "decodeUnion(%s)" % CodecType(kind)
if mojom.IsEnumKind(kind):
return JavaScriptDecodeSnippet(mojom.INT32)
raise Exception("No decode snippet for %s" % kind)
def JavaScriptEncodeSnippet(kind):
if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or
mojom.IsAnyInterfaceKind(kind)):
return "encodeStruct(%s, " % CodecType(kind)
if mojom.IsUnionKind(kind):
return "encodeStruct(%s, " % JavaScriptType(kind)
if mojom.IsStructKind(kind):
return "encodeStructPointer(%s, " % JavaScriptType(kind)
if mojom.IsMapKind(kind):
return "encodeMapPointer(%s, %s, " % \
(ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind))
if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind):
return "encodeArrayPointer(codec.PackedBool, ";
if mojom.IsArrayKind(kind):
return "encodeArrayPointer(%s, " % CodecType(kind.kind)
if mojom.IsEnumKind(kind):
return JavaScriptEncodeSnippet(mojom.INT32)
raise Exception("No encode snippet for %s" % kind)
def JavaScriptUnionDecodeSnippet(kind):
if mojom.IsUnionKind(kind):
return "decodeStructPointer(%s)" % JavaScriptType(kind)
return JavaScriptDecodeSnippet(kind)
def JavaScriptUnionEncodeSnippet(kind):
if mojom.IsUnionKind(kind):
return "encodeStructPointer(%s, " % JavaScriptType(kind)
return JavaScriptEncodeSnippet(kind)
def JavaScriptFieldOffset(packed_field):
return "offset + codec.kStructHeaderSize + %s" % packed_field.offset
def JavaScriptNullableParam(field):
return "true" if mojom.IsNullableKind(field.kind) else "false"
def GetArrayExpectedDimensionSizes(kind):
expected_dimension_sizes = []
while mojom.IsArrayKind(kind):
expected_dimension_sizes.append(generator.ExpectedArraySize(kind) or 0)
kind = kind.kind
# Strings are serialized as variable-length arrays.
if (mojom.IsStringKind(kind)):
expected_dimension_sizes.append(0)
return expected_dimension_sizes
def JavaScriptValidateArrayParams(field):
nullable = JavaScriptNullableParam(field)
element_kind = field.kind.kind
element_size = pack.PackedField.GetSizeForKind(element_kind)
expected_dimension_sizes = GetArrayExpectedDimensionSizes(
field.kind)
element_type = ElementCodecType(element_kind)
return "%s, %s, %s, %s, 0" % \
(element_size, element_type, nullable,
expected_dimension_sizes)
def JavaScriptValidateEnumParams(field):
return JavaScriptType(field.kind)
def JavaScriptValidateStructParams(field):
nullable = JavaScriptNullableParam(field)
struct_type = JavaScriptType(field.kind)
return "%s, %s" % (struct_type, nullable)
def JavaScriptValidateUnionParams(field):
nullable = JavaScriptNullableParam(field)
union_type = JavaScriptType(field.kind)
return "%s, %s" % (union_type, nullable)
def JavaScriptValidateMapParams(field):
nullable = JavaScriptNullableParam(field)
keys_type = ElementCodecType(field.kind.key_kind)
values_kind = field.kind.value_kind;
values_type = ElementCodecType(values_kind)
values_nullable = "true" if mojom.IsNullableKind(values_kind) else "false"
return "%s, %s, %s, %s" % \
(nullable, keys_type, values_type, values_nullable)
def TranslateConstants(token):
if isinstance(token, (mojom.EnumValue, mojom.NamedValue)):
# Both variable and enum constants are constructed like:
# NamespaceUid.Struct[.Enum].CONSTANT_NAME
name = []
if token.imported_from:
name.append(token.imported_from["unique_name"])
if token.parent_kind:
name.append(token.parent_kind.name)
if isinstance(token, mojom.EnumValue):
name.append(token.enum.name)
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";
return token
def ExpressionToText(value):
return TranslateConstants(value)
def IsArrayPointerField(field):
return mojom.IsArrayKind(field.kind)
def IsEnumField(field):
return mojom.IsEnumKind(field.kind)
def IsStringPointerField(field):
return mojom.IsStringKind(field.kind)
def IsStructPointerField(field):
return mojom.IsStructKind(field.kind)
def IsMapPointerField(field):
return mojom.IsMapKind(field.kind)
def IsHandleField(field):
return mojom.IsAnyHandleKind(field.kind)
def IsInterfaceField(field):
return mojom.IsInterfaceKind(field.kind)
def IsInterfaceRequestField(field):
return mojom.IsInterfaceRequestKind(field.kind)
def IsUnionField(field):
return mojom.IsUnionKind(field.kind)
def IsBoolField(field):
return mojom.IsBoolKind(field.kind)
def IsObjectField(field):
return mojom.IsObjectKind(field.kind)
def IsAnyHandleOrInterfaceField(field):
return mojom.IsAnyHandleOrInterfaceKind(field.kind)
def IsEnumField(field):
return mojom.IsEnumKind(field.kind)
class Generator(generator.Generator):
js_filters = {
"decode_snippet": JavaScriptDecodeSnippet,
"default_value": JavaScriptDefaultValue,
"encode_snippet": JavaScriptEncodeSnippet,
"expression_to_text": ExpressionToText,
"field_offset": JavaScriptFieldOffset,
"has_callbacks": mojom.HasCallbacks,
"is_any_handle_or_interface_field": IsAnyHandleOrInterfaceField,
"is_array_pointer_field": IsArrayPointerField,
"is_bool_field": IsBoolField,
"is_enum_field": IsEnumField,
"is_handle_field": IsHandleField,
"is_interface_field": IsInterfaceField,
"is_interface_request_field": IsInterfaceRequestField,
"is_map_pointer_field": IsMapPointerField,
"is_object_field": IsObjectField,
"is_string_pointer_field": IsStringPointerField,
"is_struct_pointer_field": IsStructPointerField,
"is_union_field": IsUnionField,
"js_type": JavaScriptType,
"payload_size": JavaScriptPayloadSize,
"stylize_method": generator.StudlyCapsToCamel,
"union_decode_snippet": JavaScriptUnionDecodeSnippet,
"union_encode_snippet": JavaScriptUnionEncodeSnippet,
"validate_array_params": JavaScriptValidateArrayParams,
"validate_enum_params": JavaScriptValidateEnumParams,
"validate_map_params": JavaScriptValidateMapParams,
"validate_nullable_params": JavaScriptNullableParam,
"validate_struct_params": JavaScriptValidateStructParams,
"validate_union_params": JavaScriptValidateUnionParams,
}
def GetParameters(self):
return {
"namespace": self.module.namespace,
"imports": self.GetImports(),
"kinds": self.module.kinds,
"enums": self.module.enums,
"module": self.module,
"structs": self.GetStructs() + self.GetStructsFromMethods(),
"unions": self.GetUnions(),
"use_new_js_bindings": self.use_new_js_bindings,
"interfaces": self.GetInterfaces(),
"imported_interfaces": self.GetImportedInterfaces(),
}
@staticmethod
def GetTemplatePrefix():
return "js_templates"
@classmethod
def GetFilters(cls):
return cls.js_filters
@UseJinja("module.amd.tmpl")
def GenerateAMDModule(self):
return self.GetParameters()
def GenerateFiles(self, args):
if self.variant:
raise Exception("Variants not supported in JavaScript bindings.")
self.Write(self.GenerateAMDModule(),
self.MatchMojomFilePath("%s.js" % self.module.name))
def GetImports(self):
used_names = set()
for each_import in self.module.imports:
simple_name = each_import["module_name"].split(".")[0]
# Since each import is assigned a variable in JS, they need to have unique
# names.
unique_name = simple_name
counter = 0
while unique_name in used_names:
counter += 1
unique_name = simple_name + str(counter)
used_names.add(unique_name)
each_import["unique_name"] = unique_name + "$"
counter += 1
return self.module.imports
def GetImportedInterfaces(self):
interface_to_import = {};
for each_import in self.module.imports:
for each_interface in each_import["module"].interfaces:
name = each_interface.name
interface_to_import[name] = each_import["unique_name"] + "." + name
return interface_to_import;