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 JavaScript 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_kind_to_javascript_default_value = { 13 mojom.BOOL: "false", 14 mojom.INT8: "0", 15 mojom.UINT8: "0", 16 mojom.INT16: "0", 17 mojom.UINT16: "0", 18 mojom.INT32: "0", 19 mojom.UINT32: "0", 20 mojom.FLOAT: "0", 21 mojom.HANDLE: "null", 22 mojom.DCPIPE: "null", 23 mojom.DPPIPE: "null", 24 mojom.MSGPIPE: "null", 25 mojom.SHAREDBUFFER: "null", 26 mojom.NULLABLE_HANDLE: "null", 27 mojom.NULLABLE_DCPIPE: "null", 28 mojom.NULLABLE_DPPIPE: "null", 29 mojom.NULLABLE_MSGPIPE: "null", 30 mojom.NULLABLE_SHAREDBUFFER: "null", 31 mojom.INT64: "0", 32 mojom.UINT64: "0", 33 mojom.DOUBLE: "0", 34 mojom.STRING: "null", 35 mojom.NULLABLE_STRING: "null" 36} 37 38 39def JavaScriptType(kind): 40 if kind.imported_from: 41 return kind.imported_from["unique_name"] + "." + kind.name 42 return kind.name 43 44 45def JavaScriptDefaultValue(field): 46 if field.default: 47 if mojom.IsStructKind(field.kind): 48 assert field.default == "default" 49 return "new %s()" % JavaScriptType(field.kind) 50 return ExpressionToText(field.default) 51 if field.kind in mojom.PRIMITIVES: 52 return _kind_to_javascript_default_value[field.kind] 53 if mojom.IsStructKind(field.kind): 54 return "null" 55 if mojom.IsUnionKind(field.kind): 56 return "null" 57 if mojom.IsArrayKind(field.kind): 58 return "null" 59 if mojom.IsMapKind(field.kind): 60 return "null" 61 if mojom.IsInterfaceKind(field.kind) or \ 62 mojom.IsInterfaceRequestKind(field.kind): 63 return _kind_to_javascript_default_value[mojom.MSGPIPE] 64 if mojom.IsAssociatedKind(field.kind): 65 return "null" 66 if mojom.IsEnumKind(field.kind): 67 return "0" 68 raise Exception("No valid default: %s" % field) 69 70 71def JavaScriptPayloadSize(packed): 72 packed_fields = packed.packed_fields 73 if not packed_fields: 74 return 0 75 last_field = packed_fields[-1] 76 offset = last_field.offset + last_field.size 77 pad = pack.GetPad(offset, 8) 78 return offset + pad 79 80 81_kind_to_codec_type = { 82 mojom.BOOL: "codec.Uint8", 83 mojom.INT8: "codec.Int8", 84 mojom.UINT8: "codec.Uint8", 85 mojom.INT16: "codec.Int16", 86 mojom.UINT16: "codec.Uint16", 87 mojom.INT32: "codec.Int32", 88 mojom.UINT32: "codec.Uint32", 89 mojom.FLOAT: "codec.Float", 90 mojom.HANDLE: "codec.Handle", 91 mojom.DCPIPE: "codec.Handle", 92 mojom.DPPIPE: "codec.Handle", 93 mojom.MSGPIPE: "codec.Handle", 94 mojom.SHAREDBUFFER: "codec.Handle", 95 mojom.NULLABLE_HANDLE: "codec.NullableHandle", 96 mojom.NULLABLE_DCPIPE: "codec.NullableHandle", 97 mojom.NULLABLE_DPPIPE: "codec.NullableHandle", 98 mojom.NULLABLE_MSGPIPE: "codec.NullableHandle", 99 mojom.NULLABLE_SHAREDBUFFER: "codec.NullableHandle", 100 mojom.INT64: "codec.Int64", 101 mojom.UINT64: "codec.Uint64", 102 mojom.DOUBLE: "codec.Double", 103 mojom.STRING: "codec.String", 104 mojom.NULLABLE_STRING: "codec.NullableString", 105} 106 107 108def CodecType(kind): 109 if kind in mojom.PRIMITIVES: 110 return _kind_to_codec_type[kind] 111 if mojom.IsStructKind(kind): 112 pointer_type = "NullablePointerTo" if mojom.IsNullableKind(kind) \ 113 else "PointerTo" 114 return "new codec.%s(%s)" % (pointer_type, JavaScriptType(kind)) 115 if mojom.IsUnionKind(kind): 116 return JavaScriptType(kind) 117 if mojom.IsArrayKind(kind): 118 array_type = "NullableArrayOf" if mojom.IsNullableKind(kind) else "ArrayOf" 119 array_length = "" if kind.length is None else ", %d" % kind.length 120 element_type = ElementCodecType(kind.kind) 121 return "new codec.%s(%s%s)" % (array_type, element_type, array_length) 122 if mojom.IsInterfaceKind(kind): 123 return "codec.%s" % ("NullableInterface" if mojom.IsNullableKind(kind) 124 else "Interface") 125 if mojom.IsInterfaceRequestKind(kind): 126 return CodecType(mojom.MSGPIPE) 127 if mojom.IsAssociatedInterfaceKind(kind): 128 return "codec.AssociatedInterfaceNotSupported" 129 if mojom.IsAssociatedInterfaceRequestKind(kind): 130 return "codec.AssociatedInterfaceRequestNotSupported" 131 if mojom.IsEnumKind(kind): 132 return _kind_to_codec_type[mojom.INT32] 133 if mojom.IsMapKind(kind): 134 map_type = "NullableMapOf" if mojom.IsNullableKind(kind) else "MapOf" 135 key_type = ElementCodecType(kind.key_kind) 136 value_type = ElementCodecType(kind.value_kind) 137 return "new codec.%s(%s, %s)" % (map_type, key_type, value_type) 138 raise Exception("No codec type for %s" % kind) 139 140 141def ElementCodecType(kind): 142 return "codec.PackedBool" if mojom.IsBoolKind(kind) else CodecType(kind) 143 144def JavaScriptDecodeSnippet(kind): 145 if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or 146 mojom.IsInterfaceKind(kind) or mojom.IsAssociatedKind(kind)): 147 return "decodeStruct(%s)" % CodecType(kind) 148 if mojom.IsStructKind(kind): 149 return "decodeStructPointer(%s)" % JavaScriptType(kind) 150 if mojom.IsMapKind(kind): 151 return "decodeMapPointer(%s, %s)" % \ 152 (ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind)) 153 if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind): 154 return "decodeArrayPointer(codec.PackedBool)" 155 if mojom.IsArrayKind(kind): 156 return "decodeArrayPointer(%s)" % CodecType(kind.kind) 157 if mojom.IsUnionKind(kind): 158 return "decodeUnion(%s)" % CodecType(kind) 159 if mojom.IsInterfaceRequestKind(kind): 160 return JavaScriptDecodeSnippet(mojom.MSGPIPE) 161 if mojom.IsEnumKind(kind): 162 return JavaScriptDecodeSnippet(mojom.INT32) 163 raise Exception("No decode snippet for %s" % kind) 164 165 166def JavaScriptEncodeSnippet(kind): 167 if (kind in mojom.PRIMITIVES or mojom.IsUnionKind(kind) or 168 mojom.IsInterfaceKind(kind) or mojom.IsAssociatedKind(kind)): 169 return "encodeStruct(%s, " % CodecType(kind) 170 if mojom.IsUnionKind(kind): 171 return "encodeStruct(%s, " % JavaScriptType(kind) 172 if mojom.IsStructKind(kind): 173 return "encodeStructPointer(%s, " % JavaScriptType(kind) 174 if mojom.IsMapKind(kind): 175 return "encodeMapPointer(%s, %s, " % \ 176 (ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind)) 177 if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind): 178 return "encodeArrayPointer(codec.PackedBool, "; 179 if mojom.IsArrayKind(kind): 180 return "encodeArrayPointer(%s, " % CodecType(kind.kind) 181 if mojom.IsInterfaceRequestKind(kind): 182 return JavaScriptEncodeSnippet(mojom.MSGPIPE) 183 if mojom.IsEnumKind(kind): 184 return JavaScriptEncodeSnippet(mojom.INT32) 185 raise Exception("No encode snippet for %s" % kind) 186 187 188def JavaScriptUnionDecodeSnippet(kind): 189 if mojom.IsUnionKind(kind): 190 return "decodeStructPointer(%s)" % JavaScriptType(kind) 191 return JavaScriptDecodeSnippet(kind) 192 193 194def JavaScriptUnionEncodeSnippet(kind): 195 if mojom.IsUnionKind(kind): 196 return "encodeStructPointer(%s, " % JavaScriptType(kind) 197 return JavaScriptEncodeSnippet(kind) 198 199 200def JavaScriptFieldOffset(packed_field): 201 return "offset + codec.kStructHeaderSize + %s" % packed_field.offset 202 203 204def JavaScriptNullableParam(field): 205 return "true" if mojom.IsNullableKind(field.kind) else "false" 206 207 208def GetArrayExpectedDimensionSizes(kind): 209 expected_dimension_sizes = [] 210 while mojom.IsArrayKind(kind): 211 expected_dimension_sizes.append(generator.ExpectedArraySize(kind) or 0) 212 kind = kind.kind 213 # Strings are serialized as variable-length arrays. 214 if (mojom.IsStringKind(kind)): 215 expected_dimension_sizes.append(0) 216 return expected_dimension_sizes 217 218 219def JavaScriptValidateArrayParams(field): 220 nullable = JavaScriptNullableParam(field) 221 element_kind = field.kind.kind 222 element_size = pack.PackedField.GetSizeForKind(element_kind) 223 expected_dimension_sizes = GetArrayExpectedDimensionSizes( 224 field.kind) 225 element_type = ElementCodecType(element_kind) 226 return "%s, %s, %s, %s, 0" % \ 227 (element_size, element_type, nullable, 228 expected_dimension_sizes) 229 230 231def JavaScriptValidateStructParams(field): 232 nullable = JavaScriptNullableParam(field) 233 struct_type = JavaScriptType(field.kind) 234 return "%s, %s" % (struct_type, nullable) 235 236def JavaScriptValidateUnionParams(field): 237 nullable = JavaScriptNullableParam(field) 238 union_type = JavaScriptType(field.kind) 239 return "%s, %s" % (union_type, nullable) 240 241def JavaScriptValidateMapParams(field): 242 nullable = JavaScriptNullableParam(field) 243 keys_type = ElementCodecType(field.kind.key_kind) 244 values_kind = field.kind.value_kind; 245 values_type = ElementCodecType(values_kind) 246 values_nullable = "true" if mojom.IsNullableKind(values_kind) else "false" 247 return "%s, %s, %s, %s" % \ 248 (nullable, keys_type, values_type, values_nullable) 249 250 251def JavaScriptValidateStringParams(field): 252 nullable = JavaScriptNullableParam(field) 253 return "%s" % (nullable) 254 255 256def JavaScriptValidateHandleParams(field): 257 nullable = JavaScriptNullableParam(field) 258 return "%s" % (nullable) 259 260def JavaScriptValidateInterfaceParams(field): 261 return JavaScriptValidateHandleParams(field) 262 263def JavaScriptProxyMethodParameterValue(parameter): 264 name = parameter.name; 265 if (mojom.IsInterfaceKind(parameter.kind)): 266 type = JavaScriptType(parameter.kind) 267 return "core.isHandle(%s) ? %s : connection.bindImpl" \ 268 "(%s, %s)" % (name, name, name, type) 269 if (mojom.IsInterfaceRequestKind(parameter.kind)): 270 type = JavaScriptType(parameter.kind.kind) 271 return "core.isHandle(%s) ? %s : connection.bindProxy" \ 272 "(%s, %s)" % (name, name, name, type) 273 return name; 274 275 276def JavaScriptStubMethodParameterValue(parameter): 277 name = parameter.name; 278 if (mojom.IsInterfaceKind(parameter.kind)): 279 type = JavaScriptType(parameter.kind) 280 return "connection.bindHandleToProxy(%s, %s)" % (name, type) 281 if (mojom.IsInterfaceRequestKind(parameter.kind)): 282 type = JavaScriptType(parameter.kind.kind) 283 return "connection.bindHandleToStub(%s, %s)" % (name, type) 284 return name; 285 286 287def TranslateConstants(token): 288 if isinstance(token, (mojom.EnumValue, mojom.NamedValue)): 289 # Both variable and enum constants are constructed like: 290 # NamespaceUid.Struct[.Enum].CONSTANT_NAME 291 name = [] 292 if token.imported_from: 293 name.append(token.imported_from["unique_name"]) 294 if token.parent_kind: 295 name.append(token.parent_kind.name) 296 if isinstance(token, mojom.EnumValue): 297 name.append(token.enum.name) 298 name.append(token.name) 299 return ".".join(name) 300 301 if isinstance(token, mojom.BuiltinValue): 302 if token.value == "double.INFINITY" or token.value == "float.INFINITY": 303 return "Infinity"; 304 if token.value == "double.NEGATIVE_INFINITY" or \ 305 token.value == "float.NEGATIVE_INFINITY": 306 return "-Infinity"; 307 if token.value == "double.NAN" or token.value == "float.NAN": 308 return "NaN"; 309 310 return token 311 312 313def ExpressionToText(value): 314 return TranslateConstants(value) 315 316def IsArrayPointerField(field): 317 return mojom.IsArrayKind(field.kind) 318 319def IsStringPointerField(field): 320 return mojom.IsStringKind(field.kind) 321 322def IsStructPointerField(field): 323 return mojom.IsStructKind(field.kind) 324 325def IsMapPointerField(field): 326 return mojom.IsMapKind(field.kind) 327 328def IsHandleField(field): 329 return mojom.IsAnyHandleKind(field.kind) 330 331def IsInterfaceField(field): 332 return mojom.IsInterfaceKind(field.kind) 333 334def IsUnionField(field): 335 return mojom.IsUnionKind(field.kind) 336 337 338class Generator(generator.Generator): 339 340 js_filters = { 341 "default_value": JavaScriptDefaultValue, 342 "payload_size": JavaScriptPayloadSize, 343 "decode_snippet": JavaScriptDecodeSnippet, 344 "encode_snippet": JavaScriptEncodeSnippet, 345 "union_decode_snippet": JavaScriptUnionDecodeSnippet, 346 "union_encode_snippet": JavaScriptUnionEncodeSnippet, 347 "expression_to_text": ExpressionToText, 348 "field_offset": JavaScriptFieldOffset, 349 "has_callbacks": mojom.HasCallbacks, 350 "is_array_pointer_field": IsArrayPointerField, 351 "is_map_pointer_field": IsMapPointerField, 352 "is_struct_pointer_field": IsStructPointerField, 353 "is_string_pointer_field": IsStringPointerField, 354 "is_union_field": IsUnionField, 355 "is_handle_field": IsHandleField, 356 "is_interface_field": IsInterfaceField, 357 "js_type": JavaScriptType, 358 "js_proxy_method_parameter_value": JavaScriptProxyMethodParameterValue, 359 "js_stub_method_parameter_value": JavaScriptStubMethodParameterValue, 360 "stylize_method": generator.StudlyCapsToCamel, 361 "validate_array_params": JavaScriptValidateArrayParams, 362 "validate_handle_params": JavaScriptValidateHandleParams, 363 "validate_interface_params": JavaScriptValidateInterfaceParams, 364 "validate_map_params": JavaScriptValidateMapParams, 365 "validate_string_params": JavaScriptValidateStringParams, 366 "validate_struct_params": JavaScriptValidateStructParams, 367 "validate_union_params": JavaScriptValidateUnionParams, 368 } 369 370 def GetParameters(self): 371 return { 372 "namespace": self.module.namespace, 373 "imports": self.GetImports(), 374 "kinds": self.module.kinds, 375 "enums": self.module.enums, 376 "module": self.module, 377 "structs": self.GetStructs() + self.GetStructsFromMethods(), 378 "unions": self.GetUnions(), 379 "interfaces": self.GetInterfaces(), 380 "imported_interfaces": self.GetImportedInterfaces(), 381 } 382 383 @staticmethod 384 def GetTemplatePrefix(): 385 return "js_templates" 386 387 @classmethod 388 def GetFilters(cls): 389 return cls.js_filters 390 391 @UseJinja("module.amd.tmpl") 392 def GenerateAMDModule(self): 393 return self.GetParameters() 394 395 def GenerateFiles(self, args): 396 if self.variant: 397 raise Exception("Variants not supported in JavaScript bindings.") 398 399 self.Write(self.GenerateAMDModule(), 400 self.MatchMojomFilePath("%s.js" % self.module.name)) 401 402 def GetImports(self): 403 used_names = set() 404 for each_import in self.module.imports: 405 simple_name = each_import["module_name"].split(".")[0] 406 407 # Since each import is assigned a variable in JS, they need to have unique 408 # names. 409 unique_name = simple_name 410 counter = 0 411 while unique_name in used_names: 412 counter += 1 413 unique_name = simple_name + str(counter) 414 415 used_names.add(unique_name) 416 each_import["unique_name"] = unique_name + "$" 417 counter += 1 418 return self.module.imports 419 420 def GetImportedInterfaces(self): 421 interface_to_import = {}; 422 for each_import in self.module.imports: 423 for each_interface in each_import["module"].interfaces: 424 name = each_interface.name 425 interface_to_import[name] = each_import["unique_name"] + "." + name 426 return interface_to_import; 427 428