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, to ensure that both | |
32 parsers produce the same output. | |
33 FIXME: remove BaseIdl, JSON export (json_serializable and to_json), and Perl | |
34 compatibility functions and hacks once Perl compiler gone. | |
35 """ | |
36 | |
37 # Disable attribute hiding check (else JSONEncoder default raises an error) | |
38 # pylint: disable=E0202 | |
39 # pylint doesn't understand ABCs. | |
40 # pylint: disable=W0232, E0203, W0201 | |
41 | |
42 import abc | |
43 import json | |
44 import re | |
45 | |
46 | |
47 # Base classes | |
48 | |
49 | |
50 class BaseIdl(object): | |
51 """Abstract base class, used for JSON serialization.""" | |
52 __metaclass__ = abc.ABCMeta | |
53 | |
54 @abc.abstractmethod | |
55 def json_serializable(self): | |
56 """Returns a JSON serializable form of the object. | |
57 | |
58 This should be a dictionary, with keys scoped names of the form | |
59 Class::key, where the scope is the class name. | |
60 This is so we produce identical output to the Perl code, which uses | |
61 the Perl module JSON.pm, which uses this format. | |
62 """ | |
63 pass | |
64 | |
65 | |
66 class TypedObject(object): | |
67 """Object with a type, such as an Attribute or Operation (return value). | |
68 | |
69 The type can be an actual type, or can be a typedef, which must be resolved | |
70 before passing data to the code generator. | |
71 """ | |
72 __metaclass__ = abc.ABCMeta | |
73 idl_type = None | |
74 extended_attributes = None | |
75 | |
76 def resolve_typedefs(self, typedefs): | |
77 """Resolve typedefs to actual types in the object.""" | |
78 # Constructors don't have their own return type, because it's the | |
79 # interface itself. | |
80 if not self.idl_type: | |
81 return | |
82 # (Types are represented either as strings or as IdlUnionType objects.) | |
83 # Union types are objects, which have a member function for this | |
84 if isinstance(self.idl_type, IdlUnionType): | |
85 # Method 'resolve_typedefs' call is ok, but pylint can't infer this | |
86 # pylint: disable=E1101 | |
87 self.idl_type.resolve_typedefs(typedefs) | |
88 return | |
89 # Otherwise, IDL type is represented as string, so use a function | |
90 self.idl_type = resolve_typedefs(self.idl_type, typedefs) | |
91 | |
92 | |
93 # IDL classes | |
94 | |
95 | |
96 class IdlDefinitions(BaseIdl): | |
97 def __init__(self, callback_functions=None, enumerations=None, file_name=Non
e, interfaces=None, typedefs=None): | |
98 self.callback_functions = callback_functions or {} | |
99 self.enumerations = enumerations or {} | |
100 self.file_name = file_name or None | |
101 self.interfaces = interfaces or {} | |
102 # Typedefs are not exposed by bindings; resolve typedefs with the | |
103 # actual types and then discard the Typedefs. | |
104 # http://www.w3.org/TR/WebIDL/#idl-typedefs | |
105 if typedefs: | |
106 self.resolve_typedefs(typedefs) | |
107 | |
108 def resolve_typedefs(self, typedefs): | |
109 for callback_function in self.callback_functions.itervalues(): | |
110 callback_function.resolve_typedefs(typedefs) | |
111 for interface in self.interfaces.itervalues(): | |
112 interface.resolve_typedefs(typedefs) | |
113 | |
114 def json_serializable(self): | |
115 return { | |
116 'idlDocument::callbackFunctions': self.callback_functions.values
(), | |
117 'idlDocument::enumerations': self.enumerations.values(), | |
118 'idlDocument::fileName': self.file_name, | |
119 'idlDocument::interfaces': sorted(self.interfaces.values()), | |
120 } | |
121 | |
122 def to_json(self, debug=False): | |
123 """Returns a JSON string representing the Definitions. | |
124 | |
125 The JSON output should be identical with the output of the Perl parser, | |
126 specifically the function serializeJSON in idl_serializer.pm, | |
127 which takes a Perl object created by idl_parser.pm. | |
128 """ | |
129 # Sort so order consistent, allowing comparison of output | |
130 if debug: | |
131 # indent turns on pretty-printing for legibility | |
132 return json.dumps(self, cls=IdlEncoder, sort_keys=True, indent=4) | |
133 # Use compact separators so output identical to Perl | |
134 return json.dumps(self, cls=IdlEncoder, sort_keys=True, separators=(',',
':')) | |
135 | |
136 | |
137 class IdlCallbackFunction(BaseIdl, TypedObject): | |
138 def __init__(self, name=None, idl_type=None, arguments=None): | |
139 self.idl_type = idl_type | |
140 self.name = name | |
141 self.arguments = arguments or [] | |
142 | |
143 def resolve_typedefs(self, typedefs): | |
144 TypedObject.resolve_typedefs(self, typedefs) | |
145 for argument in self.arguments: | |
146 argument.resolve_typedefs(typedefs) | |
147 | |
148 def json_serializable(self): | |
149 return { | |
150 'callbackFunction::name': self.name, | |
151 'callbackFunction::type': self.idl_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, operations=None, is_callback=Fals
e, 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.operations = operations or [] | |
176 self.is_callback = is_callback | |
177 self.is_partial = is_partial | |
178 self.is_exception = False | |
179 self.name = name | |
180 self.parent = parent | |
181 | |
182 def resolve_typedefs(self, typedefs): | |
183 for attribute in self.attributes: | |
184 attribute.resolve_typedefs(typedefs) | |
185 for constant in self.constants: | |
186 constant.resolve_typedefs(typedefs) | |
187 for constructor in self.constructors: | |
188 constructor.resolve_typedefs(typedefs) | |
189 for custom_constructor in self.custom_constructors: | |
190 custom_constructor.resolve_typedefs(typedefs) | |
191 for operation in self.operations: | |
192 operation.resolve_typedefs(typedefs) | |
193 | |
194 def json_serializable(self): | |
195 return { | |
196 'domInterface::attributes': self.attributes, | |
197 'domInterface::constants': self.constants, | |
198 'domInterface::constructors': self.constructors, | |
199 'domInterface::customConstructors': self.custom_constructors, | |
200 'domInterface::extendedAttributes': none_to_value_is_missing(self.ex
tended_attributes), | |
201 'domInterface::functions': self.operations, | |
202 'domInterface::isException': false_to_none(self.is_exception), | |
203 'domInterface::isCallback': boolean_to_perl(false_to_none(self.is_ca
llback)), | |
204 'domInterface::isPartial': false_to_none(self.is_partial), | |
205 'domInterface::name': self.name, | |
206 'domInterface::parent': self.parent, | |
207 } | |
208 | |
209 | |
210 class IdlException(IdlInterface): | |
211 # Properly exceptions and interfaces are distinct, and thus should inherit a | |
212 # common base class (say, "IdlExceptionOrInterface"). | |
213 # However, there is only one exception (DOMException), and new exceptions | |
214 # are not expected. Thus it is easier to implement exceptions as a | |
215 # restricted subclass of interfaces. | |
216 # http://www.w3.org/TR/WebIDL/#idl-exceptions | |
217 def __init__(self, name=None, constants=None, operations=None, attributes=No
ne, extended_attributes=None): | |
218 IdlInterface.__init__(self, name=name, constants=constants, operations=o
perations, attributes=attributes, extended_attributes=extended_attributes) | |
219 self.is_exception = True | |
220 | |
221 | |
222 class IdlAttribute(BaseIdl, TypedObject): | |
223 def __init__(self, idl_type=None, extended_attributes=None, getter_exception
s=None, is_nullable=False, is_static=False, is_read_only=False, name=None, sette
r_exceptions=None): | |
224 self.idl_type = idl_type | |
225 self.extended_attributes = extended_attributes or {} | |
226 self.getter_exceptions = getter_exceptions or [] | |
227 self.is_nullable = is_nullable | |
228 self.is_static = is_static | |
229 self.is_read_only = is_read_only | |
230 self.name = name | |
231 self.setter_exceptions = setter_exceptions or [] | |
232 | |
233 def json_serializable(self): | |
234 return { | |
235 'domAttribute::extendedAttributes': none_to_value_is_missing(self.ex
tended_attributes), | |
236 'domAttribute::getterExceptions': self.getter_exceptions, | |
237 'domAttribute::isNullable': boolean_to_perl_quoted(false_to_none(sel
f.is_nullable)), | |
238 'domAttribute::isReadOnly': boolean_to_perl(false_to_none(self.is_re
ad_only)), | |
239 'domAttribute::isStatic': boolean_to_perl(false_to_none(self.is_stat
ic)), | |
240 'domAttribute::name': self.name, | |
241 'domAttribute::setterExceptions': self.setter_exceptions, | |
242 'domAttribute::type': self.idl_type, | |
243 } | |
244 | |
245 | |
246 class IdlConstant(BaseIdl, TypedObject): | |
247 def __init__(self, name=None, idl_type=None, value=None, extended_attributes
=None): | |
248 self.idl_type = idl_type | |
249 self.extended_attributes = extended_attributes or {} | |
250 self.name = name | |
251 self.value = value | |
252 | |
253 def json_serializable(self): | |
254 return { | |
255 'domConstant::extendedAttributes': none_to_value_is_missing(self.ext
ended_attributes), | |
256 'domConstant::name': self.name, | |
257 'domConstant::type': self.idl_type, | |
258 'domConstant::value': self.value, | |
259 } | |
260 | |
261 | |
262 class IdlOperation(BaseIdl, TypedObject): | |
263 def __init__(self, is_static=False, name=None, idl_type=None, extended_attri
butes=None, specials=None, arguments=None, overloaded_index=None): | |
264 self.is_static = is_static | |
265 self.name = name or '' | |
266 self.idl_type = idl_type | |
267 self.extended_attributes = extended_attributes or {} | |
268 self.specials = specials or [] | |
269 self.arguments = arguments or [] | |
270 # FIXME: remove overloaded_index (only here for Perl compatibility), | |
271 # as overloading is handled in code generator (v8_interface.py). | |
272 self.overloaded_index = overloaded_index | |
273 | |
274 def resolve_typedefs(self, typedefs): | |
275 TypedObject.resolve_typedefs(self, typedefs) | |
276 for argument in self.arguments: | |
277 argument.resolve_typedefs(typedefs) | |
278 | |
279 def json_serializable(self): | |
280 return { | |
281 'domFunction::extendedAttributes': none_to_value_is_missing(self.ext
ended_attributes), | |
282 'domFunction::isStatic': boolean_to_perl(false_to_none(self.is_stati
c)), | |
283 'domFunction::name': self.name, | |
284 'domFunction::overloadedIndex': self.overloaded_index, | |
285 'domFunction::parameters': self.arguments, | |
286 'domFunction::specials': self.specials, | |
287 'domFunction::type': self.idl_type, | |
288 } | |
289 | |
290 | |
291 class IdlArgument(BaseIdl, TypedObject): | |
292 def __init__(self, name=None, idl_type=None, extended_attributes=None, is_op
tional=False, is_nullable=None, is_variadic=False): | |
293 self.idl_type = idl_type | |
294 self.extended_attributes = extended_attributes or {} | |
295 # FIXME: boolean values are inconsistent. | |
296 # The below hack is so that generated JSON is identical to | |
297 # Perl-generated JSON, where the exact values depend on the code path. | |
298 # False and None (Perl: 0 and undef) are semantically interchangeable, | |
299 # but yield different JSON. | |
300 # Once Perl removed, have all default to False. | |
301 if is_optional is None: | |
302 is_optional = False | |
303 if is_variadic is None: | |
304 is_variadic = False | |
305 self.is_nullable = is_nullable # (T?) | |
306 self.is_optional = is_optional # (optional T) | |
307 self.is_variadic = is_variadic # (T...) | |
308 self.name = name | |
309 | |
310 def json_serializable(self): | |
311 return { | |
312 'domParameter::extendedAttributes': none_to_value_is_missing(self.ex
tended_attributes), | |
313 'domParameter::isNullable': boolean_to_perl_quoted(self.is_nullable)
, | |
314 'domParameter::isOptional': boolean_to_perl(self.is_optional), | |
315 'domParameter::isVariadic': boolean_to_perl(self.is_variadic), | |
316 'domParameter::name': self.name, | |
317 'domParameter::type': self.idl_type, | |
318 } | |
319 | |
320 # Type classes | |
321 | |
322 | |
323 def resolve_typedefs(idl_type, typedefs): | |
324 """Return an IDL type (as string) with typedefs resolved.""" | |
325 # Converts a string representation to and from an IdlType object to handle | |
326 # parsing of composite types (arrays and sequences) and encapsulate typedef | |
327 # resolution, e.g., GLint[] -> unsigned long[] requires parsing the '[]'. | |
328 # Use fluent interface to avoid auxiliary variable. | |
329 return str(IdlType.from_string(idl_type).resolve_typedefs(typedefs)) | |
330 | |
331 | |
332 class IdlType(object): | |
333 # FIXME: replace type strings with these objects, | |
334 # so don't need to parse everywhere types are used. | |
335 # Types are stored internally as strings, not objects, | |
336 # e.g., as 'sequence<Foo>' or 'Foo[]', | |
337 # hence need to parse the string whenever a type is used. | |
338 # FIXME: incorporate Nullable, Variadic, etc. | |
339 # FIXME: properly should nest types | |
340 # Formally types are nested, e.g., short?[] vs. short[]?, | |
341 # but in practice these complex types aren't used and can treat | |
342 # as orthogonal properties. | |
343 def __init__(self, base_type, is_array=False, is_sequence=False): | |
344 if is_array and is_sequence: | |
345 raise ValueError('Array of Sequences are not allowed.') | |
346 self.base_type = base_type | |
347 self.is_array = is_array | |
348 self.is_sequence = is_sequence | |
349 | |
350 def __str__(self): | |
351 type_string = self.base_type | |
352 if self.is_array: | |
353 return type_string + '[]' | |
354 if self.is_sequence: | |
355 return 'sequence<%s>' % type_string | |
356 return type_string | |
357 | |
358 @classmethod | |
359 def from_string(cls, type_string): | |
360 sequence_re = r'^sequence<([^>]*)>$' | |
361 if type_string.endswith('[]'): | |
362 type_string = type_string[:-2] | |
363 sequence_match = re.match(sequence_re, type_string) | |
364 if sequence_match: | |
365 raise ValueError('Array of Sequences are not allowed.') | |
366 return cls(type_string, is_array=True) | |
367 sequence_match = re.match(sequence_re, type_string) | |
368 if sequence_match: | |
369 base_type = sequence_match.group(1) | |
370 return cls(base_type, is_sequence=True) | |
371 return cls(type_string) | |
372 | |
373 def resolve_typedefs(self, typedefs): | |
374 if self.base_type in typedefs: | |
375 self.base_type = typedefs[self.base_type] | |
376 return self # Fluent interface | |
377 | |
378 | |
379 class IdlUnionType(BaseIdl): | |
380 def __init__(self, union_member_types=None): | |
381 self.union_member_types = union_member_types or [] | |
382 | |
383 def resolve_typedefs(self, typedefs): | |
384 self.union_member_types = [ | |
385 typedefs.get(union_member_type, union_member_type) | |
386 for union_member_type in self.union_member_types] | |
387 | |
388 def json_serializable(self): | |
389 return { | |
390 'UnionType::unionMemberTypes': self.union_member_types, | |
391 } | |
392 | |
393 | |
394 # Perl JSON compatibility functions | |
395 | |
396 def none_to_value_is_missing(extended_attributes): | |
397 # Perl IDL Parser uses 'VALUE_IS_MISSING' for null values in | |
398 # extended attributes, so add this as a filter when exporting to JSON. | |
399 new_extended_attributes = {} | |
400 for key, value in extended_attributes.iteritems(): | |
401 if value is None: | |
402 new_extended_attributes[key] = 'VALUE_IS_MISSING' | |
403 else: | |
404 new_extended_attributes[key] = value | |
405 return new_extended_attributes | |
406 | |
407 | |
408 def boolean_to_perl(value): | |
409 # Perl stores booleans as 1, 0, or undefined (JSON null); | |
410 # convert to this format. | |
411 if value is None: | |
412 return None | |
413 return int(value) | |
414 | |
415 | |
416 def boolean_to_perl_quoted(value): | |
417 # Bug-for-bug compatibility with Perl. | |
418 # The value of isNullable is quoted ('1', '0', or undefined), rather than | |
419 # an integer, so add quotes. | |
420 if value is None: | |
421 return None | |
422 return str(int(value)) | |
423 | |
424 | |
425 def false_to_none(value): | |
426 # The Perl parser generally uses undefined (Python None) rather than False | |
427 # for boolean flags, because the value is simply left undefined, rather than | |
428 # explicitly set to False. | |
429 if value is False: | |
430 return None | |
431 return value | |
432 | |
433 | |
434 # JSON export | |
435 | |
436 | |
437 class IdlEncoder(json.JSONEncoder): | |
438 def default(self, obj): | |
439 if isinstance(obj, BaseIdl): | |
440 return obj.json_serializable() | |
441 return json.JSONEncoder.default(self, obj) | |
OLD | NEW |