OLD | NEW |
| (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 """Builds an IdlDefinitions object from an AST (produced by blink_idl_parser).""
" | |
30 | |
31 import os | |
32 | |
33 from idl_definitions import IdlDefinitions, IdlInterface, IdlException, IdlOpera
tion, IdlCallbackFunction, IdlArgument, IdlAttribute, IdlConstant, IdlEnum, IdlU
nionType | |
34 | |
35 SPECIAL_KEYWORD_LIST = ['GETTER', 'SETTER', 'DELETER'] | |
36 STANDARD_TYPEDEFS = { | |
37 # http://www.w3.org/TR/WebIDL/#common-DOMTimeStamp | |
38 'DOMTimeStamp': 'unsigned long long', | |
39 } | |
40 | |
41 def build_idl_definitions_from_ast(node): | |
42 if node is None: | |
43 return None | |
44 node_class = node.GetClass() | |
45 if node_class != 'File': | |
46 raise ValueError('Unrecognized node class: %s' % node_class) | |
47 return file_node_to_idl_definitions(node) | |
48 | |
49 | |
50 def file_node_to_idl_definitions(node): | |
51 callback_functions = {} | |
52 enumerations = {} | |
53 interfaces = {} | |
54 typedefs = STANDARD_TYPEDEFS | |
55 | |
56 # FIXME: only needed for Perl, remove later | |
57 file_name = os.path.abspath(node.GetName()) | |
58 | |
59 children = node.GetChildren() | |
60 for child in children: | |
61 child_class = child.GetClass() | |
62 if child_class == 'Interface': | |
63 interface = interface_node_to_idl_interface(child) | |
64 interfaces[interface.name] = interface | |
65 elif child_class == 'Exception': | |
66 exception = exception_node_to_idl_exception(child) | |
67 # For simplicity, treat exceptions as interfaces | |
68 interfaces[exception.name] = exception | |
69 elif child_class == 'Typedef': | |
70 type_name = child.GetName() | |
71 typedefs[type_name] = typedef_node_to_type(child) | |
72 elif child_class == 'Enum': | |
73 enumeration = enum_node_to_idl_enum(child) | |
74 enumerations[enumeration.name] = enumeration | |
75 elif child_class == 'Callback': | |
76 callback_function = callback_node_to_idl_callback_function(child) | |
77 callback_functions[callback_function.name] = callback_function | |
78 elif child_class == 'Implements': | |
79 # Implements is handled at the interface merging step | |
80 pass | |
81 else: | |
82 raise ValueError('Unrecognized node class: %s' % child_class) | |
83 | |
84 return IdlDefinitions(callback_functions=callback_functions, enumerations=en
umerations, file_name=file_name, interfaces=interfaces, typedefs=typedefs) | |
85 | |
86 # Constructors for Interface definitions and interface members | |
87 | |
88 | |
89 def interface_node_to_idl_interface(node): | |
90 attributes = [] | |
91 constants = [] | |
92 constructors = None | |
93 custom_constructors = None | |
94 extended_attributes = None | |
95 operations = [] | |
96 is_callback = node.GetProperty('CALLBACK') or False | |
97 # FIXME: uppercase 'Partial' => 'PARTIAL' in base IDL parser | |
98 is_partial = node.GetProperty('Partial') or False | |
99 name = node.GetName() | |
100 parent = None | |
101 | |
102 children = node.GetChildren() | |
103 for child in children: | |
104 child_class = child.GetClass() | |
105 if child_class == 'Attribute': | |
106 attributes.append(attribute_node_to_idl_attribute(child)) | |
107 elif child_class == 'Const': | |
108 constants.append(constant_node_to_idl_constant(child)) | |
109 elif child_class == 'ExtAttributes': | |
110 extended_attributes = ext_attributes_node_to_extended_attributes(chi
ld) | |
111 constructors, custom_constructors = extended_attributes_to_construct
ors(extended_attributes) | |
112 clear_constructor_attributes(extended_attributes) | |
113 elif child_class == 'Operation': | |
114 operations.append(operation_node_to_idl_operation(child)) | |
115 elif child_class == 'Inherit': | |
116 parent = child.GetName() | |
117 else: | |
118 raise ValueError('Unrecognized node class: %s' % child_class) | |
119 | |
120 return IdlInterface(name=name, attributes=attributes, constants=constants, c
onstructors=constructors, custom_constructors=custom_constructors, extended_attr
ibutes=extended_attributes, operations=operations, is_callback=is_callback, is_p
artial=is_partial, parent=parent) | |
121 | |
122 | |
123 def attribute_node_to_idl_attribute(node): | |
124 idl_type = None | |
125 extended_attributes = {} | |
126 is_nullable = False | |
127 is_read_only = node.GetProperty('READONLY') or False | |
128 is_static = node.GetProperty('STATIC') or False | |
129 name = node.GetName() | |
130 | |
131 children = node.GetChildren() | |
132 for child in children: | |
133 child_class = child.GetClass() | |
134 if child_class == 'Type': | |
135 idl_type = type_node_to_type(child) | |
136 is_nullable = child.GetProperty('NULLABLE') or False | |
137 elif child_class == 'ExtAttributes': | |
138 extended_attributes = ext_attributes_node_to_extended_attributes(chi
ld) | |
139 else: | |
140 raise ValueError('Unrecognized node class: %s' % child_class) | |
141 | |
142 return IdlAttribute(idl_type=idl_type, extended_attributes=extended_attribut
es, is_nullable=is_nullable, is_read_only=is_read_only, is_static=is_static, nam
e=name) | |
143 | |
144 | |
145 def constant_node_to_idl_constant(node): | |
146 name = node.GetName() | |
147 | |
148 children = node.GetChildren() | |
149 num_children = len(children) | |
150 if num_children < 2 or num_children > 3: | |
151 raise ValueError('Expected 2 or 3 children, got %s' % num_children) | |
152 | |
153 type_node = children[0] | |
154 # ConstType is more limited than Type, so subtree is smaller and we don't | |
155 # use the full type_node_to_type function. | |
156 idl_type = type_node_inner_to_type(type_node) | |
157 | |
158 value_node = children[1] | |
159 value_node_class = value_node.GetClass() | |
160 if value_node_class != 'Value': | |
161 raise ValueError('Expected Value node, got %s' % value_node_class) | |
162 value = value_node.GetName() | |
163 | |
164 extended_attributes = None | |
165 if num_children == 3: | |
166 ext_attributes_node = children[2] | |
167 extended_attributes = ext_attributes_node_to_extended_attributes(ext_att
ributes_node) | |
168 | |
169 return IdlConstant(idl_type=idl_type, extended_attributes=extended_attribute
s, name=name, value=value) | |
170 | |
171 | |
172 def operation_node_to_idl_operation(node): | |
173 name = node.GetName() | |
174 # FIXME: AST should use None internally | |
175 if name == '_unnamed_': | |
176 name = None | |
177 | |
178 is_static = node.GetProperty('STATIC') or False | |
179 specials = [] | |
180 property_dictionary = node.GetProperties() | |
181 for special_keyword in SPECIAL_KEYWORD_LIST: | |
182 if special_keyword in property_dictionary: | |
183 specials.append(special_keyword.lower()) | |
184 | |
185 extended_attributes = None | |
186 arguments = [] | |
187 return_type = None | |
188 children = node.GetChildren() | |
189 for child in children: | |
190 child_class = child.GetClass() | |
191 if child_class == 'Arguments': | |
192 arguments = arguments_node_to_arguments(child) | |
193 elif child_class == 'Type': | |
194 return_type = type_node_to_type(child) | |
195 elif child_class == 'ExtAttributes': | |
196 extended_attributes = ext_attributes_node_to_extended_attributes(chi
ld) | |
197 else: | |
198 raise ValueError('Unrecognized node class: %s' % child_class) | |
199 | |
200 return IdlOperation(name=name, idl_type=return_type, extended_attributes=ext
ended_attributes, is_static=is_static, arguments=arguments, specials=specials) | |
201 | |
202 | |
203 def arguments_node_to_arguments(node): | |
204 # [Constructor] and [CustomConstructor] without arguments (the bare form) | |
205 # have None instead of an arguments node, but have the same meaning as using | |
206 # an empty argument list, [Constructor()], so special-case this. | |
207 # http://www.w3.org/TR/WebIDL/#Constructor | |
208 if node is None: | |
209 return [] | |
210 arguments = [] | |
211 argument_node_list = node.GetChildren() | |
212 for argument_node in argument_node_list: | |
213 arguments.append(argument_node_to_idl_argument(argument_node)) | |
214 return arguments | |
215 | |
216 | |
217 def argument_node_to_idl_argument(node): | |
218 name = node.GetName() | |
219 | |
220 idl_type = None | |
221 extended_attributes = {} | |
222 # FIXME: Boolean values are inconsistent due to Perl compatibility. | |
223 # Make all default to False once Perl removed. | |
224 is_nullable = False | |
225 is_optional = node.GetProperty('OPTIONAL') | |
226 is_variadic = None | |
227 children = node.GetChildren() | |
228 for child in children: | |
229 child_class = child.GetClass() | |
230 if child_class == 'Type': | |
231 idl_type = type_node_to_type(child) | |
232 # FIXME: Doesn't handle nullable arrays (Foo[]?), and arrays of | |
233 # nullable (Foo?[]) are treated as nullable arrays. No actual use. | |
234 is_nullable = child.GetProperty('NULLABLE') | |
235 elif child_class == 'ExtAttributes': | |
236 extended_attributes = ext_attributes_node_to_extended_attributes(chi
ld) | |
237 elif child_class == 'Argument': | |
238 child_name = child.GetName() | |
239 if child_name != '...': | |
240 raise ValueError('Unrecognized Argument node; expected "...", go
t "%s"' % child_name) | |
241 is_variadic = child.GetProperty('ELLIPSIS') or False | |
242 else: | |
243 raise ValueError('Unrecognized node class: %s' % child_class) | |
244 | |
245 return IdlArgument(name=name, idl_type=idl_type, extended_attributes=extende
d_attributes, is_nullable=is_nullable, is_optional=is_optional, is_variadic=is_v
ariadic) | |
246 | |
247 # Constructors for for non-interface definitions | |
248 | |
249 | |
250 def callback_node_to_idl_callback_function(node): | |
251 name = node.GetName() | |
252 children = node.GetChildren() | |
253 num_children = len(children) | |
254 if num_children != 2: | |
255 raise ValueError('Expected 2 children, got %s' % num_children) | |
256 | |
257 type_node = children[0] | |
258 idl_type = type_node_to_type(type_node) | |
259 | |
260 arguments_node = children[1] | |
261 arguments_node_class = arguments_node.GetClass() | |
262 if arguments_node_class != 'Arguments': | |
263 raise ValueError('Expected Value node, got %s' % arguments_node_class) | |
264 arguments = arguments_node_to_arguments(arguments_node) | |
265 | |
266 return IdlCallbackFunction(name=name, idl_type=idl_type, arguments=arguments
) | |
267 | |
268 | |
269 def enum_node_to_idl_enum(node): | |
270 name = node.GetName() | |
271 values = [] | |
272 for child in node.GetChildren(): | |
273 values.append(child.GetName()) | |
274 return IdlEnum(name=name, values=values) | |
275 | |
276 | |
277 def exception_operation_node_to_idl_operation(node): | |
278 # Needed to handle one case in DOMException.idl: | |
279 # // Override in a Mozilla compatible format | |
280 # [NotEnumerable] DOMString toString(); | |
281 # FIXME: can we remove this? replace with a stringifier? | |
282 extended_attributes = {} | |
283 name = node.GetName() | |
284 children = node.GetChildren() | |
285 if len(children) < 1 or len(children) > 2: | |
286 raise ValueError('ExceptionOperation node with %s children, expected 1 o
r 2' % len(children)) | |
287 | |
288 type_node = children[0] | |
289 return_type = type_node_to_type(type_node) | |
290 | |
291 if len(children) > 1: | |
292 ext_attributes_node = children[1] | |
293 extended_attributes = ext_attributes_node_to_extended_attributes(ext_att
ributes_node) | |
294 | |
295 return IdlOperation(name=name, idl_type=return_type, extended_attributes=ext
ended_attributes) | |
296 | |
297 | |
298 def exception_node_to_idl_exception(node): | |
299 # Exceptions are similar to Interfaces, but simpler | |
300 attributes = [] | |
301 constants = [] | |
302 extended_attributes = None | |
303 operations = [] | |
304 name = node.GetName() | |
305 | |
306 children = node.GetChildren() | |
307 for child in children: | |
308 child_class = child.GetClass() | |
309 if child_class == 'Attribute': | |
310 attribute = attribute_node_to_idl_attribute(child) | |
311 attributes.append(attribute) | |
312 elif child_class == 'Const': | |
313 constants.append(constant_node_to_idl_constant(child)) | |
314 elif child_class == 'ExtAttributes': | |
315 extended_attributes = ext_attributes_node_to_extended_attributes(chi
ld) | |
316 elif child_class == 'ExceptionOperation': | |
317 operations.append(exception_operation_node_to_idl_operation(child)) | |
318 else: | |
319 raise ValueError('Unrecognized node class: %s' % child_class) | |
320 | |
321 return IdlException(name=name, attributes=attributes, constants=constants, e
xtended_attributes=extended_attributes, operations=operations) | |
322 | |
323 | |
324 def typedef_node_to_type(node): | |
325 children = node.GetChildren() | |
326 if len(children) != 1: | |
327 raise ValueError('Typedef node with %s children, expected 1' % len(child
ren)) | |
328 child = children[0] | |
329 child_class = child.GetClass() | |
330 if child_class != 'Type': | |
331 raise ValueError('Unrecognized node class: %s' % child_class) | |
332 return type_node_to_type(child) | |
333 | |
334 # Extended attributes | |
335 | |
336 | |
337 def ext_attributes_node_to_extended_attributes(node): | |
338 """ | |
339 Returns: | |
340 Dictionary of {ExtAttributeName: ExtAttributeValue}. | |
341 Value is usually a string, with three exceptions: | |
342 Constructors: value is a list of Arguments nodes, corresponding to | |
343 possibly signatures of the constructor. | |
344 CustomConstructors: value is a list of Arguments nodes, corresponding to | |
345 possibly signatures of the custom constructor. | |
346 NamedConstructor: value is a Call node, corresponding to the single | |
347 signature of the named constructor. | |
348 """ | |
349 # Primarily just make a dictionary from the children. | |
350 # The only complexity is handling various types of constructors: | |
351 # Constructors and Custom Constructors can have duplicate entries due to | |
352 # overloading, and thus are stored in temporary lists. | |
353 # However, Named Constructors cannot be overloaded, and thus do not have | |
354 # a list. | |
355 # FIXME: move Constructor logic into separate function, instead of modifying | |
356 # extended attributes in-place. | |
357 constructors = [] | |
358 custom_constructors = [] | |
359 extended_attributes = {} | |
360 | |
361 def child_node(extended_attribute_node): | |
362 children = extended_attribute_node.GetChildren() | |
363 if not children: | |
364 return None | |
365 if len(children) > 1: | |
366 raise ValueError('ExtAttributes node with %s children, expected at m
ost 1' % len(children)) | |
367 return children[0] | |
368 | |
369 extended_attribute_node_list = node.GetChildren() | |
370 for extended_attribute_node in extended_attribute_node_list: | |
371 name = extended_attribute_node.GetName() | |
372 child = child_node(extended_attribute_node) | |
373 child_class = child and child.GetClass() | |
374 if name == 'Constructor': | |
375 if child_class and child_class != 'Arguments': | |
376 raise ValueError('Constructor only supports Arguments as child,
but has child of class: %s' % child_class) | |
377 constructors.append(child) | |
378 elif name == 'CustomConstructor': | |
379 if child_class and child_class != 'Arguments': | |
380 raise ValueError('[CustomConstructor] only supports Arguments as
child, but has child of class: %s' % child_class) | |
381 custom_constructors.append(child) | |
382 elif name == 'NamedConstructor': | |
383 if child_class and child_class != 'Call': | |
384 raise ValueError('[NamedConstructor] only supports Call as child
, but has child of class: %s' % child_class) | |
385 extended_attributes[name] = child | |
386 elif name == 'SetWrapperReferenceTo': | |
387 if not child: | |
388 raise ValueError('[SetWrapperReferenceTo] requires a child, but
has none.') | |
389 if child_class != 'Arguments': | |
390 raise ValueError('[SetWrapperReferenceTo] only supports Argument
s as child, but has child of class: %s' % child_class) | |
391 extended_attributes[name] = arguments_node_to_arguments(child) | |
392 elif child: | |
393 raise ValueError('ExtAttributes node with unexpected children: %s' %
name) | |
394 else: | |
395 value = extended_attribute_node.GetProperty('VALUE') | |
396 extended_attributes[name] = value | |
397 | |
398 # Store constructors and custom constructors in special list attributes, | |
399 # which are deleted later. Note plural in key. | |
400 if constructors: | |
401 extended_attributes['Constructors'] = constructors | |
402 if custom_constructors: | |
403 extended_attributes['CustomConstructors'] = custom_constructors | |
404 | |
405 return extended_attributes | |
406 | |
407 | |
408 def extended_attributes_to_constructors(extended_attributes): | |
409 """Returns constructors and custom_constructors (lists of IdlOperations). | |
410 | |
411 Auxiliary function for interface_node_to_idl_interface. | |
412 """ | |
413 constructors = [] | |
414 custom_constructors = [] | |
415 if 'Constructors' in extended_attributes: | |
416 constructor_list = extended_attributes['Constructors'] | |
417 # If not overloaded, have index 0, otherwise index from 1 | |
418 overloaded_index = 0 if len(constructor_list) == 1 else 1 | |
419 for arguments_node in constructor_list: | |
420 name = 'Constructor' | |
421 arguments = arguments_node_to_arguments(arguments_node) | |
422 constructor = IdlOperation(name=name, extended_attributes=extended_a
ttributes, overloaded_index=overloaded_index, arguments=arguments) | |
423 constructors.append(constructor) | |
424 overloaded_index += 1 | |
425 | |
426 if 'CustomConstructors' in extended_attributes: | |
427 custom_constructor_list = extended_attributes['CustomConstructors'] | |
428 # If not overloaded, have index 0, otherwise index from 1 | |
429 overloaded_index = 0 if len(custom_constructor_list) == 1 else 1 | |
430 for arguments_node in custom_constructor_list: | |
431 name = 'CustomConstructor' | |
432 arguments = arguments_node_to_arguments(arguments_node) | |
433 custom_constructor = IdlOperation(name=name, extended_attributes=ext
ended_attributes, overloaded_index=overloaded_index, arguments=arguments) | |
434 custom_constructors.append(custom_constructor) | |
435 overloaded_index += 1 | |
436 | |
437 if 'NamedConstructor' in extended_attributes: | |
438 name = 'NamedConstructor' | |
439 call_node = extended_attributes['NamedConstructor'] | |
440 extended_attributes['NamedConstructor'] = call_node.GetName() | |
441 overloaded_index = None # named constructors are not overloaded | |
442 children = call_node.GetChildren() | |
443 if len(children) != 1: | |
444 raise ValueError('NamedConstructor node expects 1 child, got %s.' %
len(children)) | |
445 arguments_node = children[0] | |
446 arguments = arguments_node_to_arguments(arguments_node) | |
447 named_constructor = IdlOperation(name=name, extended_attributes=extended
_attributes, overloaded_index=overloaded_index, arguments=arguments) | |
448 # FIXME: should return named_constructor separately; appended for Perl | |
449 constructors.append(named_constructor) | |
450 | |
451 return constructors, custom_constructors | |
452 | |
453 | |
454 def clear_constructor_attributes(extended_attributes): | |
455 # Deletes Constructor*s* (plural), sets Constructor (singular) | |
456 if 'Constructors' in extended_attributes: | |
457 del extended_attributes['Constructors'] | |
458 extended_attributes['Constructor'] = None | |
459 if 'CustomConstructors' in extended_attributes: | |
460 del extended_attributes['CustomConstructors'] | |
461 extended_attributes['CustomConstructor'] = None | |
462 | |
463 | |
464 # Types | |
465 | |
466 | |
467 def type_node_to_type(node): | |
468 children = node.GetChildren() | |
469 if len(children) < 1 or len(children) > 2: | |
470 raise ValueError('Type node expects 1 or 2 children (type + optional arr
ay []), got %s (multi-dimensional arrays are not supported).' % len(children)) | |
471 | |
472 type_node_child = children[0] | |
473 idl_type = type_node_inner_to_type(type_node_child) | |
474 | |
475 if len(children) == 2: | |
476 array_node = children[1] | |
477 array_node_class = array_node.GetClass() | |
478 if array_node_class != 'Array': | |
479 raise ValueError('Expected Array node as TypeSuffix, got %s node.' %
array_node_class) | |
480 idl_type += '[]' | |
481 | |
482 return idl_type | |
483 | |
484 | |
485 def type_node_inner_to_type(node): | |
486 node_class = node.GetClass() | |
487 # Note Type*r*ef, not Typedef, meaning the type is an identifier, thus | |
488 # either a typedef shorthand (but not a Typedef declaration itself) or an | |
489 # interface type. We do not distinguish these, and just use the type name. | |
490 if node_class in ['PrimitiveType', 'Typeref']: | |
491 return node.GetName() | |
492 elif node_class == 'Any': | |
493 return 'any' | |
494 elif node_class == 'Sequence': | |
495 return sequence_node_to_type(node) | |
496 elif node_class == 'UnionType': | |
497 return union_type_node_to_idl_union_type(node) | |
498 raise ValueError('Unrecognized node class: %s' % node_class) | |
499 | |
500 | |
501 def sequence_node_to_type(node): | |
502 children = node.GetChildren() | |
503 if len(children) != 1: | |
504 raise ValueError('Sequence node expects exactly 1 child, got %s' % len(c
hildren)) | |
505 sequence_child = children[0] | |
506 sequence_child_class = sequence_child.GetClass() | |
507 if sequence_child_class != 'Type': | |
508 raise ValueError('Unrecognized node class: %s' % sequence_child_class) | |
509 sequence_type = type_node_to_type(sequence_child) | |
510 return 'sequence<%s>' % sequence_type | |
511 | |
512 | |
513 def union_type_node_to_idl_union_type(node): | |
514 union_member_types = [] | |
515 for member_type_node in node.GetChildren(): | |
516 member_type = type_node_to_type(member_type_node) | |
517 union_member_types.append(member_type) | |
518 return IdlUnionType(union_member_types=union_member_types) | |
OLD | NEW |