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