OLD | NEW |
| (Empty) |
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 if kind.imported_from: | |
41 return kind.imported_from["unique_name"] + "." + kind.name | |
42 return kind.name | |
43 | |
44 | |
45 def 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.IsArrayKind(field.kind): | |
56 return "null" | |
57 if mojom.IsMapKind(field.kind): | |
58 return "null" | |
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): | |
63 return "0" | |
64 | |
65 | |
66 def JavaScriptPayloadSize(packed): | |
67 packed_fields = packed.packed_fields | |
68 if not packed_fields: | |
69 return 0 | |
70 last_field = packed_fields[-1] | |
71 offset = last_field.offset + last_field.size | |
72 pad = pack.GetPad(offset, 8) | |
73 return offset + pad | |
74 | |
75 | |
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", | |
100 } | |
101 | |
102 | |
103 def CodecType(kind): | |
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) \ | |
108 else "PointerTo" | |
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 = ElementCodecType(kind.kind) | |
114 return "new codec.%s(%s%s)" % (array_type, element_type, array_length) | |
115 if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind): | |
116 return CodecType(mojom.MSGPIPE) | |
117 if mojom.IsEnumKind(kind): | |
118 return _kind_to_codec_type[mojom.INT32] | |
119 if mojom.IsMapKind(kind): | |
120 map_type = "NullableMapOf" if mojom.IsNullableKind(kind) else "MapOf" | |
121 key_type = ElementCodecType(kind.key_kind) | |
122 value_type = ElementCodecType(kind.value_kind) | |
123 return "new codec.%s(%s, %s)" % (map_type, key_type, value_type) | |
124 return kind | |
125 | |
126 def ElementCodecType(kind): | |
127 return "codec.PackedBool" if mojom.IsBoolKind(kind) else CodecType(kind) | |
128 | |
129 def JavaScriptDecodeSnippet(kind): | |
130 if kind in mojom.PRIMITIVES: | |
131 return "decodeStruct(%s)" % CodecType(kind) | |
132 if mojom.IsStructKind(kind): | |
133 return "decodeStructPointer(%s)" % JavaScriptType(kind) | |
134 if mojom.IsMapKind(kind): | |
135 return "decodeMapPointer(%s, %s)" % \ | |
136 (ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind)) | |
137 if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind): | |
138 return "decodeArrayPointer(codec.PackedBool)" | |
139 if mojom.IsArrayKind(kind): | |
140 return "decodeArrayPointer(%s)" % CodecType(kind.kind) | |
141 if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind): | |
142 return JavaScriptDecodeSnippet(mojom.MSGPIPE) | |
143 if mojom.IsEnumKind(kind): | |
144 return JavaScriptDecodeSnippet(mojom.INT32) | |
145 | |
146 | |
147 def JavaScriptEncodeSnippet(kind): | |
148 if kind in mojom.PRIMITIVES: | |
149 return "encodeStruct(%s, " % CodecType(kind) | |
150 if mojom.IsStructKind(kind): | |
151 return "encodeStructPointer(%s, " % JavaScriptType(kind) | |
152 if mojom.IsMapKind(kind): | |
153 return "encodeMapPointer(%s, %s, " % \ | |
154 (ElementCodecType(kind.key_kind), ElementCodecType(kind.value_kind)) | |
155 if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind): | |
156 return "encodeArrayPointer(codec.PackedBool, "; | |
157 if mojom.IsArrayKind(kind): | |
158 return "encodeArrayPointer(%s, " % CodecType(kind.kind) | |
159 if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind): | |
160 return JavaScriptEncodeSnippet(mojom.MSGPIPE) | |
161 if mojom.IsEnumKind(kind): | |
162 return JavaScriptEncodeSnippet(mojom.INT32) | |
163 | |
164 | |
165 def JavaScriptFieldOffset(packed_field): | |
166 return "offset + codec.kStructHeaderSize + %s" % packed_field.offset | |
167 | |
168 | |
169 def JavaScriptNullableParam(packed_field): | |
170 return "true" if mojom.IsNullableKind(packed_field.field.kind) else "false" | |
171 | |
172 | |
173 def GetArrayExpectedDimensionSizes(kind): | |
174 expected_dimension_sizes = [] | |
175 while mojom.IsArrayKind(kind): | |
176 expected_dimension_sizes.append(generator.ExpectedArraySize(kind) or 0) | |
177 kind = kind.kind | |
178 # Strings are serialized as variable-length arrays. | |
179 if (mojom.IsStringKind(kind)): | |
180 expected_dimension_sizes.append(0) | |
181 return expected_dimension_sizes | |
182 | |
183 | |
184 def JavaScriptValidateArrayParams(packed_field): | |
185 nullable = JavaScriptNullableParam(packed_field) | |
186 field_offset = JavaScriptFieldOffset(packed_field) | |
187 element_kind = packed_field.field.kind.kind | |
188 element_size = pack.PackedField.GetSizeForKind(element_kind) | |
189 expected_dimension_sizes = GetArrayExpectedDimensionSizes( | |
190 packed_field.field.kind) | |
191 element_type = ElementCodecType(element_kind) | |
192 return "%s, %s, %s, %s, %s, 0" % \ | |
193 (field_offset, element_size, element_type, nullable, | |
194 expected_dimension_sizes) | |
195 | |
196 | |
197 def JavaScriptValidateStructParams(packed_field): | |
198 nullable = JavaScriptNullableParam(packed_field) | |
199 field_offset = JavaScriptFieldOffset(packed_field) | |
200 struct_type = JavaScriptType(packed_field.field.kind) | |
201 return "%s, %s, %s" % (field_offset, struct_type, nullable) | |
202 | |
203 | |
204 def JavaScriptValidateMapParams(packed_field): | |
205 nullable = JavaScriptNullableParam(packed_field) | |
206 field_offset = JavaScriptFieldOffset(packed_field) | |
207 keys_type = ElementCodecType(packed_field.field.kind.key_kind) | |
208 values_kind = packed_field.field.kind.value_kind; | |
209 values_type = ElementCodecType(values_kind) | |
210 values_nullable = "true" if mojom.IsNullableKind(values_kind) else "false" | |
211 return "%s, %s, %s, %s, %s" % \ | |
212 (field_offset, nullable, keys_type, values_type, values_nullable) | |
213 | |
214 | |
215 def JavaScriptValidateStringParams(packed_field): | |
216 nullable = JavaScriptNullableParam(packed_field) | |
217 return "%s, %s" % (JavaScriptFieldOffset(packed_field), nullable) | |
218 | |
219 | |
220 def JavaScriptValidateHandleParams(packed_field): | |
221 nullable = JavaScriptNullableParam(packed_field) | |
222 field_offset = JavaScriptFieldOffset(packed_field) | |
223 return "%s, %s" % (field_offset, nullable) | |
224 | |
225 | |
226 | |
227 | |
228 def JavaScriptProxyMethodParameterValue(parameter): | |
229 name = parameter.name; | |
230 if (IsInterfaceParameter(parameter)): | |
231 type = JavaScriptType(parameter.kind) | |
232 return "core.isHandle(%s) ? %s : connection.bindProxyClient" \ | |
233 "(%s, %s, %s.client)" % (name, name, name, type, type) | |
234 if (IsInterfaceRequestParameter(parameter)): | |
235 type = JavaScriptType(parameter.kind.kind) | |
236 return "core.isHandle(%s) ? %s : connection.bindProxyClient" \ | |
237 "(%s, %s.client, %s)" % (name, name, name, type, type) | |
238 return name; | |
239 | |
240 def JavaScriptStubMethodParameterValue(parameter): | |
241 name = parameter.name; | |
242 if (IsInterfaceParameter(parameter)): | |
243 type = JavaScriptType(parameter.kind) | |
244 return "connection.bindProxyHandle(%s, %s.client, %s)" % (name, type, type) | |
245 if (IsInterfaceRequestParameter(parameter)): | |
246 type = JavaScriptType(parameter.kind.kind) | |
247 return "connection.bindProxyHandle(%s, %s, %s.client)" % (name, type, type) | |
248 return name; | |
249 | |
250 def TranslateConstants(token): | |
251 if isinstance(token, (mojom.EnumValue, mojom.NamedValue)): | |
252 # Both variable and enum constants are constructed like: | |
253 # NamespaceUid.Struct[.Enum].CONSTANT_NAME | |
254 name = [] | |
255 if token.imported_from: | |
256 name.append(token.imported_from["unique_name"]) | |
257 if token.parent_kind: | |
258 name.append(token.parent_kind.name) | |
259 if isinstance(token, mojom.EnumValue): | |
260 name.append(token.enum.name) | |
261 name.append(token.name) | |
262 return ".".join(name) | |
263 | |
264 if isinstance(token, mojom.BuiltinValue): | |
265 if token.value == "double.INFINITY" or token.value == "float.INFINITY": | |
266 return "Infinity"; | |
267 if token.value == "double.NEGATIVE_INFINITY" or \ | |
268 token.value == "float.NEGATIVE_INFINITY": | |
269 return "-Infinity"; | |
270 if token.value == "double.NAN" or token.value == "float.NAN": | |
271 return "NaN"; | |
272 | |
273 return token | |
274 | |
275 | |
276 def ExpressionToText(value): | |
277 return TranslateConstants(value) | |
278 | |
279 def IsArrayPointerField(field): | |
280 return mojom.IsArrayKind(field.kind) | |
281 | |
282 def IsStringPointerField(field): | |
283 return mojom.IsStringKind(field.kind) | |
284 | |
285 def IsStructPointerField(field): | |
286 return mojom.IsStructKind(field.kind) | |
287 | |
288 def IsMapPointerField(field): | |
289 return mojom.IsMapKind(field.kind) | |
290 | |
291 def IsHandleField(field): | |
292 return mojom.IsAnyHandleKind(field.kind) | |
293 | |
294 def IsInterfaceRequestParameter(parameter): | |
295 return mojom.IsInterfaceRequestKind(parameter.kind) | |
296 | |
297 def IsInterfaceParameter(parameter): | |
298 return mojom.IsInterfaceKind(parameter.kind) | |
299 | |
300 | |
301 class Generator(generator.Generator): | |
302 | |
303 js_filters = { | |
304 "default_value": JavaScriptDefaultValue, | |
305 "payload_size": JavaScriptPayloadSize, | |
306 "decode_snippet": JavaScriptDecodeSnippet, | |
307 "encode_snippet": JavaScriptEncodeSnippet, | |
308 "expression_to_text": ExpressionToText, | |
309 "field_offset": JavaScriptFieldOffset, | |
310 "has_callbacks": mojom.HasCallbacks, | |
311 "is_array_pointer_field": IsArrayPointerField, | |
312 "is_map_pointer_field": IsMapPointerField, | |
313 "is_struct_pointer_field": IsStructPointerField, | |
314 "is_string_pointer_field": IsStringPointerField, | |
315 "is_handle_field": IsHandleField, | |
316 "js_type": JavaScriptType, | |
317 "is_interface_request_parameter": IsInterfaceRequestParameter, | |
318 "is_interface_parameter": IsInterfaceParameter, | |
319 "js_proxy_method_parameter_value": JavaScriptProxyMethodParameterValue, | |
320 "js_stub_method_parameter_value": JavaScriptStubMethodParameterValue, | |
321 "stylize_method": generator.StudlyCapsToCamel, | |
322 "validate_array_params": JavaScriptValidateArrayParams, | |
323 "validate_handle_params": JavaScriptValidateHandleParams, | |
324 "validate_map_params": JavaScriptValidateMapParams, | |
325 "validate_string_params": JavaScriptValidateStringParams, | |
326 "validate_struct_params": JavaScriptValidateStructParams, | |
327 } | |
328 | |
329 def GetParameters(self): | |
330 return { | |
331 "namespace": self.module.namespace, | |
332 "imports": self.GetImports(), | |
333 "kinds": self.module.kinds, | |
334 "enums": self.module.enums, | |
335 "module": self.module, | |
336 "structs": self.GetStructs() + self.GetStructsFromMethods(), | |
337 "interfaces": self.module.interfaces, | |
338 "imported_interfaces": self.GetImportedInterfaces(), | |
339 } | |
340 | |
341 @UseJinja("js_templates/module.amd.tmpl", filters=js_filters) | |
342 def GenerateAMDModule(self): | |
343 return self.GetParameters() | |
344 | |
345 @UseJinja("js_templates/module.sky.tmpl", filters=js_filters) | |
346 def GenerateHTMLModule(self): | |
347 return self.GetParameters() | |
348 | |
349 def GenerateFiles(self, args): | |
350 self.Write(self.GenerateAMDModule(), | |
351 self.MatchMojomFilePath("%s.js" % self.module.name)) | |
352 self.Write(self.GenerateHTMLModule(), | |
353 self.MatchMojomFilePath("%s.sky" % self.module.name)) | |
354 | |
355 def GetImports(self): | |
356 used_names = set() | |
357 for each_import in self.module.imports: | |
358 simple_name = each_import["module_name"].split(".")[0] | |
359 | |
360 # Since each import is assigned a variable in JS, they need to have unique | |
361 # names. | |
362 unique_name = simple_name | |
363 counter = 0 | |
364 while unique_name in used_names: | |
365 counter += 1 | |
366 unique_name = simple_name + str(counter) | |
367 | |
368 used_names.add(unique_name) | |
369 each_import["unique_name"] = unique_name + "$" | |
370 counter += 1 | |
371 return self.module.imports | |
372 | |
373 def GetImportedInterfaces(self): | |
374 interface_to_import = {}; | |
375 for each_import in self.module.imports: | |
376 for each_interface in each_import["module"].interfaces: | |
377 name = each_interface.name | |
378 interface_to_import[name] = each_import["unique_name"] + "." + name | |
379 return interface_to_import; | |
380 | |
OLD | NEW |