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

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: Ready for review! (cleaner) 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.
haraken 2013/07/17 02:44:49 Given that we decided to keep deprecated_idl_parse
Nils Barth (inactive) 2013/07/17 12:05:09 Right; see separate detailed reply.
Nils Barth (inactive) 2013/07/17 12:13:46 Exactly, or more precisely: we still need the clas
32 """
33
34 # Disable attribute hiding check (else JSONEncoder default raises an error)
35 # pylint: disable=E0202
36 # pylint doesn't understand ABCs.
37 # pylint: disable=W0232, E0203, W0201
haraken 2013/07/16 14:17:51 Is this comment helpful?
Nils Barth (inactive) 2013/07/17 12:05:09 Yup, I get lots of pylint errors and warnings othe
38
39 import abc
40 import json
41 import os.path
42 import re
43
44
45 # Base classes
46
47
48 class BaseIdl:
49 """Abstract base class, used for JSON serialization."""
50 # FIXME: remove when Perl removed
51 __metaclass__ = abc.ABCMeta
haraken 2013/07/17 05:43:11 I'm a bit confused with the comments above. You ca
Nils Barth (inactive) 2013/07/17 12:05:09 Yup, but we don’t need the BaseIdl class, since it
52
53 @abc.abstractmethod
54 def json_serializable(self):
55 """Returns a JSON serializable form of the object.
56
57 This should be a dictionary, with keys scoped names of the form
58 Class::key, where the scope is the class name.
59 This is so we produce identical output to the Perl code, which uses
60 the Perl module JSON.pm, which uses this format.
61 """
62 pass
63
64
65 class TypedObject(BaseIdl):
haraken 2013/07/17 05:43:11 Actually I don't fully understand what class shoul
Nils Barth (inactive) 2013/07/17 12:05:09 BaseIdl is used for JSON export, so everything in
66 """Object with a Type, such as an Attribute or Operation (return value)."""
haraken 2013/07/21 14:31:50 You might also want to add "The type name can be m
Nils Barth (inactive) 2013/07/22 06:32:01 Good point: “type” can be a “Type” (actual type) o
67 __metaclass__ = abc.ABCMeta
haraken 2013/07/17 05:43:11 This wouldn't be needed.
Nils Barth (inactive) 2013/07/17 12:05:09 Setting metaclass to ABCMeta is needed to indicate
68 data_type = None
69 extended_attributes = {}
70
71 def apply_typedefs(self, typedefs):
72 """Applies Typedefs to object itself (e.g., return type of function).
73
74 Override for instance if calling on arguments of function."""
75 new_extended_attributes = {}
76 # Convert string representation to and from an IdlType object
77 # to handle parsing
78 data_type_object = IdlType.from_string(self.data_type)
79 base_type = data_type_object.base_type
80 if base_type in typedefs:
81 replacement_type = typedefs[base_type]
82 data_type_object.base_type = replacement_type.data_type
83 new_extended_attributes = replacement_type.extended_attributes
84 self.data_type = str(data_type_object)
85 self.extended_attributes.update(new_extended_attributes)
86 if new_extended_attributes:
87 raise ValueError('Extended attributes in a typedef are untested!')
haraken 2013/07/17 05:43:11 This will happen in bindings/tests/idls/TestTypede
Nils Barth (inactive) 2013/07/17 12:05:09 Got it, removed! (We don’t have examples in the ac
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):
haraken 2013/07/17 05:43:11 It looks like you're explicitly specifying all par
Nils Barth (inactive) 2013/07/17 12:05:09 (Long comment.) We need default parameters to hav
Nils Barth (inactive) 2013/07/17 12:17:01 (Continuing: default parameters needed for named p
haraken 2013/07/21 14:31:50 Agreed, thanks for the clarification. Let's keep t
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 if typedefs:
102 self.apply_typedefs(typedefs)
haraken 2013/07/17 05:43:11 Help me understand: Why do you need to call apply_
Nils Barth (inactive) 2013/07/17 12:05:09 Typedefs aren’t exposed by the bindings (they’re p
103
104 def apply_typedefs(self, typedefs):
105 for callback_function in self.callback_functions:
haraken 2013/07/17 05:43:11 callback_functions.itervalues(). The same comment
Nils Barth (inactive) 2013/07/17 12:05:09 Good point. It’s useful to access all of these by
106 callback_function.apply_typedefs(typedefs)
107 for exception in self.exceptions.itervalues():
108 exception.apply_typedefs(typedefs)
109 for interface in self.interfaces.itervalues():
110 interface.apply_typedefs(typedefs)
111
112 def json_serializable(self):
113 return {
114 'idlDocument::callbackFunctions': self.callback_functions,
115 'idlDocument::enumerations': self.enumerations,
116 'idlDocument::fileName': self.file_name,
117 # Perl treats exceptions as a kind of interface
118 'idlDocument::interfaces': sorted(self.exceptions.values() + sel f.interfaces.values()),
119 }
120
121 def to_json(self, debug=False):
122 """Returns a JSON string representing the Definitions.
123
124 The JSON output should be identical with the output of the Perl parser,
125 specifically the function serializeJSON in deprecated_idl_serializer.pm,
126 which takes a Perl object created by deprecated_idl_parser.pm.
127 """
128 # Sort so order consistent, allowing comparison of output
129 if debug:
130 # indent turns on pretty-printing for legibility
131 return json.dumps(self, cls=IdlEncoder, sort_keys=True, indent=4)
132 # Use compact separators so output identical to Perl
133 return json.dumps(self, cls=IdlEncoder, sort_keys=True, separators=(',', ':'))
134
135
136 class IdlCallbackFunction(TypedObject):
137 def __init__(self, name=None, data_type=None, arguments=None):
138 self.data_type = data_type
139 self.name = name
140 self.arguments = arguments or []
141
142 def apply_typedefs(self, typedefs):
143 TypedObject.apply_typedefs(self, typedefs)
144 for argument in self.arguments:
145 argument.apply_typedefs(typedefs)
146 raise ValueError('Typedefs in callback functions are untested!')
147
148 def json_serializable(self):
149 return {
150 'callbackFunction::name': self.name,
151 'callbackFunction::type': self.data_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, functions=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.functions = functions or []
176 self.is_callback = is_callback
177 self.is_partial = is_partial
178 self.name = name
179 self.parent = parent
180
181 def apply_typedefs(self, typedefs):
182 for attribute in self.attributes:
183 attribute.apply_typedefs(typedefs)
184 for constant in self.constants:
185 constant.apply_typedefs(typedefs)
186 for constructor in self.constructors:
187 constructor.apply_typedefs(typedefs)
188 for custom_constructor in self.custom_constructors:
189 custom_constructor.apply_typedefs(typedefs)
190 for function in self.functions:
191 function.apply_typedefs(typedefs)
192
193 def json_serializable(self):
194 return {
195 'domInterface::attributes': self.attributes,
196 'domInterface::constants': self.constants,
197 'domInterface::constructors': self.constructors,
198 'domInterface::customConstructors': self.custom_constructors,
199 'domInterface::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes),
200 'domInterface::functions': self.functions,
201 'domInterface::isException': None,
202 'domInterface::isCallback': boolean_to_perl(false_to_none(self.is_ca llback)),
203 'domInterface::isPartial': false_to_none(self.is_partial),
204 'domInterface::name': self.name,
205 'domInterface::parent': self.parent,
206 }
207
208
209 class IdlException(BaseIdl):
210 def __init__(self, name=None, constants=None, functions=None, attributes=Non e, extended_attributes=None):
211 self.attributes = attributes or []
212 self.constants = constants or []
213 self.extended_attributes = extended_attributes or {}
214 self.functions = functions or []
215 self.name = name
216
217 def apply_typedefs(self, typedefs):
218 for constant in self.constants:
219 constant.apply_typedefs(typedefs)
220 for attribute in self.attributes:
221 attribute.apply_typedefs(typedefs)
222 for function in self.functions:
223 function.apply_typedefs(typedefs)
224
225 def json_serializable(self):
226 return {
227 # Perl code treats Exceptions as a kind of Interface
228 'domInterface::name': self.name,
229 'domInterface::attributes': self.attributes,
230 'domInterface::constants': self.constants,
231 'domInterface::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes),
232 'domInterface::functions': self.functions,
233 # These values don't vary for exceptions
234 'domInterface::constructors': [],
235 'domInterface::customConstructors': [],
236 'domInterface::isException': 1,
237 'domInterface::isCallback': None,
238 'domInterface::isPartial': None,
239 'domInterface::parent': None,
240 }
241
242
243 class IdlAttribute(TypedObject):
244 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):
245 self.data_type = data_type
246 self.extended_attributes = extended_attributes or {}
247 self.getter_exceptions = getter_exceptions or []
248 self.is_nullable = is_nullable
249 self.is_static = is_static
250 self.is_read_only = is_read_only
251 self.name = name
252 self.setter_exceptions = setter_exceptions or []
253
254 def json_serializable(self):
255 return {
256 'domAttribute::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes),
257 'domAttribute::getterExceptions': self.getter_exceptions,
258 'domAttribute::isNullable': boolean_to_perl_quoted(false_to_none(sel f.is_nullable)),
259 'domAttribute::isReadOnly': boolean_to_perl(false_to_none(self.is_re ad_only)),
260 'domAttribute::isStatic': boolean_to_perl(false_to_none(self.is_stat ic)),
261 'domAttribute::name': self.name,
262 'domAttribute::setterExceptions': self.setter_exceptions,
263 'domAttribute::type': self.data_type,
264 }
265
266
267 class IdlConstant(TypedObject):
268 def __init__(self, name=None, data_type=None, value=None, extended_attribute s=None):
269 self.data_type = data_type
270 self.extended_attributes = extended_attributes or {}
271 self.name = name
272 self.value = value
273
274 def json_serializable(self):
275 return {
276 'domConstant::extendedAttributes': none_to_value_is_missing(self.ext ended_attributes),
277 'domConstant::name': self.name,
278 'domConstant::type': self.data_type,
279 'domConstant::value': self.value,
280 }
281
282
283 class IdlOperation(TypedObject):
284 def __init__(self, is_static=False, name=None, data_type=None, extended_attr ibutes=None, specials=None, arguments=None, overloaded_index=None):
285 self.is_static = is_static
286 self.name = name or ''
287 self.data_type = data_type
288 self.extended_attributes = extended_attributes or {}
289 self.specials = specials or []
290 self.arguments = arguments or []
291 self.overloaded_index = overloaded_index
292
293 def apply_typedefs(self, typedefs):
294 TypedObject.apply_typedefs(self, typedefs)
295 for argument in self.arguments:
296 argument.apply_typedefs(typedefs)
297
298 def json_serializable(self):
299 return {
300 'domFunction::extendedAttributes': none_to_value_is_missing(self.ext ended_attributes),
301 'domFunction::isStatic': boolean_to_perl(false_to_none(self.is_stati c)),
302 'domFunction::name': self.name,
303 'domFunction::overloadedIndex': self.overloaded_index,
304 'domFunction::parameters': self.arguments,
305 'domFunction::specials': self.specials,
306 'domFunction::type': self.data_type,
307 }
308
309
310 class IdlArgument(TypedObject):
311 def __init__(self, name=None, data_type=None, extended_attributes=None, is_o ptional=False, is_nullable=None, is_variadic=False):
312 self.data_type = data_type
313 self.extended_attributes = extended_attributes or {}
314 # FIXME: boolean values are inconsistent.
315 # The below hack is so that generated JSON is identical to
316 # Perl-generated JSON, where the exact values depend on the code path.
317 # False and None (Perl: 0 and undef) are semantically interchangeable,
318 # but yield different JSON.
319 # Once Perl removed, have all default to False.
320 if is_optional is None:
321 is_optional = False
322 if is_variadic is None:
323 is_variadic = False
324 self.is_nullable = is_nullable # (T?)
325 self.is_optional = is_optional # (optional T)
326 self.is_variadic = is_variadic # (T...)
327 self.name = name
328
329 def json_serializable(self):
330 return {
331 'domParameter::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes),
332 'domParameter::isNullable': boolean_to_perl_quoted(self.is_nullable) ,
333 'domParameter::isOptional': boolean_to_perl(self.is_optional),
334 'domParameter::isVariadic': boolean_to_perl(self.is_variadic),
335 'domParameter::name': self.name,
336 'domParameter::type': self.data_type,
337 }
338
339 # Type classes
340
341
342 class IdlType:
343 # FIXME: replace Type strings with these objects,
344 # so don't need to parse everywhere types are used.
345 # Types are stored internally as strings, not objects,
346 # e.g., as 'sequence<Foo>' or 'Foo[]',
347 # hence need to parse the string whenever a type is used.
348 # FIXME: incorporate Nullable, Variadic, etc.
349 # FIXME: properly should nest types
350 # Formally types are nested, e.g., short?[] vs. short[]?,
351 # but in practice these complex types aren't used and can treat
352 # as orthogonal properties.
353 def __init__(self, base_type=None, is_array=False, is_sequence=False):
354 if is_array and is_sequence:
355 raise ValueError('Array of Sequences not allowed.')
356 self.base_type = base_type
357 self.is_array = is_array
358 self.is_sequence = is_sequence
359
360 def __str__(self):
361 type_string = self.base_type
362 if self.is_array:
363 type_string += '[]'
364 if self.is_sequence:
365 type_string = 'sequence<%s>' % type_string
366 return type_string
367
368 @classmethod
369 def from_string(cls, type_string):
370 is_array = False
371 if type_string.endswith('[]'):
372 is_array = True
373 type_string = type_string[:-2]
374
haraken 2013/07/17 05:43:11 Shall we use if-else? if type_string.endwith('[]'
Nils Barth (inactive) 2013/07/17 12:05:09 You’re right, this code was a bit messy and duplic
375 is_sequence = False
376 sequence_re = r'^sequence<([^>]*)>$'
377 sequence_match = re.match(sequence_re, type_string)
378 if sequence_match:
379 is_sequence = True
380 type_string = sequence_match.group(1)
381 base_type = type_string
382 return cls(base_type=base_type, is_array=is_array, is_sequence=is_sequen ce)
383
384
385 class IdlTypedef:
386 # Internal to IDL parsing: typedefs are all translated during IdlObject
387 # construction, and the typedefs themselves not stored in the object.
388 def __init__(self, extended_attributes=None, data_type=None):
389 self.extended_attributes = extended_attributes or {}
390 self.data_type = data_type
391
392
393 class IdlUnionType(BaseIdl):
394 def __init__(self, union_member_types=None):
395 self.union_member_types = union_member_types or []
396
397 def json_serializable(self):
398 return {
399 'UnionType::unionMemberTypes': self.union_member_types,
400 }
401
402
403 # Perl JSON compatibility functions
404 # FIXME: remove when Perl removed
405
406 def none_to_value_is_missing(extended_attributes):
407 # Perl IDL Parser uses 'VALUE_IS_MISSING' for null values in
408 # extended attributes, so add this as a filter when exporting to JSON.
409 new_extended_attributes = {}
410 for key, value in extended_attributes.iteritems():
411 if value is None:
412 new_extended_attributes[key] = 'VALUE_IS_MISSING'
413 else:
414 new_extended_attributes[key] = value
415 return new_extended_attributes
416
417
418 def boolean_to_perl(value):
419 # Perl stores booleans as 1, 0, or undefined (JSON null);
420 # convert to this format.
421 if value is None:
422 return None
423 return int(value)
424
425
426 def boolean_to_perl_quoted(value):
427 # Bug-for-bug compatibility with Perl.
428 # The value of isNullable is quoted ('1', '0', or undefined), rather than
429 # an integer, so add quotes.
430 if value is None:
431 return None
432 return str(int(value))
433
434
435 def false_to_none(value):
436 # The Perl parser generally uses undefined (Python None) rather than False
437 # for boolean flags, because the value is simply left undefined, rather than
438 # explicitly set to False.
439 if value is False:
440 return None
441 return value
442
443
444 # JSON export
445 # FIXME: remove when Perl removed, as then no longer using JSON as
446 # an intermediate format.
447
448
449 class IdlEncoder(json.JSONEncoder):
450 def default(self, obj):
451 if isinstance(obj, BaseIdl):
452 return obj.json_serializable()
453 return json.JSONEncoder.default(self, obj)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698