Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(511)

Side by Side Diff: Source/bindings/scripts/idl_definitions.py

Issue 15801003: IDL parser rewrite in Python (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Final (rebased). Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 os.path
45 import re
46
47
48 # Base classes
49
50
51 class BaseIdl:
52 """Abstract base class, used for JSON serialization."""
53 __metaclass__ = abc.ABCMeta
54
55 @abc.abstractmethod
56 def json_serializable(self):
57 """Returns a JSON serializable form of the object.
58
59 This should be a dictionary, with keys scoped names of the form
60 Class::key, where the scope is the class name.
61 This is so we produce identical output to the Perl code, which uses
62 the Perl module JSON.pm, which uses this format.
63 """
64 pass
65
66
67 class TypedObject:
68 """Object with a type, such as an Attribute or Operation (return value).
69
70 The type can be an actual Type, or can be a Typedef, which must be resolved
71 before passing data to the code generator.
72 """
73 __metaclass__ = abc.ABCMeta
74 data_type = None
75 extended_attributes = None
76
77 def resolve_typedefs(self, typedefs):
78 """Resolve Typedefs to actual Types in the object."""
79 additional_extended_attributes = {}
80 # Convert string representation to and from an IdlType object
81 # to handle parsing
82 data_type_object = IdlType.from_string(self.data_type)
83 base_type = data_type_object.base_type
84 if base_type in typedefs:
85 replacement_type = typedefs[base_type]
86 data_type_object.base_type = replacement_type.data_type
87 additional_extended_attributes = replacement_type.extended_attribute s
88 self.data_type = str(data_type_object)
89 self.extended_attributes.update(additional_extended_attributes)
90
91
92 # IDL classes
93
94
95 class IdlDefinitions(BaseIdl):
96 def __init__(self, callback_functions=None, enumerations=None, exceptions=No ne, file_name=None, interfaces=None, typedefs=None):
97 self.callback_functions = callback_functions or {}
98 self.enumerations = enumerations or {}
99 self.exceptions = exceptions or {}
100 if file_name:
101 self.file_name = os.path.abspath(file_name)
102 self.interfaces = interfaces or {}
103 # Typedefs are not exposed by bindings; resolve Typedefs with the
104 # actual Types and then discard the Typedefs.
105 # http://www.w3.org/TR/WebIDL/#idl-typedefs
106 if typedefs:
107 self.resolve_typedefs(typedefs)
108
109 def resolve_typedefs(self, typedefs):
110 for callback_function in self.callback_functions.itervalues():
111 callback_function.resolve_typedefs(typedefs)
112 for exception in self.exceptions.itervalues():
113 exception.resolve_typedefs(typedefs)
114 for interface in self.interfaces.itervalues():
115 interface.resolve_typedefs(typedefs)
116
117 def json_serializable(self):
118 return {
119 'idlDocument::callbackFunctions': self.callback_functions.values (),
120 'idlDocument::enumerations': self.enumerations.values(),
121 'idlDocument::fileName': self.file_name,
122 # Perl treats exceptions as a kind of interface
123 'idlDocument::interfaces': sorted(self.exceptions.values() + sel f.interfaces.values()),
124 }
125
126 def to_json(self, debug=False):
127 """Returns a JSON string representing the Definitions.
128
129 The JSON output should be identical with the output of the Perl parser,
130 specifically the function serializeJSON in deprecated_idl_serializer.pm,
131 which takes a Perl object created by deprecated_idl_parser.pm.
132 """
133 # Sort so order consistent, allowing comparison of output
134 if debug:
135 # indent turns on pretty-printing for legibility
136 return json.dumps(self, cls=IdlEncoder, sort_keys=True, indent=4)
137 # Use compact separators so output identical to Perl
138 return json.dumps(self, cls=IdlEncoder, sort_keys=True, separators=(',', ':'))
139
140
141 class IdlCallbackFunction(BaseIdl, TypedObject):
142 def __init__(self, name=None, data_type=None, arguments=None):
143 self.data_type = data_type
144 self.name = name
145 self.arguments = arguments or []
146
147 def resolve_typedefs(self, typedefs):
148 TypedObject.resolve_typedefs(self, typedefs)
149 for argument in self.arguments:
150 argument.resolve_typedefs(typedefs)
151 raise ValueError('Typedefs in callback functions are untested!')
152
153 def json_serializable(self):
154 return {
155 'callbackFunction::name': self.name,
156 'callbackFunction::type': self.data_type,
157 'callbackFunction::parameters': self.arguments,
158 }
159
160
161 class IdlEnum(BaseIdl):
162 def __init__(self, name=None, values=None):
163 self.name = name
164 self.values = values or []
165
166 def json_serializable(self):
167 return {
168 'domEnum::name': self.name,
169 'domEnum::values': self.values,
170 }
171
172
173 class IdlInterface(BaseIdl):
174 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):
175 self.attributes = attributes or []
176 self.constants = constants or []
177 self.constructors = constructors or []
178 self.custom_constructors = custom_constructors or []
179 self.extended_attributes = extended_attributes or {}
180 self.operations = operations or []
181 self.is_callback = is_callback
182 self.is_partial = is_partial
183 self.name = name
184 self.parent = parent
185
186 def resolve_typedefs(self, typedefs):
187 for attribute in self.attributes:
188 attribute.resolve_typedefs(typedefs)
189 for constant in self.constants:
190 constant.resolve_typedefs(typedefs)
191 for constructor in self.constructors:
192 constructor.resolve_typedefs(typedefs)
193 for custom_constructor in self.custom_constructors:
194 custom_constructor.resolve_typedefs(typedefs)
195 for operation in self.operations:
196 operation.resolve_typedefs(typedefs)
197
198 def json_serializable(self):
199 return {
200 'domInterface::attributes': self.attributes,
201 'domInterface::constants': self.constants,
202 'domInterface::constructors': self.constructors,
203 'domInterface::customConstructors': self.custom_constructors,
204 'domInterface::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes),
205 'domInterface::functions': self.operations,
206 'domInterface::isException': None,
207 'domInterface::isCallback': boolean_to_perl(false_to_none(self.is_ca llback)),
208 'domInterface::isPartial': false_to_none(self.is_partial),
209 'domInterface::name': self.name,
210 'domInterface::parent': self.parent,
211 }
212
213
214 class IdlException(BaseIdl):
215 def __init__(self, name=None, constants=None, operations=None, attributes=No ne, extended_attributes=None):
216 self.attributes = attributes or []
217 self.constants = constants or []
218 self.extended_attributes = extended_attributes or {}
219 self.operations = operations or []
220 self.name = name
221
222 def resolve_typedefs(self, typedefs):
223 for constant in self.constants:
224 constant.resolve_typedefs(typedefs)
225 for attribute in self.attributes:
226 attribute.resolve_typedefs(typedefs)
227 for operations in self.operations:
228 operations.resolve_typedefs(typedefs)
229
230 def json_serializable(self):
231 return {
232 # Perl code treats Exceptions as a kind of Interface
233 'domInterface::name': self.name,
234 'domInterface::attributes': self.attributes,
235 'domInterface::constants': self.constants,
236 'domInterface::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes),
237 'domInterface::functions': self.operations,
238 # These values don't vary for exceptions
239 'domInterface::constructors': [],
240 'domInterface::customConstructors': [],
241 'domInterface::isException': 1,
242 'domInterface::isCallback': None,
243 'domInterface::isPartial': None,
244 'domInterface::parent': None,
245 }
246
247
248 class IdlAttribute(BaseIdl, TypedObject):
249 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):
250 self.data_type = data_type
251 self.extended_attributes = extended_attributes or {}
252 self.getter_exceptions = getter_exceptions or []
253 self.is_nullable = is_nullable
254 self.is_static = is_static
255 self.is_read_only = is_read_only
256 self.name = name
257 self.setter_exceptions = setter_exceptions or []
258
259 def json_serializable(self):
260 return {
261 'domAttribute::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes),
262 'domAttribute::getterExceptions': self.getter_exceptions,
263 'domAttribute::isNullable': boolean_to_perl_quoted(false_to_none(sel f.is_nullable)),
264 'domAttribute::isReadOnly': boolean_to_perl(false_to_none(self.is_re ad_only)),
265 'domAttribute::isStatic': boolean_to_perl(false_to_none(self.is_stat ic)),
266 'domAttribute::name': self.name,
267 'domAttribute::setterExceptions': self.setter_exceptions,
268 'domAttribute::type': self.data_type,
269 }
270
271
272 class IdlConstant(BaseIdl, TypedObject):
273 def __init__(self, name=None, data_type=None, value=None, extended_attribute s=None):
274 self.data_type = data_type
275 self.extended_attributes = extended_attributes or {}
276 self.name = name
277 self.value = value
278
279 def json_serializable(self):
280 return {
281 'domConstant::extendedAttributes': none_to_value_is_missing(self.ext ended_attributes),
282 'domConstant::name': self.name,
283 'domConstant::type': self.data_type,
284 'domConstant::value': self.value,
285 }
286
287
288 class IdlOperation(BaseIdl, TypedObject):
289 def __init__(self, is_static=False, name=None, data_type=None, extended_attr ibutes=None, specials=None, arguments=None, overloaded_index=None):
290 self.is_static = is_static
291 self.name = name or ''
292 self.data_type = data_type
293 self.extended_attributes = extended_attributes or {}
294 self.specials = specials or []
295 self.arguments = arguments or []
296 self.overloaded_index = overloaded_index
297
298 def resolve_typedefs(self, typedefs):
299 TypedObject.resolve_typedefs(self, typedefs)
300 for argument in self.arguments:
301 argument.resolve_typedefs(typedefs)
302
303 def json_serializable(self):
304 return {
305 'domFunction::extendedAttributes': none_to_value_is_missing(self.ext ended_attributes),
306 'domFunction::isStatic': boolean_to_perl(false_to_none(self.is_stati c)),
307 'domFunction::name': self.name,
308 'domFunction::overloadedIndex': self.overloaded_index,
309 'domFunction::parameters': self.arguments,
310 'domFunction::specials': self.specials,
311 'domFunction::type': self.data_type,
312 }
313
314
315 class IdlArgument(BaseIdl, TypedObject):
316 def __init__(self, name=None, data_type=None, extended_attributes=None, is_o ptional=False, is_nullable=None, is_variadic=False):
317 self.data_type = data_type
318 self.extended_attributes = extended_attributes or {}
319 # FIXME: boolean values are inconsistent.
320 # The below hack is so that generated JSON is identical to
321 # Perl-generated JSON, where the exact values depend on the code path.
322 # False and None (Perl: 0 and undef) are semantically interchangeable,
323 # but yield different JSON.
324 # Once Perl removed, have all default to False.
325 if is_optional is None:
326 is_optional = False
327 if is_variadic is None:
328 is_variadic = False
329 self.is_nullable = is_nullable # (T?)
330 self.is_optional = is_optional # (optional T)
331 self.is_variadic = is_variadic # (T...)
332 self.name = name
333
334 def json_serializable(self):
335 return {
336 'domParameter::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes),
337 'domParameter::isNullable': boolean_to_perl_quoted(self.is_nullable) ,
338 'domParameter::isOptional': boolean_to_perl(self.is_optional),
339 'domParameter::isVariadic': boolean_to_perl(self.is_variadic),
340 'domParameter::name': self.name,
341 'domParameter::type': self.data_type,
342 }
343
344 # Type classes
345
346
347 class IdlType:
348 # FIXME: replace Type strings with these objects,
349 # so don't need to parse everywhere types are used.
350 # Types are stored internally as strings, not objects,
351 # e.g., as 'sequence<Foo>' or 'Foo[]',
352 # hence need to parse the string whenever a type is used.
353 # FIXME: incorporate Nullable, Variadic, etc.
354 # FIXME: properly should nest types
355 # Formally types are nested, e.g., short?[] vs. short[]?,
356 # but in practice these complex types aren't used and can treat
357 # as orthogonal properties.
358 def __init__(self, base_type, is_array=False, is_sequence=False):
359 if is_array and is_sequence:
360 raise ValueError('Array of Sequences are not allowed.')
361 self.base_type = base_type
362 self.is_array = is_array
363 self.is_sequence = is_sequence
364
365 def __str__(self):
366 type_string = self.base_type
367 if self.is_array:
368 return type_string + '[]'
369 if self.is_sequence:
370 return 'sequence<%s>' % type_string
371 return type_string
372
373 @classmethod
374 def from_string(cls, type_string):
375 sequence_re = r'^sequence<([^>]*)>$'
376 if type_string.endswith('[]'):
377 type_string = type_string[:-2]
378 sequence_match = re.match(sequence_re, type_string)
379 if sequence_match:
380 raise ValueError('Array of Sequences are not allowed.')
381 return cls(type_string, is_array=True)
382 sequence_match = re.match(sequence_re, type_string)
383 if sequence_match:
384 base_type = sequence_match.group(1)
385 return cls(base_type, is_sequence=True)
386 return cls(type_string)
387
388
389 class IdlTypedef:
390 # Internal to IDL parsing: typedefs are all translated during IdlObject
391 # construction, and the typedefs themselves not stored in the object.
392 def __init__(self, extended_attributes=None, data_type=None):
393 self.extended_attributes = extended_attributes or {}
394 self.data_type = data_type
395
396
397 class IdlUnionType(BaseIdl):
398 def __init__(self, union_member_types=None):
399 self.union_member_types = union_member_types or []
400
401 def json_serializable(self):
402 return {
403 'UnionType::unionMemberTypes': self.union_member_types,
404 }
405
406
407 # Perl JSON compatibility functions
408
409 def none_to_value_is_missing(extended_attributes):
410 # Perl IDL Parser uses 'VALUE_IS_MISSING' for null values in
411 # extended attributes, so add this as a filter when exporting to JSON.
412 new_extended_attributes = {}
413 for key, value in extended_attributes.iteritems():
414 if value is None:
415 new_extended_attributes[key] = 'VALUE_IS_MISSING'
416 else:
417 new_extended_attributes[key] = value
418 return new_extended_attributes
419
420
421 def boolean_to_perl(value):
422 # Perl stores booleans as 1, 0, or undefined (JSON null);
423 # convert to this format.
424 if value is None:
425 return None
426 return int(value)
427
428
429 def boolean_to_perl_quoted(value):
430 # Bug-for-bug compatibility with Perl.
431 # The value of isNullable is quoted ('1', '0', or undefined), rather than
432 # an integer, so add quotes.
433 if value is None:
434 return None
435 return str(int(value))
436
437
438 def false_to_none(value):
439 # The Perl parser generally uses undefined (Python None) rather than False
440 # for boolean flags, because the value is simply left undefined, rather than
441 # explicitly set to False.
442 if value is False:
443 return None
444 return value
445
446
447 # JSON export
448
449
450 class IdlEncoder(json.JSONEncoder):
451 def default(self, obj):
452 if isinstance(obj, BaseIdl):
453 return obj.json_serializable()
454 return json.JSONEncoder.default(self, obj)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698