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.IsAnyArrayKind(field.kind): 56 return "null" 57 if mojom.IsInterfaceKind(field.kind) or \ 58 mojom.IsInterfaceRequestKind(field.kind): 59 return _kind_to_javascript_default_value[mojom.MSGPIPE] 60 if mojom.IsEnumKind(field.kind): 61 return "0" 62 63 64def JavaScriptPayloadSize(packed): 65 packed_fields = packed.packed_fields 66 if not packed_fields: 67 return 0 68 last_field = packed_fields[-1] 69 offset = last_field.offset + last_field.size 70 pad = pack.GetPad(offset, 8) 71 return offset + pad 72 73 74_kind_to_codec_type = { 75 mojom.BOOL: "codec.Uint8", 76 mojom.INT8: "codec.Int8", 77 mojom.UINT8: "codec.Uint8", 78 mojom.INT16: "codec.Int16", 79 mojom.UINT16: "codec.Uint16", 80 mojom.INT32: "codec.Int32", 81 mojom.UINT32: "codec.Uint32", 82 mojom.FLOAT: "codec.Float", 83 mojom.HANDLE: "codec.Handle", 84 mojom.DCPIPE: "codec.Handle", 85 mojom.DPPIPE: "codec.Handle", 86 mojom.MSGPIPE: "codec.Handle", 87 mojom.SHAREDBUFFER: "codec.Handle", 88 mojom.NULLABLE_HANDLE: "codec.NullableHandle", 89 mojom.NULLABLE_DCPIPE: "codec.NullableHandle", 90 mojom.NULLABLE_DPPIPE: "codec.NullableHandle", 91 mojom.NULLABLE_MSGPIPE: "codec.NullableHandle", 92 mojom.NULLABLE_SHAREDBUFFER: "codec.NullableHandle", 93 mojom.INT64: "codec.Int64", 94 mojom.UINT64: "codec.Uint64", 95 mojom.DOUBLE: "codec.Double", 96 mojom.STRING: "codec.String", 97 mojom.NULLABLE_STRING: "codec.NullableString", 98} 99 100 101def CodecType(kind): 102 if kind in mojom.PRIMITIVES: 103 return _kind_to_codec_type[kind] 104 if mojom.IsStructKind(kind): 105 pointer_type = "NullablePointerTo" if mojom.IsNullableKind(kind) \ 106 else "PointerTo" 107 return "new codec.%s(%s)" % (pointer_type, JavaScriptType(kind)) 108 if mojom.IsAnyArrayKind(kind): 109 array_type = "NullableArrayOf" if mojom.IsNullableKind(kind) else "ArrayOf" 110 element_type = "codec.PackedBool" if mojom.IsBoolKind(kind.kind) \ 111 else CodecType(kind.kind) 112 return "new codec.%s(%s)" % (array_type, element_type) 113 if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind): 114 return CodecType(mojom.MSGPIPE) 115 if mojom.IsEnumKind(kind): 116 return _kind_to_codec_type[mojom.INT32] 117 return kind 118 119 120def JavaScriptDecodeSnippet(kind): 121 if kind in mojom.PRIMITIVES: 122 return "decodeStruct(%s)" % CodecType(kind) 123 if mojom.IsStructKind(kind): 124 return "decodeStructPointer(%s)" % JavaScriptType(kind) 125 if mojom.IsAnyArrayKind(kind) and mojom.IsBoolKind(kind.kind): 126 return "decodeArrayPointer(codec.PackedBool)" 127 if mojom.IsAnyArrayKind(kind): 128 return "decodeArrayPointer(%s)" % CodecType(kind.kind) 129 if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind): 130 return JavaScriptDecodeSnippet(mojom.MSGPIPE) 131 if mojom.IsEnumKind(kind): 132 return JavaScriptDecodeSnippet(mojom.INT32) 133 134 135def JavaScriptEncodeSnippet(kind): 136 if kind in mojom.PRIMITIVES: 137 return "encodeStruct(%s, " % CodecType(kind) 138 if mojom.IsStructKind(kind): 139 return "encodeStructPointer(%s, " % JavaScriptType(kind) 140 if mojom.IsAnyArrayKind(kind) and mojom.IsBoolKind(kind.kind): 141 return "encodeArrayPointer(codec.PackedBool, "; 142 if mojom.IsAnyArrayKind(kind): 143 return "encodeArrayPointer(%s, " % CodecType(kind.kind) 144 if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind): 145 return JavaScriptEncodeSnippet(mojom.MSGPIPE) 146 if mojom.IsEnumKind(kind): 147 return JavaScriptEncodeSnippet(mojom.INT32) 148 149 150def JavaScriptFieldOffset(packed_field): 151 return "offset + codec.kStructHeaderSize + %s" % packed_field.offset 152 153 154def JavaScriptNullableParam(packed_field): 155 return "true" if mojom.IsNullableKind(packed_field.field.kind) else "false" 156 157 158def JavaScriptValidateArrayParams(packed_field): 159 nullable = JavaScriptNullableParam(packed_field) 160 field_offset = JavaScriptFieldOffset(packed_field) 161 element_kind = packed_field.field.kind.kind 162 element_size = pack.PackedField.GetSizeForKind(element_kind) 163 element_count = generator.ExpectedArraySize(packed_field.field.kind) 164 element_type = "codec.PackedBool" if mojom.IsBoolKind(element_kind) \ 165 else CodecType(element_kind) 166 return "%s, %s, %s, %s, %s" % \ 167 (field_offset, element_size, element_count, element_type, nullable) 168 169 170def JavaScriptValidateStructParams(packed_field): 171 nullable = JavaScriptNullableParam(packed_field) 172 field_offset = JavaScriptFieldOffset(packed_field) 173 struct_type = JavaScriptType(packed_field.field.kind) 174 return "%s, %s, %s" % (field_offset, struct_type, nullable) 175 176 177def JavaScriptValidateStringParams(packed_field): 178 nullable = JavaScriptNullableParam(packed_field) 179 return "%s, %s" % (JavaScriptFieldOffset(packed_field), nullable) 180 181 182def JavaScriptValidateHandleParams(packed_field): 183 nullable = JavaScriptNullableParam(packed_field) 184 field_offset = JavaScriptFieldOffset(packed_field) 185 return "%s, %s" % (field_offset, nullable) 186 187 188def TranslateConstants(token): 189 if isinstance(token, (mojom.EnumValue, mojom.NamedValue)): 190 # Both variable and enum constants are constructed like: 191 # NamespaceUid.Struct[.Enum].CONSTANT_NAME 192 name = [] 193 if token.imported_from: 194 name.append(token.imported_from["unique_name"]) 195 if token.parent_kind: 196 name.append(token.parent_kind.name) 197 if isinstance(token, mojom.EnumValue): 198 name.append(token.enum.name) 199 name.append(token.name) 200 return ".".join(name) 201 202 if isinstance(token, mojom.BuiltinValue): 203 if token.value == "double.INFINITY" or token.value == "float.INFINITY": 204 return "Infinity"; 205 if token.value == "double.NEGATIVE_INFINITY" or \ 206 token.value == "float.NEGATIVE_INFINITY": 207 return "-Infinity"; 208 if token.value == "double.NAN" or token.value == "float.NAN": 209 return "NaN"; 210 211 return token 212 213 214def ExpressionToText(value): 215 return TranslateConstants(value) 216 217 218def IsArrayPointerField(field): 219 return mojom.IsAnyArrayKind(field.kind) 220 221def IsStringPointerField(field): 222 return mojom.IsStringKind(field.kind) 223 224def IsStructPointerField(field): 225 return mojom.IsStructKind(field.kind) 226 227def IsHandleField(field): 228 return mojom.IsAnyHandleKind(field.kind) 229 230 231class Generator(generator.Generator): 232 233 js_filters = { 234 "default_value": JavaScriptDefaultValue, 235 "payload_size": JavaScriptPayloadSize, 236 "decode_snippet": JavaScriptDecodeSnippet, 237 "encode_snippet": JavaScriptEncodeSnippet, 238 "expression_to_text": ExpressionToText, 239 "field_offset": JavaScriptFieldOffset, 240 "has_callbacks": mojom.HasCallbacks, 241 "is_array_pointer_field": IsArrayPointerField, 242 "is_struct_pointer_field": IsStructPointerField, 243 "is_string_pointer_field": IsStringPointerField, 244 "is_handle_field": IsHandleField, 245 "js_type": JavaScriptType, 246 "stylize_method": generator.StudlyCapsToCamel, 247 "validate_array_params": JavaScriptValidateArrayParams, 248 "validate_handle_params": JavaScriptValidateHandleParams, 249 "validate_string_params": JavaScriptValidateStringParams, 250 "validate_struct_params": JavaScriptValidateStructParams, 251 } 252 253 @UseJinja("js_templates/module.js.tmpl", filters=js_filters) 254 def GenerateJsModule(self): 255 return { 256 "namespace": self.module.namespace, 257 "imports": self.GetImports(), 258 "kinds": self.module.kinds, 259 "enums": self.module.enums, 260 "module": self.module, 261 "structs": self.GetStructs() + self.GetStructsFromMethods(), 262 "interfaces": self.module.interfaces, 263 } 264 265 def GenerateFiles(self, args): 266 self.Write(self.GenerateJsModule(), "%s.js" % self.module.name) 267 268 def GetImports(self): 269 # Since each import is assigned a variable in JS, they need to have unique 270 # names. 271 counter = 1 272 for each in self.module.imports: 273 each["unique_name"] = "import" + str(counter) 274 counter += 1 275 return self.module.imports 276