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