Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / bindings / scripts / unstable / idl_definitions.py
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:
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:
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=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
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, 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
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.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,
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=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
220
221
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
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.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,
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.extended_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_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
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.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,
288             }
289
290
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:
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.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,
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:
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)