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