1 # Copyright (C) 2013 Google Inc. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
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
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.
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.
29 """Blink IDL Intermediate Representation (IR) classes.
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.
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
51 """Abstract base class, used for JSON serialization."""
52 __metaclass__ = abc.ABCMeta
55 def json_serializable(self):
56 """Returns a JSON serializable form of the object.
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.
67 """Object with a type, such as an Attribute or Operation (return value).
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.
72 __metaclass__ = abc.ABCMeta
74 extended_attributes = None
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
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)
89 # Otherwise, IDL type is represented as string, so use a function
90 self.idl_type = resolve_typedefs(self.idl_type, typedefs)
96 class IdlDefinitions(BaseIdl):
97 def __init__(self, callback_functions=None, enumerations=None, file_name=None, 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
106 self.resolve_typedefs(typedefs)
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)
114 def json_serializable(self):
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()),
122 def to_json(self, debug=False):
123 """Returns a JSON string representing the Definitions.
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.
129 # Sort so order consistent, allowing comparison of output
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=(',', ':'))
137 class IdlCallbackFunction(BaseIdl, TypedObject):
138 def __init__(self, name=None, idl_type=None, arguments=None):
139 self.idl_type = idl_type
141 self.arguments = arguments or []
143 def resolve_typedefs(self, typedefs):
144 TypedObject.resolve_typedefs(self, typedefs)
145 for argument in self.arguments:
146 argument.resolve_typedefs(typedefs)
148 def json_serializable(self):
150 'callbackFunction::name': self.name,
151 'callbackFunction::type': self.idl_type,
152 'callbackFunction::parameters': self.arguments,
156 class IdlEnum(BaseIdl):
157 def __init__(self, name=None, values=None):
159 self.values = values or []
161 def json_serializable(self):
163 'domEnum::name': self.name,
164 'domEnum::values': self.values,
168 class IdlInterface(BaseIdl):
169 def __init__(self, attributes=None, constants=None, constructors=None, custom_constructors=None, extended_attributes=None, operations=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.operations = operations or []
176 self.is_callback = is_callback
177 self.is_partial = is_partial
178 self.is_exception = False
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)
194 def json_serializable(self):
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.extended_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_callback)),
204 'domInterface::isPartial': false_to_none(self.is_partial),
205 'domInterface::name': self.name,
206 'domInterface::parent': self.parent,
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=None, extended_attributes=None):
218 IdlInterface.__init__(self, name=name, constants=constants, operations=operations, attributes=attributes, extended_attributes=extended_attributes)
219 self.is_exception = True
222 class IdlAttribute(BaseIdl, TypedObject):
223 def __init__(self, idl_type=None, extended_attributes=None, getter_exceptions=None, is_nullable=False, is_static=False, is_read_only=False, name=None, setter_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
231 self.setter_exceptions = setter_exceptions or []
233 def json_serializable(self):
235 'domAttribute::extendedAttributes': none_to_value_is_missing(self.extended_attributes),
236 'domAttribute::getterExceptions': self.getter_exceptions,
237 'domAttribute::isNullable': boolean_to_perl_quoted(false_to_none(self.is_nullable)),
238 'domAttribute::isReadOnly': boolean_to_perl(false_to_none(self.is_read_only)),
239 'domAttribute::isStatic': boolean_to_perl(false_to_none(self.is_static)),
240 'domAttribute::name': self.name,
241 'domAttribute::setterExceptions': self.setter_exceptions,
242 'domAttribute::type': self.idl_type,
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 {}
253 def json_serializable(self):
255 'domConstant::extendedAttributes': none_to_value_is_missing(self.extended_attributes),
256 'domConstant::name': self.name,
257 'domConstant::type': self.idl_type,
258 'domConstant::value': self.value,
262 class IdlOperation(BaseIdl, TypedObject):
263 def __init__(self, is_static=False, name=None, idl_type=None, extended_attributes=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
274 def resolve_typedefs(self, typedefs):
275 TypedObject.resolve_typedefs(self, typedefs)
276 for argument in self.arguments:
277 argument.resolve_typedefs(typedefs)
279 def json_serializable(self):
281 'domFunction::extendedAttributes': none_to_value_is_missing(self.extended_attributes),
282 'domFunction::isStatic': boolean_to_perl(false_to_none(self.is_static)),
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,
291 class IdlArgument(BaseIdl, TypedObject):
292 def __init__(self, name=None, idl_type=None, extended_attributes=None, is_optional=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:
303 if is_variadic is None:
305 self.is_nullable = is_nullable # (T?)
306 self.is_optional = is_optional # (optional T)
307 self.is_variadic = is_variadic # (T...)
310 def json_serializable(self):
312 'domParameter::extendedAttributes': none_to_value_is_missing(self.extended_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,
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))
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
351 type_string = self.base_type
353 return type_string + '[]'
355 return 'sequence<%s>' % type_string
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)
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)
369 base_type = sequence_match.group(1)
370 return cls(base_type, is_sequence=True)
371 return cls(type_string)
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
379 class IdlUnionType(BaseIdl):
380 def __init__(self, union_member_types=None):
381 self.union_member_types = union_member_types or []
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]
388 def json_serializable(self):
390 'UnionType::unionMemberTypes': self.union_member_types,
394 # Perl JSON compatibility functions
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():
402 new_extended_attributes[key] = 'VALUE_IS_MISSING'
404 new_extended_attributes[key] = value
405 return new_extended_attributes
408 def boolean_to_perl(value):
409 # Perl stores booleans as 1, 0, or undefined (JSON null);
410 # convert to this format.
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.
422 return str(int(value))
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.
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)