OLD | NEW |
---|---|
(Empty) | |
1 # Copyright (C) 2013 Google Inc. All rights reserved. | |
2 # | |
3 # Redistribution and use in source and binary forms, with or without | |
4 # modification, are permitted provided that the following conditions are | |
5 # met: | |
6 # | |
7 # * Redistributions of source code must retain the above copyright | |
8 # notice, this list of conditions and the following disclaimer. | |
9 # * Redistributions in binary form must reproduce the above | |
10 # copyright notice, this list of conditions and the following disclaimer | |
11 # in the documentation and/or other materials provided with the | |
12 # distribution. | |
13 # * Neither the name of Google Inc. nor the names of its | |
14 # contributors may be used to endorse or promote products derived from | |
15 # this software without specific prior written permission. | |
16 # | |
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | |
29 """Blink IDL Intermediate Representation (IR) classes. | |
30 | |
31 Also JSON export, using legacy Perl terms and format. | |
haraken
2013/07/17 02:44:49
Given that we decided to keep deprecated_idl_parse
Nils Barth (inactive)
2013/07/17 12:05:09
Right; see separate detailed reply.
Nils Barth (inactive)
2013/07/17 12:13:46
Exactly, or more precisely: we still need the clas
| |
32 """ | |
33 | |
34 # Disable attribute hiding check (else JSONEncoder default raises an error) | |
35 # pylint: disable=E0202 | |
36 # pylint doesn't understand ABCs. | |
37 # pylint: disable=W0232, E0203, W0201 | |
haraken
2013/07/16 14:17:51
Is this comment helpful?
Nils Barth (inactive)
2013/07/17 12:05:09
Yup, I get lots of pylint errors and warnings othe
| |
38 | |
39 import abc | |
40 import json | |
41 import os.path | |
42 import re | |
43 | |
44 | |
45 # Base classes | |
46 | |
47 | |
48 class BaseIdl: | |
49 """Abstract base class, used for JSON serialization.""" | |
50 # FIXME: remove when Perl removed | |
51 __metaclass__ = abc.ABCMeta | |
haraken
2013/07/17 05:43:11
I'm a bit confused with the comments above. You ca
Nils Barth (inactive)
2013/07/17 12:05:09
Yup, but we don’t need the BaseIdl class, since it
| |
52 | |
53 @abc.abstractmethod | |
54 def json_serializable(self): | |
55 """Returns a JSON serializable form of the object. | |
56 | |
57 This should be a dictionary, with keys scoped names of the form | |
58 Class::key, where the scope is the class name. | |
59 This is so we produce identical output to the Perl code, which uses | |
60 the Perl module JSON.pm, which uses this format. | |
61 """ | |
62 pass | |
63 | |
64 | |
65 class TypedObject(BaseIdl): | |
haraken
2013/07/17 05:43:11
Actually I don't fully understand what class shoul
Nils Barth (inactive)
2013/07/17 12:05:09
BaseIdl is used for JSON export, so everything in
| |
66 """Object with a Type, such as an Attribute or Operation (return value).""" | |
haraken
2013/07/21 14:31:50
You might also want to add "The type name can be m
Nils Barth (inactive)
2013/07/22 06:32:01
Good point: “type” can be a “Type” (actual type) o
| |
67 __metaclass__ = abc.ABCMeta | |
haraken
2013/07/17 05:43:11
This wouldn't be needed.
Nils Barth (inactive)
2013/07/17 12:05:09
Setting metaclass to ABCMeta is needed to indicate
| |
68 data_type = None | |
69 extended_attributes = {} | |
70 | |
71 def apply_typedefs(self, typedefs): | |
72 """Applies Typedefs to object itself (e.g., return type of function). | |
73 | |
74 Override for instance if calling on arguments of function.""" | |
75 new_extended_attributes = {} | |
76 # Convert string representation to and from an IdlType object | |
77 # to handle parsing | |
78 data_type_object = IdlType.from_string(self.data_type) | |
79 base_type = data_type_object.base_type | |
80 if base_type in typedefs: | |
81 replacement_type = typedefs[base_type] | |
82 data_type_object.base_type = replacement_type.data_type | |
83 new_extended_attributes = replacement_type.extended_attributes | |
84 self.data_type = str(data_type_object) | |
85 self.extended_attributes.update(new_extended_attributes) | |
86 if new_extended_attributes: | |
87 raise ValueError('Extended attributes in a typedef are untested!') | |
haraken
2013/07/17 05:43:11
This will happen in bindings/tests/idls/TestTypede
Nils Barth (inactive)
2013/07/17 12:05:09
Got it, removed! (We don’t have examples in the ac
| |
88 | |
89 | |
90 # IDL classes | |
91 | |
92 | |
93 class IdlDefinitions(BaseIdl): | |
94 def __init__(self, callback_functions=None, enumerations=None, exceptions=No ne, file_name=None, interfaces=None, typedefs=None): | |
haraken
2013/07/17 05:43:11
It looks like you're explicitly specifying all par
Nils Barth (inactive)
2013/07/17 12:05:09
(Long comment.)
We need default parameters to hav
Nils Barth (inactive)
2013/07/17 12:17:01
(Continuing: default parameters needed for named p
haraken
2013/07/21 14:31:50
Agreed, thanks for the clarification. Let's keep t
| |
95 self.callback_functions = callback_functions or [] | |
96 self.enumerations = enumerations or [] | |
97 self.exceptions = exceptions or {} | |
98 if file_name: | |
99 self.file_name = os.path.abspath(file_name) | |
100 self.interfaces = interfaces or {} | |
101 if typedefs: | |
102 self.apply_typedefs(typedefs) | |
haraken
2013/07/17 05:43:11
Help me understand: Why do you need to call apply_
Nils Barth (inactive)
2013/07/17 12:05:09
Typedefs aren’t exposed by the bindings (they’re p
| |
103 | |
104 def apply_typedefs(self, typedefs): | |
105 for callback_function in self.callback_functions: | |
haraken
2013/07/17 05:43:11
callback_functions.itervalues().
The same comment
Nils Barth (inactive)
2013/07/17 12:05:09
Good point. It’s useful to access all of these by
| |
106 callback_function.apply_typedefs(typedefs) | |
107 for exception in self.exceptions.itervalues(): | |
108 exception.apply_typedefs(typedefs) | |
109 for interface in self.interfaces.itervalues(): | |
110 interface.apply_typedefs(typedefs) | |
111 | |
112 def json_serializable(self): | |
113 return { | |
114 'idlDocument::callbackFunctions': self.callback_functions, | |
115 'idlDocument::enumerations': self.enumerations, | |
116 'idlDocument::fileName': self.file_name, | |
117 # Perl treats exceptions as a kind of interface | |
118 'idlDocument::interfaces': sorted(self.exceptions.values() + sel f.interfaces.values()), | |
119 } | |
120 | |
121 def to_json(self, debug=False): | |
122 """Returns a JSON string representing the Definitions. | |
123 | |
124 The JSON output should be identical with the output of the Perl parser, | |
125 specifically the function serializeJSON in deprecated_idl_serializer.pm, | |
126 which takes a Perl object created by deprecated_idl_parser.pm. | |
127 """ | |
128 # Sort so order consistent, allowing comparison of output | |
129 if debug: | |
130 # indent turns on pretty-printing for legibility | |
131 return json.dumps(self, cls=IdlEncoder, sort_keys=True, indent=4) | |
132 # Use compact separators so output identical to Perl | |
133 return json.dumps(self, cls=IdlEncoder, sort_keys=True, separators=(',', ':')) | |
134 | |
135 | |
136 class IdlCallbackFunction(TypedObject): | |
137 def __init__(self, name=None, data_type=None, arguments=None): | |
138 self.data_type = data_type | |
139 self.name = name | |
140 self.arguments = arguments or [] | |
141 | |
142 def apply_typedefs(self, typedefs): | |
143 TypedObject.apply_typedefs(self, typedefs) | |
144 for argument in self.arguments: | |
145 argument.apply_typedefs(typedefs) | |
146 raise ValueError('Typedefs in callback functions are untested!') | |
147 | |
148 def json_serializable(self): | |
149 return { | |
150 'callbackFunction::name': self.name, | |
151 'callbackFunction::type': self.data_type, | |
152 'callbackFunction::parameters': self.arguments, | |
153 } | |
154 | |
155 | |
156 class IdlEnum(BaseIdl): | |
157 def __init__(self, name=None, values=None): | |
158 self.name = name | |
159 self.values = values or [] | |
160 | |
161 def json_serializable(self): | |
162 return { | |
163 'domEnum::name': self.name, | |
164 'domEnum::values': self.values, | |
165 } | |
166 | |
167 | |
168 class IdlInterface(BaseIdl): | |
169 def __init__(self, attributes=None, constants=None, constructors=None, custo m_constructors=None, extended_attributes=None, functions=None, is_callback=False , is_partial=False, name=None, parent=None): | |
170 self.attributes = attributes or [] | |
171 self.constants = constants or [] | |
172 self.constructors = constructors or [] | |
173 self.custom_constructors = custom_constructors or [] | |
174 self.extended_attributes = extended_attributes or {} | |
175 self.functions = functions or [] | |
176 self.is_callback = is_callback | |
177 self.is_partial = is_partial | |
178 self.name = name | |
179 self.parent = parent | |
180 | |
181 def apply_typedefs(self, typedefs): | |
182 for attribute in self.attributes: | |
183 attribute.apply_typedefs(typedefs) | |
184 for constant in self.constants: | |
185 constant.apply_typedefs(typedefs) | |
186 for constructor in self.constructors: | |
187 constructor.apply_typedefs(typedefs) | |
188 for custom_constructor in self.custom_constructors: | |
189 custom_constructor.apply_typedefs(typedefs) | |
190 for function in self.functions: | |
191 function.apply_typedefs(typedefs) | |
192 | |
193 def json_serializable(self): | |
194 return { | |
195 'domInterface::attributes': self.attributes, | |
196 'domInterface::constants': self.constants, | |
197 'domInterface::constructors': self.constructors, | |
198 'domInterface::customConstructors': self.custom_constructors, | |
199 'domInterface::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes), | |
200 'domInterface::functions': self.functions, | |
201 'domInterface::isException': None, | |
202 'domInterface::isCallback': boolean_to_perl(false_to_none(self.is_ca llback)), | |
203 'domInterface::isPartial': false_to_none(self.is_partial), | |
204 'domInterface::name': self.name, | |
205 'domInterface::parent': self.parent, | |
206 } | |
207 | |
208 | |
209 class IdlException(BaseIdl): | |
210 def __init__(self, name=None, constants=None, functions=None, attributes=Non e, extended_attributes=None): | |
211 self.attributes = attributes or [] | |
212 self.constants = constants or [] | |
213 self.extended_attributes = extended_attributes or {} | |
214 self.functions = functions or [] | |
215 self.name = name | |
216 | |
217 def apply_typedefs(self, typedefs): | |
218 for constant in self.constants: | |
219 constant.apply_typedefs(typedefs) | |
220 for attribute in self.attributes: | |
221 attribute.apply_typedefs(typedefs) | |
222 for function in self.functions: | |
223 function.apply_typedefs(typedefs) | |
224 | |
225 def json_serializable(self): | |
226 return { | |
227 # Perl code treats Exceptions as a kind of Interface | |
228 'domInterface::name': self.name, | |
229 'domInterface::attributes': self.attributes, | |
230 'domInterface::constants': self.constants, | |
231 'domInterface::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes), | |
232 'domInterface::functions': self.functions, | |
233 # These values don't vary for exceptions | |
234 'domInterface::constructors': [], | |
235 'domInterface::customConstructors': [], | |
236 'domInterface::isException': 1, | |
237 'domInterface::isCallback': None, | |
238 'domInterface::isPartial': None, | |
239 'domInterface::parent': None, | |
240 } | |
241 | |
242 | |
243 class IdlAttribute(TypedObject): | |
244 def __init__(self, data_type=None, extended_attributes=None, getter_exceptio ns=None, is_nullable=False, is_static=False, is_read_only=False, name=None, sett er_exceptions=None): | |
245 self.data_type = data_type | |
246 self.extended_attributes = extended_attributes or {} | |
247 self.getter_exceptions = getter_exceptions or [] | |
248 self.is_nullable = is_nullable | |
249 self.is_static = is_static | |
250 self.is_read_only = is_read_only | |
251 self.name = name | |
252 self.setter_exceptions = setter_exceptions or [] | |
253 | |
254 def json_serializable(self): | |
255 return { | |
256 'domAttribute::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes), | |
257 'domAttribute::getterExceptions': self.getter_exceptions, | |
258 'domAttribute::isNullable': boolean_to_perl_quoted(false_to_none(sel f.is_nullable)), | |
259 'domAttribute::isReadOnly': boolean_to_perl(false_to_none(self.is_re ad_only)), | |
260 'domAttribute::isStatic': boolean_to_perl(false_to_none(self.is_stat ic)), | |
261 'domAttribute::name': self.name, | |
262 'domAttribute::setterExceptions': self.setter_exceptions, | |
263 'domAttribute::type': self.data_type, | |
264 } | |
265 | |
266 | |
267 class IdlConstant(TypedObject): | |
268 def __init__(self, name=None, data_type=None, value=None, extended_attribute s=None): | |
269 self.data_type = data_type | |
270 self.extended_attributes = extended_attributes or {} | |
271 self.name = name | |
272 self.value = value | |
273 | |
274 def json_serializable(self): | |
275 return { | |
276 'domConstant::extendedAttributes': none_to_value_is_missing(self.ext ended_attributes), | |
277 'domConstant::name': self.name, | |
278 'domConstant::type': self.data_type, | |
279 'domConstant::value': self.value, | |
280 } | |
281 | |
282 | |
283 class IdlOperation(TypedObject): | |
284 def __init__(self, is_static=False, name=None, data_type=None, extended_attr ibutes=None, specials=None, arguments=None, overloaded_index=None): | |
285 self.is_static = is_static | |
286 self.name = name or '' | |
287 self.data_type = data_type | |
288 self.extended_attributes = extended_attributes or {} | |
289 self.specials = specials or [] | |
290 self.arguments = arguments or [] | |
291 self.overloaded_index = overloaded_index | |
292 | |
293 def apply_typedefs(self, typedefs): | |
294 TypedObject.apply_typedefs(self, typedefs) | |
295 for argument in self.arguments: | |
296 argument.apply_typedefs(typedefs) | |
297 | |
298 def json_serializable(self): | |
299 return { | |
300 'domFunction::extendedAttributes': none_to_value_is_missing(self.ext ended_attributes), | |
301 'domFunction::isStatic': boolean_to_perl(false_to_none(self.is_stati c)), | |
302 'domFunction::name': self.name, | |
303 'domFunction::overloadedIndex': self.overloaded_index, | |
304 'domFunction::parameters': self.arguments, | |
305 'domFunction::specials': self.specials, | |
306 'domFunction::type': self.data_type, | |
307 } | |
308 | |
309 | |
310 class IdlArgument(TypedObject): | |
311 def __init__(self, name=None, data_type=None, extended_attributes=None, is_o ptional=False, is_nullable=None, is_variadic=False): | |
312 self.data_type = data_type | |
313 self.extended_attributes = extended_attributes or {} | |
314 # FIXME: boolean values are inconsistent. | |
315 # The below hack is so that generated JSON is identical to | |
316 # Perl-generated JSON, where the exact values depend on the code path. | |
317 # False and None (Perl: 0 and undef) are semantically interchangeable, | |
318 # but yield different JSON. | |
319 # Once Perl removed, have all default to False. | |
320 if is_optional is None: | |
321 is_optional = False | |
322 if is_variadic is None: | |
323 is_variadic = False | |
324 self.is_nullable = is_nullable # (T?) | |
325 self.is_optional = is_optional # (optional T) | |
326 self.is_variadic = is_variadic # (T...) | |
327 self.name = name | |
328 | |
329 def json_serializable(self): | |
330 return { | |
331 'domParameter::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes), | |
332 'domParameter::isNullable': boolean_to_perl_quoted(self.is_nullable) , | |
333 'domParameter::isOptional': boolean_to_perl(self.is_optional), | |
334 'domParameter::isVariadic': boolean_to_perl(self.is_variadic), | |
335 'domParameter::name': self.name, | |
336 'domParameter::type': self.data_type, | |
337 } | |
338 | |
339 # Type classes | |
340 | |
341 | |
342 class IdlType: | |
343 # FIXME: replace Type strings with these objects, | |
344 # so don't need to parse everywhere types are used. | |
345 # Types are stored internally as strings, not objects, | |
346 # e.g., as 'sequence<Foo>' or 'Foo[]', | |
347 # hence need to parse the string whenever a type is used. | |
348 # FIXME: incorporate Nullable, Variadic, etc. | |
349 # FIXME: properly should nest types | |
350 # Formally types are nested, e.g., short?[] vs. short[]?, | |
351 # but in practice these complex types aren't used and can treat | |
352 # as orthogonal properties. | |
353 def __init__(self, base_type=None, is_array=False, is_sequence=False): | |
354 if is_array and is_sequence: | |
355 raise ValueError('Array of Sequences not allowed.') | |
356 self.base_type = base_type | |
357 self.is_array = is_array | |
358 self.is_sequence = is_sequence | |
359 | |
360 def __str__(self): | |
361 type_string = self.base_type | |
362 if self.is_array: | |
363 type_string += '[]' | |
364 if self.is_sequence: | |
365 type_string = 'sequence<%s>' % type_string | |
366 return type_string | |
367 | |
368 @classmethod | |
369 def from_string(cls, type_string): | |
370 is_array = False | |
371 if type_string.endswith('[]'): | |
372 is_array = True | |
373 type_string = type_string[:-2] | |
374 | |
haraken
2013/07/17 05:43:11
Shall we use if-else?
if type_string.endwith('[]'
Nils Barth (inactive)
2013/07/17 12:05:09
You’re right, this code was a bit messy and duplic
| |
375 is_sequence = False | |
376 sequence_re = r'^sequence<([^>]*)>$' | |
377 sequence_match = re.match(sequence_re, type_string) | |
378 if sequence_match: | |
379 is_sequence = True | |
380 type_string = sequence_match.group(1) | |
381 base_type = type_string | |
382 return cls(base_type=base_type, is_array=is_array, is_sequence=is_sequen ce) | |
383 | |
384 | |
385 class IdlTypedef: | |
386 # Internal to IDL parsing: typedefs are all translated during IdlObject | |
387 # construction, and the typedefs themselves not stored in the object. | |
388 def __init__(self, extended_attributes=None, data_type=None): | |
389 self.extended_attributes = extended_attributes or {} | |
390 self.data_type = data_type | |
391 | |
392 | |
393 class IdlUnionType(BaseIdl): | |
394 def __init__(self, union_member_types=None): | |
395 self.union_member_types = union_member_types or [] | |
396 | |
397 def json_serializable(self): | |
398 return { | |
399 'UnionType::unionMemberTypes': self.union_member_types, | |
400 } | |
401 | |
402 | |
403 # Perl JSON compatibility functions | |
404 # FIXME: remove when Perl removed | |
405 | |
406 def none_to_value_is_missing(extended_attributes): | |
407 # Perl IDL Parser uses 'VALUE_IS_MISSING' for null values in | |
408 # extended attributes, so add this as a filter when exporting to JSON. | |
409 new_extended_attributes = {} | |
410 for key, value in extended_attributes.iteritems(): | |
411 if value is None: | |
412 new_extended_attributes[key] = 'VALUE_IS_MISSING' | |
413 else: | |
414 new_extended_attributes[key] = value | |
415 return new_extended_attributes | |
416 | |
417 | |
418 def boolean_to_perl(value): | |
419 # Perl stores booleans as 1, 0, or undefined (JSON null); | |
420 # convert to this format. | |
421 if value is None: | |
422 return None | |
423 return int(value) | |
424 | |
425 | |
426 def boolean_to_perl_quoted(value): | |
427 # Bug-for-bug compatibility with Perl. | |
428 # The value of isNullable is quoted ('1', '0', or undefined), rather than | |
429 # an integer, so add quotes. | |
430 if value is None: | |
431 return None | |
432 return str(int(value)) | |
433 | |
434 | |
435 def false_to_none(value): | |
436 # The Perl parser generally uses undefined (Python None) rather than False | |
437 # for boolean flags, because the value is simply left undefined, rather than | |
438 # explicitly set to False. | |
439 if value is False: | |
440 return None | |
441 return value | |
442 | |
443 | |
444 # JSON export | |
445 # FIXME: remove when Perl removed, as then no longer using JSON as | |
446 # an intermediate format. | |
447 | |
448 | |
449 class IdlEncoder(json.JSONEncoder): | |
450 def default(self, obj): | |
451 if isinstance(obj, BaseIdl): | |
452 return obj.json_serializable() | |
453 return json.JSONEncoder.default(self, obj) | |
OLD | NEW |