1 # Copyright (C) 2013 Google Inc. All rights reserved.
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
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
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.
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.
29 """Blink IDL Intermediate Representation (IR) classes.
31 Classes are primarily constructors, which build an IdlDefinitions object
32 (and various contained objects) from an AST (produced by blink_idl_parser).
35 * Constructors walk the AST, creating objects.
38 Typedefs are all resolved here, and not stored in IR.
40 Typedef resolution uses some auxiliary classes and OOP techniques to make this
41 a generic call, via the resolve_typedefs() method.
43 Class hierarchy (mostly containment, '<' for inheritance):
46 IdlCallbackFunction < TypedObject
47 IdlEnum :: FIXME: remove, just use a dict for enums
49 IdlAttribute < TypedObject
50 IdlConstant < TypedObject
51 IdlOperation < TypedObject
52 IdlArgument < TypedObject
53 IdlException < IdlInterface
54 (same contents as IdlInterface)
56 TypedObject :: mixin for typedef resolution
58 Design doc: http://www.chromium.org/developers/design-documents/idl-compiler
63 from idl_types import IdlType, IdlUnionType
65 SPECIAL_KEYWORD_LIST = ['GETTER', 'SETTER', 'DELETER']
67 # http://www.w3.org/TR/WebIDL/#common-DOMTimeStamp
68 'DOMTimeStamp': 'unsigned long long',
72 ################################################################################
73 # TypedObject (mixin for typedef resolution)
74 ################################################################################
76 class TypedObject(object):
77 """Object with a type, such as an Attribute or Operation (return value).
79 The type can be an actual type, or can be a typedef, which must be resolved
80 before passing data to the code generator.
82 __metaclass__ = abc.ABCMeta
85 def resolve_typedefs(self, typedefs):
86 """Resolve typedefs to actual types in the object."""
87 # Constructors don't have their own return type, because it's the
91 # Need to re-assign self.idl_type, not just mutate idl_type,
92 # since type(idl_type) may change.
93 self.idl_type = self.idl_type.resolve_typedefs(typedefs)
96 ################################################################################
97 # Definitions (main container class)
98 ################################################################################
100 class IdlDefinitions(object):
101 def __init__(self, node):
102 """Args: node: AST root node, class == 'File'"""
103 self.callback_functions = {}
104 self.dictionaries = {}
105 self.enumerations = {}
108 node_class = node.GetClass()
109 if node_class != 'File':
110 raise ValueError('Unrecognized node class: %s' % node_class)
112 typedefs = dict((typedef_name, IdlType(type_name))
113 for typedef_name, type_name in
114 STANDARD_TYPEDEFS.iteritems())
116 children = node.GetChildren()
117 for child in children:
118 child_class = child.GetClass()
119 if child_class == 'Interface':
120 interface = IdlInterface(child)
121 self.interfaces[interface.name] = interface
122 elif child_class == 'Exception':
123 exception = IdlException(child)
124 # For simplicity, treat exceptions as interfaces
125 self.interfaces[exception.name] = exception
126 elif child_class == 'Typedef':
127 type_name = child.GetName()
128 typedefs[type_name] = typedef_node_to_type(child)
129 elif child_class == 'Enum':
130 enumeration = IdlEnum(child)
131 self.enumerations[enumeration.name] = enumeration
132 elif child_class == 'Callback':
133 callback_function = IdlCallbackFunction(child)
134 self.callback_functions[callback_function.name] = callback_function
135 elif child_class == 'Implements':
136 # Implements is handled at the interface merging step
138 elif child_class == 'Dictionary':
139 dictionary = IdlDictionary(child)
140 self.dictionaries[dictionary.name] = dictionary
142 raise ValueError('Unrecognized node class: %s' % child_class)
144 # Typedefs are not stored in IR:
145 # Resolve typedefs with the actual types and then discard the Typedefs.
146 # http://www.w3.org/TR/WebIDL/#idl-typedefs
147 self.resolve_typedefs(typedefs)
149 def resolve_typedefs(self, typedefs):
150 for callback_function in self.callback_functions.itervalues():
151 callback_function.resolve_typedefs(typedefs)
152 for interface in self.interfaces.itervalues():
153 interface.resolve_typedefs(typedefs)
155 def update(self, other):
156 """Update with additional IdlDefinitions."""
157 for interface_name, new_interface in other.interfaces.iteritems():
158 if not new_interface.is_partial:
159 # Add as new interface
160 self.interfaces[interface_name] = new_interface
163 # Merge partial to existing interface
165 self.interfaces[interface_name].merge(new_interface)
167 raise Exception('Tried to merge partial interface for {0}, '
168 'but no existing interface by that name'
169 .format(interface_name))
171 # Merge callbacks and enumerations
172 self.enumerations.update(other.enumerations)
173 self.callback_functions.update(other.callback_functions)
176 ################################################################################
178 ################################################################################
180 class IdlCallbackFunction(TypedObject):
181 def __init__(self, node):
182 children = node.GetChildren()
183 num_children = len(children)
184 if num_children != 2:
185 raise ValueError('Expected 2 children, got %s' % num_children)
186 type_node, arguments_node = children
187 arguments_node_class = arguments_node.GetClass()
188 if arguments_node_class != 'Arguments':
189 raise ValueError('Expected Arguments node, got %s' % arguments_node_class)
191 self.name = node.GetName()
192 self.idl_type = type_node_to_type(type_node)
193 self.arguments = arguments_node_to_arguments(arguments_node)
195 def resolve_typedefs(self, typedefs):
196 TypedObject.resolve_typedefs(self, typedefs)
197 for argument in self.arguments:
198 argument.resolve_typedefs(typedefs)
201 ################################################################################
203 ################################################################################
205 class IdlDictionary(object):
206 def __init__(self, node):
208 self.name = node.GetName()
210 for child in node.GetChildren():
211 child_class = child.GetClass()
212 if child_class == 'Inherit':
213 self.parent = child.GetName()
214 elif child_class == 'Key':
215 self.members.append(IdlDictionaryMember(child))
217 raise ValueError('Unrecognized node class: %s' % child_class)
220 class IdlDictionaryMember(object):
221 def __init__(self, node):
222 self.default_value = None
223 self.extended_attributes = {}
225 self.name = node.GetName()
226 for child in node.GetChildren():
227 child_class = child.GetClass()
228 if child_class == 'Type':
229 self.idl_type = type_node_to_type(child)
230 elif child_class == 'Default':
231 self.default_value = child.GetProperty('VALUE')
232 elif child_class == 'ExtAttributes':
233 self.extended_attributes = ext_attributes_node_to_extended_attributes(child)
235 raise ValueError('Unrecognized node class: %s' % child_class)
238 ################################################################################
240 ################################################################################
242 class IdlEnum(object):
243 # FIXME: remove, just treat enums as a dictionary
244 def __init__(self, node):
245 self.name = node.GetName()
247 for child in node.GetChildren():
248 self.values.append(child.GetName())
251 ################################################################################
252 # Interfaces and Exceptions
253 ################################################################################
255 class IdlInterface(object):
256 def __init__(self, node=None):
259 self.constructors = []
260 self.custom_constructors = []
261 self.extended_attributes = {}
264 if not node: # Early exit for IdlException.__init__
267 self.is_callback = node.GetProperty('CALLBACK') or False
268 self.is_exception = False
269 # FIXME: uppercase 'Partial' => 'PARTIAL' in base IDL parser
270 self.is_partial = node.GetProperty('Partial') or False
271 self.name = node.GetName()
273 children = node.GetChildren()
274 for child in children:
275 child_class = child.GetClass()
276 if child_class == 'Attribute':
277 self.attributes.append(IdlAttribute(child))
278 elif child_class == 'Const':
279 self.constants.append(IdlConstant(child))
280 elif child_class == 'ExtAttributes':
281 extended_attributes = ext_attributes_node_to_extended_attributes(child)
282 self.constructors, self.custom_constructors = (
283 extended_attributes_to_constructors(extended_attributes))
284 clear_constructor_attributes(extended_attributes)
285 self.extended_attributes = extended_attributes
286 elif child_class == 'Operation':
287 self.operations.append(IdlOperation(child))
288 elif child_class == 'Inherit':
289 self.parent = child.GetName()
291 raise ValueError('Unrecognized node class: %s' % child_class)
293 def resolve_typedefs(self, typedefs):
294 for attribute in self.attributes:
295 attribute.resolve_typedefs(typedefs)
296 for constant in self.constants:
297 constant.resolve_typedefs(typedefs)
298 for constructor in self.constructors:
299 constructor.resolve_typedefs(typedefs)
300 for custom_constructor in self.custom_constructors:
301 custom_constructor.resolve_typedefs(typedefs)
302 for operation in self.operations:
303 operation.resolve_typedefs(typedefs)
305 def merge(self, other):
306 """Merge in another interface's members (e.g., partial interface)"""
307 self.attributes.extend(other.attributes)
308 self.constants.extend(other.constants)
309 self.operations.extend(other.operations)
312 class IdlException(IdlInterface):
313 # Properly exceptions and interfaces are distinct, and thus should inherit a
314 # common base class (say, "IdlExceptionOrInterface").
315 # However, there is only one exception (DOMException), and new exceptions
316 # are not expected. Thus it is easier to implement exceptions as a
317 # restricted subclass of interfaces.
318 # http://www.w3.org/TR/WebIDL/#idl-exceptions
319 def __init__(self, node):
320 # Exceptions are similar to Interfaces, but simpler
321 IdlInterface.__init__(self)
322 self.is_callback = False
323 self.is_exception = True
324 self.is_partial = False
325 self.name = node.GetName()
327 children = node.GetChildren()
328 for child in children:
329 child_class = child.GetClass()
330 if child_class == 'Attribute':
331 attribute = IdlAttribute(child)
332 self.attributes.append(attribute)
333 elif child_class == 'Const':
334 self.constants.append(IdlConstant(child))
335 elif child_class == 'ExtAttributes':
336 self.extended_attributes = ext_attributes_node_to_extended_attributes(child)
337 elif child_class == 'ExceptionOperation':
338 self.operations.append(IdlOperation.from_exception_operation_node(child))
340 raise ValueError('Unrecognized node class: %s' % child_class)
343 ################################################################################
345 ################################################################################
347 class IdlAttribute(TypedObject):
348 def __init__(self, node):
349 self.is_read_only = node.GetProperty('READONLY') or False
350 self.is_static = node.GetProperty('STATIC') or False
351 self.name = node.GetName()
352 # Defaults, overridden below
354 self.extended_attributes = {}
356 children = node.GetChildren()
357 for child in children:
358 child_class = child.GetClass()
359 if child_class == 'Type':
360 self.idl_type = type_node_to_type(child)
361 elif child_class == 'ExtAttributes':
362 self.extended_attributes = ext_attributes_node_to_extended_attributes(child)
364 raise ValueError('Unrecognized node class: %s' % child_class)
367 ################################################################################
369 ################################################################################
371 class IdlConstant(TypedObject):
372 def __init__(self, node):
373 children = node.GetChildren()
374 num_children = len(children)
375 if num_children < 2 or num_children > 3:
376 raise ValueError('Expected 2 or 3 children, got %s' % num_children)
377 type_node = children[0]
378 value_node = children[1]
379 value_node_class = value_node.GetClass()
380 if value_node_class != 'Value':
381 raise ValueError('Expected Value node, got %s' % value_node_class)
383 self.name = node.GetName()
384 # ConstType is more limited than Type, so subtree is smaller and
385 # we don't use the full type_node_to_type function.
386 self.idl_type = type_node_inner_to_type(type_node)
387 self.value = value_node.GetName()
389 if num_children == 3:
390 ext_attributes_node = children[2]
391 self.extended_attributes = ext_attributes_node_to_extended_attributes(ext_attributes_node)
393 self.extended_attributes = {}
396 ################################################################################
398 ################################################################################
400 class IdlOperation(TypedObject):
401 def __init__(self, node=None):
403 self.extended_attributes = {}
407 self.is_static = False
409 self.name = node.GetName() # FIXME: should just be: or ''
410 # FIXME: AST should use None internally
411 if self.name == '_unnamed_':
414 self.is_static = node.GetProperty('STATIC') or False
415 property_dictionary = node.GetProperties()
416 for special_keyword in SPECIAL_KEYWORD_LIST:
417 if special_keyword in property_dictionary:
418 self.specials.append(special_keyword.lower())
421 children = node.GetChildren()
422 for child in children:
423 child_class = child.GetClass()
424 if child_class == 'Arguments':
425 self.arguments = arguments_node_to_arguments(child)
426 elif child_class == 'Type':
427 self.idl_type = type_node_to_type(child)
428 elif child_class == 'ExtAttributes':
429 self.extended_attributes = ext_attributes_node_to_extended_attributes(child)
431 raise ValueError('Unrecognized node class: %s' % child_class)
434 def from_exception_operation_node(cls, node):
435 # Needed to handle one case in DOMException.idl:
436 # // Override in a Mozilla compatible format
437 # [NotEnumerable] DOMString toString();
438 # FIXME: can we remove this? replace with a stringifier?
440 operation.name = node.GetName()
441 children = node.GetChildren()
442 if len(children) < 1 or len(children) > 2:
443 raise ValueError('ExceptionOperation node with %s children, expected 1 or 2' % len(children))
445 type_node = children[0]
446 operation.idl_type = type_node_to_type(type_node)
448 if len(children) > 1:
449 ext_attributes_node = children[1]
450 operation.extended_attributes = ext_attributes_node_to_extended_attributes(ext_attributes_node)
455 def constructor_from_arguments_node(cls, name, arguments_node):
457 constructor.name = name
458 constructor.arguments = arguments_node_to_arguments(arguments_node)
461 def resolve_typedefs(self, typedefs):
462 TypedObject.resolve_typedefs(self, typedefs)
463 for argument in self.arguments:
464 argument.resolve_typedefs(typedefs)
467 ################################################################################
469 ################################################################################
471 class IdlArgument(TypedObject):
472 def __init__(self, node):
473 self.extended_attributes = {}
475 self.is_optional = node.GetProperty('OPTIONAL') # syntax: (optional T)
476 self.is_variadic = False # syntax: (T...)
477 self.name = node.GetName()
479 children = node.GetChildren()
480 for child in children:
481 child_class = child.GetClass()
482 if child_class == 'Type':
483 self.idl_type = type_node_to_type(child)
484 elif child_class == 'ExtAttributes':
485 self.extended_attributes = ext_attributes_node_to_extended_attributes(child)
486 elif child_class == 'Argument':
487 child_name = child.GetName()
488 if child_name != '...':
489 raise ValueError('Unrecognized Argument node; expected "...", got "%s"' % child_name)
490 self.is_variadic = child.GetProperty('ELLIPSIS') or False
492 raise ValueError('Unrecognized node class: %s' % child_class)
495 def arguments_node_to_arguments(node):
496 # [Constructor] and [CustomConstructor] without arguments (the bare form)
497 # have None instead of an arguments node, but have the same meaning as using
498 # an empty argument list, [Constructor()], so special-case this.
499 # http://www.w3.org/TR/WebIDL/#Constructor
502 return [IdlArgument(argument_node)
503 for argument_node in node.GetChildren()]
506 ################################################################################
507 # Extended attributes
508 ################################################################################
510 def ext_attributes_node_to_extended_attributes(node):
513 Dictionary of {ExtAttributeName: ExtAttributeValue}.
514 Value is usually a string, with three exceptions:
515 Constructors: value is a list of Arguments nodes, corresponding to
516 possible signatures of the constructor.
517 CustomConstructors: value is a list of Arguments nodes, corresponding to
518 possible signatures of the custom constructor.
519 NamedConstructor: value is a Call node, corresponding to the single
520 signature of the named constructor.
522 # Primarily just make a dictionary from the children.
523 # The only complexity is handling various types of constructors:
524 # Constructors and Custom Constructors can have duplicate entries due to
525 # overloading, and thus are stored in temporary lists.
526 # However, Named Constructors cannot be overloaded, and thus do not have
528 # FIXME: move Constructor logic into separate function, instead of modifying
529 # extended attributes in-place.
531 custom_constructors = []
532 extended_attributes = {}
534 def child_node(extended_attribute_node):
535 children = extended_attribute_node.GetChildren()
538 if len(children) > 1:
539 raise ValueError('ExtAttributes node with %s children, expected at most 1' % len(children))
542 extended_attribute_node_list = node.GetChildren()
543 for extended_attribute_node in extended_attribute_node_list:
544 name = extended_attribute_node.GetName()
545 child = child_node(extended_attribute_node)
546 child_class = child and child.GetClass()
547 if name == 'Constructor':
548 if child_class and child_class != 'Arguments':
549 raise ValueError('Constructor only supports Arguments as child, but has child of class: %s' % child_class)
550 constructors.append(child)
551 elif name == 'CustomConstructor':
552 if child_class and child_class != 'Arguments':
553 raise ValueError('[CustomConstructor] only supports Arguments as child, but has child of class: %s' % child_class)
554 custom_constructors.append(child)
555 elif name == 'NamedConstructor':
556 if child_class and child_class != 'Call':
557 raise ValueError('[NamedConstructor] only supports Call as child, but has child of class: %s' % child_class)
558 extended_attributes[name] = child
559 elif name == 'SetWrapperReferenceTo':
561 raise ValueError('[SetWrapperReferenceTo] requires a child, but has none.')
562 if child_class != 'Arguments':
563 raise ValueError('[SetWrapperReferenceTo] only supports Arguments as child, but has child of class: %s' % child_class)
564 extended_attributes[name] = arguments_node_to_arguments(child)
566 raise ValueError('ExtAttributes node with unexpected children: %s' % name)
568 value = extended_attribute_node.GetProperty('VALUE')
569 extended_attributes[name] = value
571 # Store constructors and custom constructors in special list attributes,
572 # which are deleted later. Note plural in key.
574 extended_attributes['Constructors'] = constructors
575 if custom_constructors:
576 extended_attributes['CustomConstructors'] = custom_constructors
578 return extended_attributes
581 def extended_attributes_to_constructors(extended_attributes):
582 """Returns constructors and custom_constructors (lists of IdlOperations).
584 Auxiliary function for IdlInterface.__init__.
587 constructor_list = extended_attributes.get('Constructors', [])
589 IdlOperation.constructor_from_arguments_node('Constructor', arguments_node)
590 for arguments_node in constructor_list]
592 custom_constructor_list = extended_attributes.get('CustomConstructors', [])
593 custom_constructors = [
594 IdlOperation.constructor_from_arguments_node('CustomConstructor', arguments_node)
595 for arguments_node in custom_constructor_list]
597 if 'NamedConstructor' in extended_attributes:
598 # FIXME: support overloaded named constructors, and make homogeneous
599 name = 'NamedConstructor'
600 call_node = extended_attributes['NamedConstructor']
601 extended_attributes['NamedConstructor'] = call_node.GetName()
602 children = call_node.GetChildren()
603 if len(children) != 1:
604 raise ValueError('NamedConstructor node expects 1 child, got %s.' % len(children))
605 arguments_node = children[0]
606 named_constructor = IdlOperation.constructor_from_arguments_node('NamedConstructor', arguments_node)
607 # FIXME: should return named_constructor separately; appended for Perl
608 constructors.append(named_constructor)
610 return constructors, custom_constructors
613 def clear_constructor_attributes(extended_attributes):
614 # Deletes Constructor*s* (plural), sets Constructor (singular)
615 if 'Constructors' in extended_attributes:
616 del extended_attributes['Constructors']
617 extended_attributes['Constructor'] = None
618 if 'CustomConstructors' in extended_attributes:
619 del extended_attributes['CustomConstructors']
620 extended_attributes['CustomConstructor'] = None
623 ################################################################################
625 ################################################################################
627 def type_node_to_type(node):
628 children = node.GetChildren()
629 if len(children) < 1 or len(children) > 2:
630 raise ValueError('Type node expects 1 or 2 children (type + optional array []), got %s (multi-dimensional arrays are not supported).' % len(children))
632 type_node_child = children[0]
634 if len(children) == 2:
635 array_node = children[1]
636 array_node_class = array_node.GetClass()
637 if array_node_class != 'Array':
638 raise ValueError('Expected Array node as TypeSuffix, got %s node.' % array_node_class)
639 # FIXME: use IdlArrayType instead of is_array, once have that
644 is_nullable = node.GetProperty('NULLABLE') or False # syntax: T?
646 return type_node_inner_to_type(type_node_child, is_array=is_array, is_nullable=is_nullable)
649 def type_node_inner_to_type(node, is_array=False, is_nullable=False):
650 # FIXME: remove is_array and is_nullable once have IdlArrayType and IdlNullableType
651 node_class = node.GetClass()
652 # Note Type*r*ef, not Typedef, meaning the type is an identifier, thus
653 # either a typedef shorthand (but not a Typedef declaration itself) or an
654 # interface type. We do not distinguish these, and just use the type name.
655 if node_class in ['PrimitiveType', 'Typeref']:
656 # unrestricted syntax: unrestricted double | unrestricted float
657 is_unrestricted = node.GetProperty('UNRESTRICTED') or False
658 return IdlType(node.GetName(), is_array=is_array, is_nullable=is_nullable, is_unrestricted=is_unrestricted)
659 elif node_class == 'Any':
660 return IdlType('any', is_array=is_array, is_nullable=is_nullable)
661 elif node_class == 'Sequence':
663 raise ValueError('Arrays of sequences are not supported')
664 return sequence_node_to_type(node, is_nullable=is_nullable)
665 elif node_class == 'UnionType':
667 raise ValueError('Arrays of unions are not supported')
668 return union_type_node_to_idl_union_type(node, is_nullable=is_nullable)
669 raise ValueError('Unrecognized node class: %s' % node_class)
672 def sequence_node_to_type(node, is_nullable=False):
673 children = node.GetChildren()
674 if len(children) != 1:
675 raise ValueError('Sequence node expects exactly 1 child, got %s' % len(children))
676 sequence_child = children[0]
677 sequence_child_class = sequence_child.GetClass()
678 if sequence_child_class != 'Type':
679 raise ValueError('Unrecognized node class: %s' % sequence_child_class)
680 element_type = type_node_to_type(sequence_child).base_type
681 return IdlType(element_type, is_sequence=True, is_nullable=is_nullable)
684 def typedef_node_to_type(node):
685 children = node.GetChildren()
686 if len(children) != 1:
687 raise ValueError('Typedef node with %s children, expected 1' % len(children))
689 child_class = child.GetClass()
690 if child_class != 'Type':
691 raise ValueError('Unrecognized node class: %s' % child_class)
692 return type_node_to_type(child)
695 def union_type_node_to_idl_union_type(node, is_nullable=False):
696 member_types = [type_node_to_type(member_type_node)
697 for member_type_node in node.GetChildren()]
698 return IdlUnionType(member_types, is_nullable=is_nullable)