OLD | NEW |
---|---|
(Empty) | |
1 # Copyright (C) 2013 Google Inc. All rights reserved. | |
dominicc (has gone to gerrit)
2013/06/26 04:20:53
Your comments need work.
Fragments do not work we
Nils Barth (inactive)
2013/06/26 04:43:37
Will do.
| |
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 | |
dominicc (has gone to gerrit)
2013/06/26 04:20:53
End the first line of a docstring comment with a p
| |
30 | |
31 Also JSON export. | |
32 """ | |
33 | |
34 # Disable attribute hiding check (else JSONEncoder default raises an error) | |
35 # pylint: disable=E0202 | |
36 | |
37 import abc | |
38 import json | |
39 import os.path | |
40 import re | |
41 | |
42 | |
43 # Base classes | |
44 | |
45 | |
46 class BaseIdl(): | |
47 """Abstract base class, used for JSON serialization.""" | |
48 __metaclass__ = abc.ABCMeta | |
49 | |
50 @abc.abstractmethod | |
51 def to_json(self): | |
dominicc (has gone to gerrit)
2013/06/26 04:20:53
Is there a better name for this? It doesn't return
Nils Barth (inactive)
2013/06/26 04:43:37
"json_serializable" is more precise; will change.
| |
52 """Return a serializable form of the object. | |
dominicc (has gone to gerrit)
2013/06/26 04:20:53
Return -> Returns
| |
53 | |
54 Compatible with Perl IR and JSON import. | |
dominicc (has gone to gerrit)
2013/06/26 04:20:53
Wrap text; separate paragraphs with blank lines. Y
dominicc (has gone to gerrit)
2013/06/26 04:20:53
This documentation needs to be improved.
"Compati
| |
55 In practice a dictionary, whose keys specify the class.""" | |
56 pass | |
dominicc (has gone to gerrit)
2013/06/26 04:20:53
It would be better to raise an exception here. If
Nils Barth (inactive)
2013/06/26 04:43:37
The ABC (Abstract Base Class) library handles this
| |
57 | |
58 | |
59 class TypedIdlObject(BaseIdl): | |
dominicc (has gone to gerrit)
2013/06/26 04:20:53
If this class handles typedefs, maybe it should ha
| |
60 """Auxiliary class for handling typedefs.""" | |
dominicc (has gone to gerrit)
2013/06/26 04:20:53
Auxiliary is a meaningless word without more conte
| |
61 data_type = None | |
62 extended_attributes = {} | |
dominicc (has gone to gerrit)
2013/06/26 04:20:53
Won't this hash object be shared by all instances
Nils Barth (inactive)
2013/06/26 04:43:37
(Just added this to silence lint errors; probably
| |
63 | |
64 def apply_typedefs(self, typedefs): | |
65 """Applies typedefs to object itself (e.g., return type of function). | |
66 | |
67 Override if calling on parameters as well.""" | |
68 new_extended_attributes = {} | |
69 # Convert string representation to and from a Type object | |
70 # to handle parsing | |
71 data_type_object = Type.from_string(self.data_type) | |
72 base_type = data_type_object.base_type | |
73 if base_type in typedefs: | |
74 replacement_type = typedefs[base_type] | |
75 data_type_object.base_type = replacement_type.data_type | |
76 new_extended_attributes = replacement_type.extended_attributes | |
77 self.data_type = str(data_type_object) | |
78 self.extended_attributes.update(new_extended_attributes) | |
79 if new_extended_attributes: | |
80 raise ValueError('Extended attributes in a typedef are untested!') | |
81 | |
82 | |
83 # IDL classes | |
84 | |
85 | |
86 class IdlDocument(BaseIdl): | |
87 def __init__(self, callback_functions=None, enumerations=None, file_name=Non e, interfaces=None, typedefs=None): | |
88 self.callback_functions = callback_functions or [] | |
89 self.enumerations = enumerations or [] | |
90 if file_name: | |
91 self.file_name = os.path.abspath(file_name) | |
92 self.interfaces = interfaces or [] | |
93 if typedefs: | |
94 self.apply_typedefs(typedefs) | |
95 | |
96 def apply_typedefs(self, typedefs): | |
97 for callback_function in self.callback_functions: | |
98 callback_function.apply_typedefs(typedefs) | |
99 for interface in self.interfaces: | |
100 interface.apply_typedefs(typedefs) | |
101 | |
102 def to_json(self): | |
103 return { | |
104 'idlDocument::callbackFunctions': self.callback_functions, | |
105 'idlDocument::enumerations': self.enumerations, | |
106 'idlDocument::fileName': self.file_name, | |
107 'idlDocument::interfaces': self.interfaces, | |
108 } | |
109 | |
110 | |
111 class CallbackFunction(TypedIdlObject): | |
112 def __init__(self, name=None, data_type=None, parameters=None): | |
113 """parameters: List of DomParameters""" | |
114 self.data_type = data_type | |
115 self.name = name or "" # FIXME: is "" needed? | |
116 self.parameters = parameters or [] | |
117 | |
118 def apply_typedefs(self, typedefs): | |
119 TypedIdlObject.apply_typedefs(self, typedefs) | |
120 for parameter in self.parameters: | |
121 parameter.apply_typedefs(typedefs) | |
122 raise ValueError('Typedefs in CallbackFunctions are untested!') | |
123 | |
124 def to_json(self): | |
125 return { | |
126 'callbackFunction::name': self.name, | |
127 'callbackFunction::type': self.data_type, | |
128 'callbackFunction::parameters': self.parameters, | |
129 } | |
130 | |
131 | |
132 class DomEnum(BaseIdl): | |
133 def __init__(self, name=None, values=None): | |
134 """name: enumeration identifier | |
135 values: enumeration values, list of unique strings | |
136 """ | |
137 self.name = name | |
138 self.values = values or [] | |
139 | |
140 def to_json(self): | |
141 return { | |
142 'domEnum::name': self.name, | |
143 'domEnum::values': self.values, | |
144 } | |
145 | |
146 | |
147 class DomInterface(BaseIdl): | |
148 def __init__(self, name=None, parents=None, constants=None, functions=None, attributes=None, extended_attributes=None, constructors=None, custom_constructor s=None, is_exception=None, is_callback=None, is_partial=None): | |
149 """ | |
150 attributes: list of DomAttributes | |
151 constants: list of DomConstants | |
152 constructors: list of DomFunctions | |
153 custom_constructors: list of DomFunctions | |
154 functions: list of DomFunctions | |
155 is_exception: used for exceptions | |
156 parents: list of strings | |
157 """ | |
158 self.attributes = attributes or [] | |
159 self.constants = constants or [] | |
160 self.constructors = constructors or [] | |
161 self.custom_constructors = custom_constructors or [] | |
162 self.extended_attributes = extended_attributes or {} | |
163 self.functions = functions or [] | |
164 self.is_exception = is_exception | |
165 self.is_callback = is_callback | |
166 self.is_partial = is_partial | |
167 self.name = name | |
168 self.parents = parents or [] | |
169 | |
170 def apply_typedefs(self, typedefs): | |
171 for constant in self.constants: | |
172 constant.apply_typedefs(typedefs) | |
173 for attribute in self.attributes: | |
174 attribute.apply_typedefs(typedefs) | |
175 for function in self.functions: | |
176 function.apply_typedefs(typedefs) | |
177 for constructor in self.constructors: | |
178 constructor.apply_typedefs(typedefs) | |
179 for custom_constructor in self.custom_constructors: | |
180 custom_constructor.apply_typedefs(typedefs) | |
181 | |
182 def to_json(self): | |
183 return { | |
184 'domInterface::name': self.name, | |
185 'domInterface::parents': self.parents, | |
186 'domInterface::constants': self.constants, | |
187 'domInterface::functions': self.functions, | |
188 'domInterface::attributes': self.attributes, | |
189 'domInterface::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes), | |
190 'domInterface::constructors': self.constructors, | |
191 'domInterface::customConstructors': self.custom_constructors, | |
192 'domInterface::isException': boolean_to_perl(self.is_exception), | |
193 'domInterface::isCallback': boolean_to_perl(self.is_callback), | |
194 'domInterface::isPartial': self.is_partial, | |
195 } | |
196 | |
197 | |
198 class DomAttribute(TypedIdlObject): | |
199 def __init__(self, data_type=None, name=None, is_nullable=None, is_static=No ne, is_read_only=None, getter_exceptions=None, setter_exceptions=None, extended_ attributes=None): | |
200 """data_type: Attribute type (including namespace), string or UnionType | |
201 is_nullable: (T?) | |
202 getter_exceptions: Possibly raised exceptions | |
203 setter_exceptions: Possibly raised exceptions | |
204 """ | |
205 self.data_type = data_type | |
206 self.extended_attributes = extended_attributes or {} | |
207 self.getter_exceptions = getter_exceptions or [] | |
208 self.is_nullable = is_nullable | |
209 self.is_static = is_static | |
210 self.is_read_only = is_read_only | |
211 self.name = name | |
212 self.setter_exceptions = setter_exceptions or [] | |
213 | |
214 def to_json(self): | |
215 return { | |
216 'domAttribute::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes), | |
217 'domAttribute::getterExceptions': self.getter_exceptions, | |
218 'domAttribute::isNullable': self.is_nullable, | |
219 'domAttribute::isReadOnly': self.is_read_only, | |
220 'domAttribute::isStatic': self.is_static, | |
221 'domAttribute::name': self.name, | |
222 'domAttribute::setterExceptions': self.setter_exceptions, | |
223 'domAttribute::type': self.data_type, | |
224 } | |
225 | |
226 | |
227 class DomConstant(TypedIdlObject): | |
228 def __init__(self, name=None, data_type=None, value=None, extended_attribute s=None): | |
229 self.data_type = data_type | |
230 self.extended_attributes = extended_attributes or {} | |
231 self.name = name | |
232 self.value = value | |
233 | |
234 def to_json(self): | |
235 return { | |
236 'domConstant::extendedAttributes': none_to_value_is_missing(self.ext ended_attributes), | |
237 'domConstant::name': self.name, | |
238 'domConstant::type': self.data_type, | |
239 'domConstant::value': self.value, | |
240 } | |
241 | |
242 | |
243 class DomFunction(TypedIdlObject): | |
244 def __init__(self, is_static=None, name=None, data_type=None, extended_attri butes=None, specials=None, parameters=None, overloaded_index=None): | |
245 """parameters: List of DomParameters""" | |
246 self.is_static = is_static | |
247 self.name = name or "" | |
248 self.data_type = data_type | |
249 self.extended_attributes = extended_attributes or {} | |
250 self.specials = specials or [] | |
251 self.parameters = parameters or [] | |
252 self.overloaded_index = overloaded_index | |
253 | |
254 def apply_typedefs(self, typedefs): | |
255 TypedIdlObject.apply_typedefs(self, typedefs) | |
256 for parameter in self.parameters: | |
257 parameter.apply_typedefs(typedefs) | |
258 | |
259 def to_json(self): | |
260 return { | |
261 'domFunction::extendedAttributes': none_to_value_is_missing(self.ext ended_attributes), | |
262 'domFunction::isStatic': boolean_to_perl(self.is_static), | |
263 'domFunction::name': self.name, | |
264 'domFunction::overloadedIndex': self.overloaded_index, | |
265 'domFunction::parameters': self.parameters, | |
266 'domFunction::specials': self.specials, | |
267 'domFunction::type': self.data_type, | |
268 } | |
269 | |
270 | |
271 class DomParameter(TypedIdlObject): | |
272 def __init__(self, name=None, data_type=None, extended_attributes=None, is_o ptional=False, is_nullable=None, is_variadic=False): | |
273 """Used to represent a map of 'variable name' <-> 'variable type' | |
274 | |
275 data_type: string or UnionType | |
276 is_optional: (optional T) | |
277 is_nullable: (T?) | |
278 is_variadic: (long... numbers) | |
279 """ | |
280 self.data_type = data_type | |
281 self.extended_attributes = extended_attributes or {} | |
282 # FIXME: boolean values are inconsistent (due to Perl), | |
283 # being sometimes True, False, or None (Perl: 1, 0, undef) | |
284 # Should all default to False, and be either True or False | |
285 if is_optional is None: | |
286 is_optional = False | |
287 if is_variadic is None: | |
288 is_variadic = False | |
289 self.is_nullable = is_nullable | |
290 # FIXME: can these be in to_json instead? | |
291 self.is_optional = boolean_to_perl(is_optional) | |
292 self.is_variadic = boolean_to_perl(is_variadic) | |
293 self.name = name | |
294 | |
295 def to_json(self): | |
296 return { | |
297 'domParameter::extendedAttributes': none_to_value_is_missing(self.ex tended_attributes), | |
298 'domParameter::isNullable': self.is_nullable, | |
299 'domParameter::isOptional': self.is_optional, | |
300 'domParameter::isVariadic': self.is_variadic, | |
301 'domParameter::name': self.name, | |
302 'domParameter::type': self.data_type, | |
303 } | |
304 | |
305 # Type classes | |
306 | |
307 | |
308 class Type(): | |
309 # FIXME: replace Type strings with these objects, | |
310 # so don't need to parse everywhere types are used. | |
311 # Types are stored internally as strings, not objects, | |
312 # e.g., as 'sequence<Foo>' or 'Foo[]', | |
313 # hence need to parse the string whenever a type is used. | |
314 # FIXME: incorporate Nullable, Variadic, etc. | |
315 # FIXME: properly should nest types | |
316 # Formally types are nested, e.g., short?[] vs. short[]?, | |
317 # but in practice these complex types aren't used and can treat | |
318 # as orthogonal properties. | |
319 def __init__(self, base_type=None, is_array=False, is_sequence=False): | |
320 if is_array and is_sequence: | |
321 raise ValueError('Array of Sequences not allowed.') | |
322 self.base_type = base_type | |
323 self.is_array = is_array | |
324 self.is_sequence = is_sequence | |
325 | |
326 def __str__(self): | |
327 type_string = self.base_type | |
328 if self.is_array: | |
329 type_string += '[]' | |
330 if self.is_sequence: | |
331 type_string = 'sequence<%s>' % type_string | |
332 return type_string | |
333 | |
334 @classmethod | |
335 def from_string(cls, type_string): | |
336 is_array = False | |
337 if type_string.endswith('[]'): | |
338 is_array = True | |
339 type_string = type_string[:-2] | |
340 | |
341 is_sequence = False | |
342 sequence_re = r'^sequence<([^>]*)>$' | |
343 sequence_match = re.match(sequence_re, type_string) | |
344 if sequence_match: | |
345 is_sequence = True | |
346 type_string = sequence_match.group(1) | |
347 base_type = type_string | |
348 return cls(base_type=base_type, is_array=is_array, is_sequence=is_sequen ce) | |
349 | |
350 | |
351 class Typedef(): | |
352 # Not exposed in bindings, internal to IDL parsing | |
353 def __init__(self, extended_attributes=None, data_type=None): | |
354 self.extended_attributes = extended_attributes or {} | |
355 self.data_type = data_type | |
356 | |
357 | |
358 class UnionType(BaseIdl): | |
359 def __init__(self, union_member_types=None): | |
360 """union_member_types: list of string or UnionType""" | |
361 self.union_member_types = union_member_types or [] | |
362 | |
363 def to_json(self): | |
364 return { | |
365 'UnionType::unionMemberTypes': self.union_member_types, | |
366 } | |
367 | |
368 | |
369 # Perl JSON compatiblity functions | |
370 # FIXME: remove when Perl removed | |
371 | |
372 def none_to_value_is_missing(extended_attributes): | |
373 # Perl IDL Parser uses 'VALUE_IS_MISSING' for null values in | |
374 # extended attributes, so add this as a filter when exporting to JSON. | |
375 new_extended_attributes = {} | |
376 for key, value in extended_attributes.iteritems(): | |
377 if value is None: | |
378 new_extended_attributes[key] = 'VALUE_IS_MISSING' | |
379 else: | |
380 new_extended_attributes[key] = value | |
381 return new_extended_attributes | |
382 | |
383 | |
384 def boolean_to_perl(value): | |
385 # Perl stores booleans as 1, 0, or undefined (JSON null); | |
386 # convert to this format | |
387 if value is None: | |
388 return None | |
389 return int(value) | |
390 | |
391 # JSON export | |
392 # FIXME: remove when Perl removed | |
393 # (so no longer using JSON as intermediate format) | |
394 | |
395 | |
396 class IdlEncoder(json.JSONEncoder): | |
397 def default(self, obj): | |
398 if isinstance(obj, BaseIdl): | |
399 return obj.to_json() | |
400 return json.JSONEncoder.default(self, obj) | |
401 | |
402 | |
403 def ir_to_json(ir, debug=False): | |
404 if debug: | |
405 # More legible | |
406 return json.dumps(ir, cls=IdlEncoder, sort_keys=True, indent=4) | |
407 return json.dumps(ir, cls=IdlEncoder, sort_keys=True, separators=(',', ':')) | |
OLD | NEW |