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

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

Powered by Google App Engine
This is Rietveld 408576698