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.
5 """Generates JavaScript source files from a mojom.Module."""
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
12 _kind_to_javascript_default_value
= {
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",
35 mojom
.NULLABLE_STRING
: "null"
39 def JavaScriptType(kind
):
40 if kind
.imported_from
:
41 return kind
.imported_from
["unique_name"] + "." + kind
.name
45 def JavaScriptDefaultValue(field
):
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
):
55 if mojom
.IsArrayKind(field
.kind
):
57 if mojom
.IsMapKind(field
.kind
):
59 if mojom
.IsInterfaceKind(field
.kind
) or \
60 mojom
.IsInterfaceRequestKind(field
.kind
):
61 return _kind_to_javascript_default_value
[mojom
.MSGPIPE
]
62 if mojom
.IsEnumKind(field
.kind
):
66 def JavaScriptPayloadSize(packed
):
67 packed_fields
= packed
.packed_fields
70 last_field
= packed_fields
[-1]
71 offset
= last_field
.offset
+ last_field
.size
72 pad
= pack
.GetPad(offset
, 8)
76 _kind_to_codec_type
= {
77 mojom
.BOOL
: "codec.Uint8",
78 mojom
.INT8
: "codec.Int8",
79 mojom
.UINT8
: "codec.Uint8",
80 mojom
.INT16
: "codec.Int16",
81 mojom
.UINT16
: "codec.Uint16",
82 mojom
.INT32
: "codec.Int32",
83 mojom
.UINT32
: "codec.Uint32",
84 mojom
.FLOAT
: "codec.Float",
85 mojom
.HANDLE
: "codec.Handle",
86 mojom
.DCPIPE
: "codec.Handle",
87 mojom
.DPPIPE
: "codec.Handle",
88 mojom
.MSGPIPE
: "codec.Handle",
89 mojom
.SHAREDBUFFER
: "codec.Handle",
90 mojom
.NULLABLE_HANDLE
: "codec.NullableHandle",
91 mojom
.NULLABLE_DCPIPE
: "codec.NullableHandle",
92 mojom
.NULLABLE_DPPIPE
: "codec.NullableHandle",
93 mojom
.NULLABLE_MSGPIPE
: "codec.NullableHandle",
94 mojom
.NULLABLE_SHAREDBUFFER
: "codec.NullableHandle",
95 mojom
.INT64
: "codec.Int64",
96 mojom
.UINT64
: "codec.Uint64",
97 mojom
.DOUBLE
: "codec.Double",
98 mojom
.STRING
: "codec.String",
99 mojom
.NULLABLE_STRING
: "codec.NullableString",
104 if kind
in mojom
.PRIMITIVES
:
105 return _kind_to_codec_type
[kind
]
106 if mojom
.IsStructKind(kind
):
107 pointer_type
= "NullablePointerTo" if mojom
.IsNullableKind(kind
) \
109 return "new codec.%s(%s)" % (pointer_type
, JavaScriptType(kind
))
110 if mojom
.IsArrayKind(kind
):
111 array_type
= "NullableArrayOf" if mojom
.IsNullableKind(kind
) else "ArrayOf"
112 array_length
= "" if kind
.length
is None else ", %d" % kind
.length
113 element_type
= "codec.PackedBool" if mojom
.IsBoolKind(kind
.kind
) \
114 else CodecType(kind
.kind
)
115 return "new codec.%s(%s%s)" % (array_type
, element_type
, array_length
)
116 if mojom
.IsInterfaceKind(kind
) or mojom
.IsInterfaceRequestKind(kind
):
117 return CodecType(mojom
.MSGPIPE
)
118 if mojom
.IsEnumKind(kind
):
119 return _kind_to_codec_type
[mojom
.INT32
]
122 def MapCodecType(kind
):
123 return "codec.PackedBool" if mojom
.IsBoolKind(kind
) else CodecType(kind
)
125 def JavaScriptDecodeSnippet(kind
):
126 if kind
in mojom
.PRIMITIVES
:
127 return "decodeStruct(%s)" % CodecType(kind
)
128 if mojom
.IsStructKind(kind
):
129 return "decodeStructPointer(%s)" % JavaScriptType(kind
)
130 if mojom
.IsMapKind(kind
):
131 return "decodeMapPointer(%s, %s)" % \
132 (MapCodecType(kind
.key_kind
), MapCodecType(kind
.value_kind
))
133 if mojom
.IsArrayKind(kind
) and mojom
.IsBoolKind(kind
.kind
):
134 return "decodeArrayPointer(codec.PackedBool)"
135 if mojom
.IsArrayKind(kind
):
136 return "decodeArrayPointer(%s)" % CodecType(kind
.kind
)
137 if mojom
.IsInterfaceKind(kind
) or mojom
.IsInterfaceRequestKind(kind
):
138 return JavaScriptDecodeSnippet(mojom
.MSGPIPE
)
139 if mojom
.IsEnumKind(kind
):
140 return JavaScriptDecodeSnippet(mojom
.INT32
)
143 def JavaScriptEncodeSnippet(kind
):
144 if kind
in mojom
.PRIMITIVES
:
145 return "encodeStruct(%s, " % CodecType(kind
)
146 if mojom
.IsStructKind(kind
):
147 return "encodeStructPointer(%s, " % JavaScriptType(kind
)
148 if mojom
.IsMapKind(kind
):
149 return "encodeMapPointer(%s, %s, " % \
150 (MapCodecType(kind
.key_kind
), MapCodecType(kind
.value_kind
))
151 if mojom
.IsArrayKind(kind
) and mojom
.IsBoolKind(kind
.kind
):
152 return "encodeArrayPointer(codec.PackedBool, ";
153 if mojom
.IsArrayKind(kind
):
154 return "encodeArrayPointer(%s, " % CodecType(kind
.kind
)
155 if mojom
.IsInterfaceKind(kind
) or mojom
.IsInterfaceRequestKind(kind
):
156 return JavaScriptEncodeSnippet(mojom
.MSGPIPE
)
157 if mojom
.IsEnumKind(kind
):
158 return JavaScriptEncodeSnippet(mojom
.INT32
)
161 def JavaScriptFieldOffset(packed_field
):
162 return "offset + codec.kStructHeaderSize + %s" % packed_field
.offset
165 def JavaScriptNullableParam(packed_field
):
166 return "true" if mojom
.IsNullableKind(packed_field
.field
.kind
) else "false"
169 def GetArrayExpectedDimensionSizes(kind
):
170 expected_dimension_sizes
= []
171 while mojom
.IsArrayKind(kind
):
172 expected_dimension_sizes
.append(generator
.ExpectedArraySize(kind
) or 0)
174 # Strings are serialized as variable-length arrays.
175 if (mojom
.IsStringKind(kind
)):
176 expected_dimension_sizes
.append(0)
177 return expected_dimension_sizes
180 def JavaScriptValidateArrayParams(packed_field
):
181 nullable
= JavaScriptNullableParam(packed_field
)
182 field_offset
= JavaScriptFieldOffset(packed_field
)
183 element_kind
= packed_field
.field
.kind
.kind
184 element_size
= pack
.PackedField
.GetSizeForKind(element_kind
)
185 expected_dimension_sizes
= GetArrayExpectedDimensionSizes(
186 packed_field
.field
.kind
)
187 element_type
= "codec.PackedBool" if mojom
.IsBoolKind(element_kind
) \
188 else CodecType(element_kind
)
189 return "%s, %s, %s, %s, %s, 0" % \
190 (field_offset
, element_size
, element_type
, nullable
,
191 expected_dimension_sizes
)
194 def JavaScriptValidateStructParams(packed_field
):
195 nullable
= JavaScriptNullableParam(packed_field
)
196 field_offset
= JavaScriptFieldOffset(packed_field
)
197 struct_type
= JavaScriptType(packed_field
.field
.kind
)
198 return "%s, %s, %s" % (field_offset
, struct_type
, nullable
)
201 def JavaScriptValidateMapParams(packed_field
):
202 nullable
= JavaScriptNullableParam(packed_field
)
203 field_offset
= JavaScriptFieldOffset(packed_field
)
204 keys_type
= MapCodecType(packed_field
.field
.kind
.key_kind
)
205 values_kind
= packed_field
.field
.kind
.value_kind
;
206 values_type
= MapCodecType(values_kind
)
207 values_nullable
= "true" if mojom
.IsNullableKind(values_kind
) else "false"
208 return "%s, %s, %s, %s, %s" % \
209 (field_offset
, nullable
, keys_type
, values_type
, values_nullable
)
212 def JavaScriptValidateStringParams(packed_field
):
213 nullable
= JavaScriptNullableParam(packed_field
)
214 return "%s, %s" % (JavaScriptFieldOffset(packed_field
), nullable
)
217 def JavaScriptValidateHandleParams(packed_field
):
218 nullable
= JavaScriptNullableParam(packed_field
)
219 field_offset
= JavaScriptFieldOffset(packed_field
)
220 return "%s, %s" % (field_offset
, nullable
)
223 def TranslateConstants(token
):
224 if isinstance(token
, (mojom
.EnumValue
, mojom
.NamedValue
)):
225 # Both variable and enum constants are constructed like:
226 # NamespaceUid.Struct[.Enum].CONSTANT_NAME
228 if token
.imported_from
:
229 name
.append(token
.imported_from
["unique_name"])
230 if token
.parent_kind
:
231 name
.append(token
.parent_kind
.name
)
232 if isinstance(token
, mojom
.EnumValue
):
233 name
.append(token
.enum
.name
)
234 name
.append(token
.name
)
235 return ".".join(name
)
237 if isinstance(token
, mojom
.BuiltinValue
):
238 if token
.value
== "double.INFINITY" or token
.value
== "float.INFINITY":
240 if token
.value
== "double.NEGATIVE_INFINITY" or \
241 token
.value
== "float.NEGATIVE_INFINITY":
243 if token
.value
== "double.NAN" or token
.value
== "float.NAN":
249 def ExpressionToText(value
):
250 return TranslateConstants(value
)
253 def IsArrayPointerField(field
):
254 return mojom
.IsArrayKind(field
.kind
)
256 def IsStringPointerField(field
):
257 return mojom
.IsStringKind(field
.kind
)
259 def IsStructPointerField(field
):
260 return mojom
.IsStructKind(field
.kind
)
262 def IsMapPointerField(field
):
263 return mojom
.IsMapKind(field
.kind
)
265 def IsHandleField(field
):
266 return mojom
.IsAnyHandleKind(field
.kind
)
269 class Generator(generator
.Generator
):
272 "default_value": JavaScriptDefaultValue
,
273 "payload_size": JavaScriptPayloadSize
,
274 "decode_snippet": JavaScriptDecodeSnippet
,
275 "encode_snippet": JavaScriptEncodeSnippet
,
276 "expression_to_text": ExpressionToText
,
277 "field_offset": JavaScriptFieldOffset
,
278 "has_callbacks": mojom
.HasCallbacks
,
279 "is_array_pointer_field": IsArrayPointerField
,
280 "is_map_pointer_field": IsMapPointerField
,
281 "is_struct_pointer_field": IsStructPointerField
,
282 "is_string_pointer_field": IsStringPointerField
,
283 "is_handle_field": IsHandleField
,
284 "js_type": JavaScriptType
,
285 "stylize_method": generator
.StudlyCapsToCamel
,
286 "validate_array_params": JavaScriptValidateArrayParams
,
287 "validate_handle_params": JavaScriptValidateHandleParams
,
288 "validate_map_params": JavaScriptValidateMapParams
,
289 "validate_string_params": JavaScriptValidateStringParams
,
290 "validate_struct_params": JavaScriptValidateStructParams
,
293 def GetParameters(self
):
295 "namespace": self
.module
.namespace
,
296 "imports": self
.GetImports(),
297 "kinds": self
.module
.kinds
,
298 "enums": self
.module
.enums
,
299 "module": self
.module
,
300 "structs": self
.GetStructs() + self
.GetStructsFromMethods(),
301 "interfaces": self
.module
.interfaces
,
302 "imported_interfaces": self
.GetImportedInterfaces(),
305 @UseJinja("js_templates/module.amd.tmpl", filters
=js_filters
)
306 def GenerateAMDModule(self
):
307 return self
.GetParameters()
309 @UseJinja("js_templates/module.sky.tmpl", filters
=js_filters
)
310 def GenerateHTMLModule(self
):
311 return self
.GetParameters()
313 def GenerateFiles(self
, args
):
314 self
.Write(self
.GenerateAMDModule(),
315 self
.MatchMojomFilePath("%s.js" % self
.module
.name
))
316 self
.Write(self
.GenerateHTMLModule(),
317 self
.MatchMojomFilePath("%s.sky" % self
.module
.name
))
319 def GetImports(self
):
321 for each_import
in self
.module
.imports
:
322 simple_name
= each_import
["module_name"].split(".")[0]
324 # Since each import is assigned a variable in JS, they need to have unique
326 unique_name
= simple_name
328 while unique_name
in used_names
:
330 unique_name
= simple_name
+ str(counter
)
332 used_names
.add(unique_name
)
333 each_import
["unique_name"] = unique_name
335 return self
.module
.imports
337 def GetImportedInterfaces(self
):
338 interface_to_import
= {};
339 for each_import
in self
.module
.imports
:
340 for each_interface
in each_import
["module"].interfaces
:
341 name
= each_interface
.name
342 interface_to_import
[name
] = each_import
["unique_name"] + "." + name
343 return interface_to_import
;