Home | History | Annotate | Download | only in scripts
      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 """Blink IDL Intermediate Representation (IR) classes.
     30 
     31 Classes are primarily constructors, which build an IdlDefinitions object
     32 (and various contained objects) from an AST (produced by blink_idl_parser).
     33 
     34 This is in two steps:
     35 * Constructors walk the AST, creating objects.
     36 * Typedef resolution.
     37 
     38 Typedefs are all resolved here, and not stored in IR.
     39 
     40 Typedef resolution uses some auxiliary classes and OOP techniques to make this
     41 a generic call, via the resolve_typedefs() method.
     42 
     43 Class hierarchy (mostly containment, '<' for inheritance):
     44 
     45 IdlDefinitions
     46     IdlCallbackFunction < TypedObject
     47     IdlEnum :: FIXME: remove, just use a dict for enums
     48     IdlInterface
     49         IdlAttribute < TypedObject
     50         IdlConstant < TypedObject
     51         IdlLiteral
     52         IdlOperation < TypedObject
     53             IdlArgument < TypedObject
     54         IdlStringifier
     55     IdlException < IdlInterface
     56         (same contents as IdlInterface)
     57 
     58 TypedObject :: mixin for typedef resolution
     59 
     60 Design doc: http://www.chromium.org/developers/design-documents/idl-compiler
     61 """
     62 
     63 import abc
     64 
     65 from idl_types import IdlType, IdlUnionType, IdlArrayType, IdlSequenceType, IdlNullableType
     66 
     67 SPECIAL_KEYWORD_LIST = ['GETTER', 'SETTER', 'DELETER']
     68 STANDARD_TYPEDEFS = {
     69     # http://www.w3.org/TR/WebIDL/#common-DOMTimeStamp
     70     'DOMTimeStamp': 'unsigned long long',
     71 }
     72 
     73 
     74 ################################################################################
     75 # TypedObject (mixin for typedef resolution)
     76 ################################################################################
     77 
     78 class TypedObject(object):
     79     """Object with a type, such as an Attribute or Operation (return value).
     80 
     81     The type can be an actual type, or can be a typedef, which must be resolved
     82     before passing data to the code generator.
     83     """
     84     __metaclass__ = abc.ABCMeta
     85     idl_type = None
     86 
     87     def resolve_typedefs(self, typedefs):
     88         """Resolve typedefs to actual types in the object."""
     89         # Constructors don't have their own return type, because it's the
     90         # interface itself.
     91         if not self.idl_type:
     92             return
     93         # Need to re-assign self.idl_type, not just mutate idl_type,
     94         # since type(idl_type) may change.
     95         self.idl_type = self.idl_type.resolve_typedefs(typedefs)
     96 
     97 
     98 ################################################################################
     99 # Definitions (main container class)
    100 ################################################################################
    101 
    102 class IdlDefinitions(object):
    103     def __init__(self, idl_name, node):
    104         """Args: node: AST root node, class == 'File'"""
    105         self.callback_functions = {}
    106         self.dictionaries = {}
    107         self.enumerations = {}
    108         self.interfaces = {}
    109         self.idl_name = idl_name
    110 
    111         node_class = node.GetClass()
    112         if node_class != 'File':
    113             raise ValueError('Unrecognized node class: %s' % node_class)
    114 
    115         typedefs = dict((typedef_name, IdlType(type_name))
    116                         for typedef_name, type_name in
    117                         STANDARD_TYPEDEFS.iteritems())
    118 
    119         children = node.GetChildren()
    120         for child in children:
    121             child_class = child.GetClass()
    122             if child_class == 'Interface':
    123                 interface = IdlInterface(idl_name, child)
    124                 self.interfaces[interface.name] = interface
    125             elif child_class == 'Exception':
    126                 exception = IdlException(idl_name, child)
    127                 # For simplicity, treat exceptions as interfaces
    128                 self.interfaces[exception.name] = exception
    129             elif child_class == 'Typedef':
    130                 type_name = child.GetName()
    131                 typedefs[type_name] = typedef_node_to_type(child)
    132             elif child_class == 'Enum':
    133                 enumeration = IdlEnum(idl_name, child)
    134                 self.enumerations[enumeration.name] = enumeration
    135             elif child_class == 'Callback':
    136                 callback_function = IdlCallbackFunction(idl_name, child)
    137                 self.callback_functions[callback_function.name] = callback_function
    138             elif child_class == 'Implements':
    139                 # Implements is handled at the interface merging step
    140                 pass
    141             elif child_class == 'Dictionary':
    142                 dictionary = IdlDictionary(idl_name, child)
    143                 self.dictionaries[dictionary.name] = dictionary
    144             else:
    145                 raise ValueError('Unrecognized node class: %s' % child_class)
    146 
    147         # Typedefs are not stored in IR:
    148         # Resolve typedefs with the actual types and then discard the Typedefs.
    149         # http://www.w3.org/TR/WebIDL/#idl-typedefs
    150         self.resolve_typedefs(typedefs)
    151 
    152     def resolve_typedefs(self, typedefs):
    153         for callback_function in self.callback_functions.itervalues():
    154             callback_function.resolve_typedefs(typedefs)
    155         for interface in self.interfaces.itervalues():
    156             interface.resolve_typedefs(typedefs)
    157 
    158     def update(self, other):
    159         """Update with additional IdlDefinitions."""
    160         for interface_name, new_interface in other.interfaces.iteritems():
    161             if not new_interface.is_partial:
    162                 # Add as new interface
    163                 self.interfaces[interface_name] = new_interface
    164                 continue
    165 
    166             # Merge partial to existing interface
    167             try:
    168                 self.interfaces[interface_name].merge(new_interface)
    169             except KeyError:
    170                 raise Exception('Tried to merge partial interface for {0}, '
    171                                 'but no existing interface by that name'
    172                                 .format(interface_name))
    173 
    174             # Merge callbacks and enumerations
    175             self.enumerations.update(other.enumerations)
    176             self.callback_functions.update(other.callback_functions)
    177 
    178 
    179 ################################################################################
    180 # Callback Functions
    181 ################################################################################
    182 
    183 class IdlCallbackFunction(TypedObject):
    184     def __init__(self, idl_name, node):
    185         children = node.GetChildren()
    186         num_children = len(children)
    187         if num_children != 2:
    188             raise ValueError('Expected 2 children, got %s' % num_children)
    189         type_node, arguments_node = children
    190         arguments_node_class = arguments_node.GetClass()
    191         if arguments_node_class != 'Arguments':
    192             raise ValueError('Expected Arguments node, got %s' % arguments_node_class)
    193 
    194         self.idl_name = idl_name
    195         self.name = node.GetName()
    196         self.idl_type = type_node_to_type(type_node)
    197         self.arguments = arguments_node_to_arguments(idl_name, arguments_node)
    198 
    199     def resolve_typedefs(self, typedefs):
    200         TypedObject.resolve_typedefs(self, typedefs)
    201         for argument in self.arguments:
    202             argument.resolve_typedefs(typedefs)
    203 
    204 
    205 ################################################################################
    206 # Dictionary
    207 ################################################################################
    208 
    209 class IdlDictionary(object):
    210     def __init__(self, idl_name, node):
    211         self.extended_attributes = {}
    212         self.is_partial = node.GetProperty('Partial') or False
    213         self.idl_name = idl_name
    214         self.name = node.GetName()
    215         self.members = []
    216         self.parent = None
    217         for child in node.GetChildren():
    218             child_class = child.GetClass()
    219             if child_class == 'Inherit':
    220                 self.parent = child.GetName()
    221             elif child_class == 'Key':
    222                 self.members.append(IdlDictionaryMember(idl_name, child))
    223             elif child_class == 'ExtAttributes':
    224                 self.extended_attributes = (
    225                     ext_attributes_node_to_extended_attributes(idl_name, child))
    226             else:
    227                 raise ValueError('Unrecognized node class: %s' % child_class)
    228 
    229 
    230 class IdlDictionaryMember(object):
    231     def __init__(self, idl_name, node):
    232         self.default_value = None
    233         self.extended_attributes = {}
    234         self.idl_type = None
    235         self.idl_name = idl_name
    236         self.name = node.GetName()
    237         for child in node.GetChildren():
    238             child_class = child.GetClass()
    239             if child_class == 'Type':
    240                 self.idl_type = type_node_to_type(child)
    241             elif child_class == 'Default':
    242                 self.default_value = default_node_to_idl_literal(child)
    243             elif child_class == 'ExtAttributes':
    244                 self.extended_attributes = (
    245                     ext_attributes_node_to_extended_attributes(idl_name, child))
    246             else:
    247                 raise ValueError('Unrecognized node class: %s' % child_class)
    248 
    249 
    250 ################################################################################
    251 # Enumerations
    252 ################################################################################
    253 
    254 class IdlEnum(object):
    255     # FIXME: remove, just treat enums as a dictionary
    256     def __init__(self, idl_name, node):
    257         self.idl_name = idl_name
    258         self.name = node.GetName()
    259         self.values = []
    260         for child in node.GetChildren():
    261             self.values.append(child.GetName())
    262 
    263 
    264 ################################################################################
    265 # Interfaces and Exceptions
    266 ################################################################################
    267 
    268 class IdlInterface(object):
    269     def __init__(self, idl_name, node=None):
    270         self.attributes = []
    271         self.constants = []
    272         self.constructors = []
    273         self.custom_constructors = []
    274         self.extended_attributes = {}
    275         self.operations = []
    276         self.parent = None
    277         self.stringifier = None
    278         if not node:  # Early exit for IdlException.__init__
    279             return
    280 
    281         self.is_callback = node.GetProperty('CALLBACK') or False
    282         self.is_exception = False
    283         # FIXME: uppercase 'Partial' => 'PARTIAL' in base IDL parser
    284         self.is_partial = node.GetProperty('Partial') or False
    285         self.idl_name = idl_name
    286         self.name = node.GetName()
    287 
    288         children = node.GetChildren()
    289         for child in children:
    290             child_class = child.GetClass()
    291             if child_class == 'Attribute':
    292                 self.attributes.append(IdlAttribute(idl_name, child))
    293             elif child_class == 'Const':
    294                 self.constants.append(IdlConstant(idl_name, child))
    295             elif child_class == 'ExtAttributes':
    296                 extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child)
    297                 self.constructors, self.custom_constructors = (
    298                     extended_attributes_to_constructors(idl_name, extended_attributes))
    299                 clear_constructor_attributes(extended_attributes)
    300                 self.extended_attributes = extended_attributes
    301             elif child_class == 'Operation':
    302                 self.operations.append(IdlOperation(idl_name, child))
    303             elif child_class == 'Inherit':
    304                 self.parent = child.GetName()
    305             elif child_class == 'Stringifier':
    306                 self.stringifier = IdlStringifier(idl_name, child)
    307                 self.process_stringifier()
    308             else:
    309                 raise ValueError('Unrecognized node class: %s' % child_class)
    310 
    311     def resolve_typedefs(self, typedefs):
    312         for attribute in self.attributes:
    313             attribute.resolve_typedefs(typedefs)
    314         for constant in self.constants:
    315             constant.resolve_typedefs(typedefs)
    316         for constructor in self.constructors:
    317             constructor.resolve_typedefs(typedefs)
    318         for custom_constructor in self.custom_constructors:
    319             custom_constructor.resolve_typedefs(typedefs)
    320         for operation in self.operations:
    321             operation.resolve_typedefs(typedefs)
    322 
    323     def process_stringifier(self):
    324         """Add the stringifier's attribute or named operation child, if it has
    325         one, as a regular attribute/operation of this interface."""
    326         if self.stringifier.attribute:
    327             self.attributes.append(self.stringifier.attribute)
    328         elif self.stringifier.operation:
    329             self.operations.append(self.stringifier.operation)
    330 
    331     def merge(self, other):
    332         """Merge in another interface's members (e.g., partial interface)"""
    333         self.attributes.extend(other.attributes)
    334         self.constants.extend(other.constants)
    335         self.operations.extend(other.operations)
    336 
    337 
    338 class IdlException(IdlInterface):
    339     # Properly exceptions and interfaces are distinct, and thus should inherit a
    340     # common base class (say, "IdlExceptionOrInterface").
    341     # However, there is only one exception (DOMException), and new exceptions
    342     # are not expected. Thus it is easier to implement exceptions as a
    343     # restricted subclass of interfaces.
    344     # http://www.w3.org/TR/WebIDL/#idl-exceptions
    345     def __init__(self, idl_name, node):
    346         # Exceptions are similar to Interfaces, but simpler
    347         IdlInterface.__init__(self, idl_name)
    348         self.is_callback = False
    349         self.is_exception = True
    350         self.is_partial = False
    351         self.idl_name = idl_name
    352         self.name = node.GetName()
    353 
    354         children = node.GetChildren()
    355         for child in children:
    356             child_class = child.GetClass()
    357             if child_class == 'Attribute':
    358                 attribute = IdlAttribute(idl_name, child)
    359                 self.attributes.append(attribute)
    360             elif child_class == 'Const':
    361                 self.constants.append(IdlConstant(idl_name, child))
    362             elif child_class == 'ExtAttributes':
    363                 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child)
    364             elif child_class == 'ExceptionOperation':
    365                 self.operations.append(IdlOperation.from_exception_operation_node(idl_name, child))
    366             else:
    367                 raise ValueError('Unrecognized node class: %s' % child_class)
    368 
    369 
    370 ################################################################################
    371 # Attributes
    372 ################################################################################
    373 
    374 class IdlAttribute(TypedObject):
    375     def __init__(self, idl_name, node):
    376         self.is_read_only = node.GetProperty('READONLY') or False
    377         self.is_static = node.GetProperty('STATIC') or False
    378         self.idl_name = idl_name
    379         self.name = node.GetName()
    380         # Defaults, overridden below
    381         self.idl_type = None
    382         self.extended_attributes = {}
    383 
    384         children = node.GetChildren()
    385         for child in children:
    386             child_class = child.GetClass()
    387             if child_class == 'Type':
    388                 self.idl_type = type_node_to_type(child)
    389             elif child_class == 'ExtAttributes':
    390                 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child)
    391             else:
    392                 raise ValueError('Unrecognized node class: %s' % child_class)
    393 
    394 
    395 ################################################################################
    396 # Constants
    397 ################################################################################
    398 
    399 class IdlConstant(TypedObject):
    400     def __init__(self, idl_name, node):
    401         children = node.GetChildren()
    402         num_children = len(children)
    403         if num_children < 2 or num_children > 3:
    404             raise ValueError('Expected 2 or 3 children, got %s' % num_children)
    405         type_node = children[0]
    406         value_node = children[1]
    407         value_node_class = value_node.GetClass()
    408         if value_node_class != 'Value':
    409             raise ValueError('Expected Value node, got %s' % value_node_class)
    410 
    411         self.idl_name = idl_name
    412         self.name = node.GetName()
    413         # ConstType is more limited than Type, so subtree is smaller and
    414         # we don't use the full type_node_to_type function.
    415         self.idl_type = type_node_inner_to_type(type_node)
    416         # FIXME: This code is unnecessarily complicated due to the rather
    417         # inconsistent way the upstream IDL parser outputs default values.
    418         # http://crbug.com/374178
    419         if value_node.GetProperty('TYPE') == 'float':
    420             self.value = value_node.GetProperty('VALUE')
    421         else:
    422             self.value = value_node.GetName()
    423 
    424         if num_children == 3:
    425             ext_attributes_node = children[2]
    426             self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, ext_attributes_node)
    427         else:
    428             self.extended_attributes = {}
    429 
    430 
    431 ################################################################################
    432 # Literals
    433 ################################################################################
    434 
    435 class IdlLiteral(object):
    436     def __init__(self, idl_type, value):
    437         self.idl_type = idl_type
    438         self.value = value
    439         self.is_null = False
    440 
    441     def __str__(self):
    442         if self.idl_type == 'DOMString':
    443             return 'String("%s")' % self.value
    444         if self.idl_type == 'integer':
    445             return '%d' % self.value
    446         if self.idl_type == 'float':
    447             return '%g' % self.value
    448         if self.idl_type == 'boolean':
    449             return 'true' if self.value else 'false'
    450         raise ValueError('Unsupported literal type: %s' % self.idl_type)
    451 
    452 
    453 class IdlLiteralNull(IdlLiteral):
    454     def __init__(self):
    455         self.idl_type = 'NULL'
    456         self.value = None
    457         self.is_null = True
    458 
    459     def __str__(self):
    460         return 'nullptr'
    461 
    462 
    463 def default_node_to_idl_literal(node):
    464     # FIXME: This code is unnecessarily complicated due to the rather
    465     # inconsistent way the upstream IDL parser outputs default values.
    466     # http://crbug.com/374178
    467     idl_type = node.GetProperty('TYPE')
    468     if idl_type == 'DOMString':
    469         value = node.GetProperty('NAME')
    470         if '"' in value or '\\' in value:
    471             raise ValueError('Unsupported string value: %r' % value)
    472         return IdlLiteral(idl_type, value)
    473     if idl_type == 'integer':
    474         return IdlLiteral(idl_type, int(node.GetProperty('NAME'), base=0))
    475     if idl_type == 'float':
    476         return IdlLiteral(idl_type, float(node.GetProperty('VALUE')))
    477     if idl_type == 'boolean':
    478         return IdlLiteral(idl_type, node.GetProperty('VALUE'))
    479     if idl_type == 'NULL':
    480         return IdlLiteralNull()
    481     raise ValueError('Unrecognized default value type: %s' % idl_type)
    482 
    483 
    484 ################################################################################
    485 # Operations
    486 ################################################################################
    487 
    488 class IdlOperation(TypedObject):
    489     def __init__(self, idl_name, node=None):
    490         self.arguments = []
    491         self.extended_attributes = {}
    492         self.specials = []
    493         self.is_constructor = False
    494 
    495         if not node:
    496             self.is_static = False
    497             return
    498         self.idl_name = idl_name
    499         self.name = node.GetName()  # FIXME: should just be: or ''
    500         # FIXME: AST should use None internally
    501         if self.name == '_unnamed_':
    502             self.name = ''
    503 
    504         self.is_static = node.GetProperty('STATIC') or False
    505         property_dictionary = node.GetProperties()
    506         for special_keyword in SPECIAL_KEYWORD_LIST:
    507             if special_keyword in property_dictionary:
    508                 self.specials.append(special_keyword.lower())
    509 
    510         self.idl_type = None
    511         children = node.GetChildren()
    512         for child in children:
    513             child_class = child.GetClass()
    514             if child_class == 'Arguments':
    515                 self.arguments = arguments_node_to_arguments(idl_name, child)
    516             elif child_class == 'Type':
    517                 self.idl_type = type_node_to_type(child)
    518             elif child_class == 'ExtAttributes':
    519                 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child)
    520             else:
    521                 raise ValueError('Unrecognized node class: %s' % child_class)
    522 
    523     @classmethod
    524     def from_exception_operation_node(cls, idl_name, node):
    525         # Needed to handle one case in DOMException.idl:
    526         # // Override in a Mozilla compatible format
    527         # [NotEnumerable] DOMString toString();
    528         # FIXME: can we remove this? replace with a stringifier?
    529         operation = cls(idl_name)
    530         operation.name = node.GetName()
    531         children = node.GetChildren()
    532         if len(children) < 1 or len(children) > 2:
    533             raise ValueError('ExceptionOperation node with %s children, expected 1 or 2' % len(children))
    534 
    535         type_node = children[0]
    536         operation.idl_type = type_node_to_type(type_node)
    537 
    538         if len(children) > 1:
    539             ext_attributes_node = children[1]
    540             operation.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, ext_attributes_node)
    541 
    542         return operation
    543 
    544     @classmethod
    545     def constructor_from_arguments_node(cls, name, idl_name, arguments_node):
    546         constructor = cls(idl_name)
    547         constructor.name = name
    548         constructor.arguments = arguments_node_to_arguments(idl_name, arguments_node)
    549         constructor.is_constructor = True
    550         return constructor
    551 
    552     def resolve_typedefs(self, typedefs):
    553         TypedObject.resolve_typedefs(self, typedefs)
    554         for argument in self.arguments:
    555             argument.resolve_typedefs(typedefs)
    556 
    557 
    558 ################################################################################
    559 # Arguments
    560 ################################################################################
    561 
    562 class IdlArgument(TypedObject):
    563     def __init__(self, idl_name, node):
    564         self.extended_attributes = {}
    565         self.idl_type = None
    566         self.is_optional = node.GetProperty('OPTIONAL')  # syntax: (optional T)
    567         self.is_variadic = False  # syntax: (T...)
    568         self.idl_name = idl_name
    569         self.name = node.GetName()
    570         self.default_value = None
    571 
    572         children = node.GetChildren()
    573         for child in children:
    574             child_class = child.GetClass()
    575             if child_class == 'Type':
    576                 self.idl_type = type_node_to_type(child)
    577             elif child_class == 'ExtAttributes':
    578                 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child)
    579             elif child_class == 'Argument':
    580                 child_name = child.GetName()
    581                 if child_name != '...':
    582                     raise ValueError('Unrecognized Argument node; expected "...", got "%s"' % child_name)
    583                 self.is_variadic = child.GetProperty('ELLIPSIS') or False
    584             elif child_class == 'Default':
    585                 self.default_value = default_node_to_idl_literal(child)
    586             else:
    587                 raise ValueError('Unrecognized node class: %s' % child_class)
    588 
    589 
    590 def arguments_node_to_arguments(idl_name, node):
    591     # [Constructor] and [CustomConstructor] without arguments (the bare form)
    592     # have None instead of an arguments node, but have the same meaning as using
    593     # an empty argument list, [Constructor()], so special-case this.
    594     # http://www.w3.org/TR/WebIDL/#Constructor
    595     if node is None:
    596         return []
    597     return [IdlArgument(idl_name, argument_node)
    598             for argument_node in node.GetChildren()]
    599 
    600 
    601 ################################################################################
    602 # Stringifiers
    603 ################################################################################
    604 
    605 class IdlStringifier(object):
    606     def __init__(self, idl_name, node):
    607         self.attribute = None
    608         self.operation = None
    609         self.extended_attributes = {}
    610         self.idl_name = idl_name
    611 
    612         for child in node.GetChildren():
    613             child_class = child.GetClass()
    614             if child_class == 'Attribute':
    615                 self.attribute = IdlAttribute(idl_name, child)
    616             elif child_class == 'Operation':
    617                 operation = IdlOperation(idl_name, child)
    618                 if operation.name:
    619                     self.operation = operation
    620             elif child_class == 'ExtAttributes':
    621                 self.extended_attributes = ext_attributes_node_to_extended_attributes(idl_name, child)
    622             else:
    623                 raise ValueError('Unrecognized node class: %s' % child_class)
    624 
    625         # Copy the stringifier's extended attributes (such as [Unforgable]) onto
    626         # the underlying attribute or operation, if there is one.
    627         if self.attribute or self.operation:
    628             (self.attribute or self.operation).extended_attributes.update(
    629                 self.extended_attributes)
    630 
    631 
    632 ################################################################################
    633 # Extended attributes
    634 ################################################################################
    635 
    636 def ext_attributes_node_to_extended_attributes(idl_name, node):
    637     """
    638     Returns:
    639       Dictionary of {ExtAttributeName: ExtAttributeValue}.
    640       Value is usually a string, with these exceptions:
    641       Constructors: value is a list of Arguments nodes, corresponding to
    642         possible signatures of the constructor.
    643       CustomConstructors: value is a list of Arguments nodes, corresponding to
    644         possible signatures of the custom constructor.
    645       NamedConstructor: value is a Call node, corresponding to the single
    646         signature of the named constructor.
    647       SetWrapperReferenceTo: value is an Arguments node.
    648     """
    649     # Primarily just make a dictionary from the children.
    650     # The only complexity is handling various types of constructors:
    651     # Constructors and Custom Constructors can have duplicate entries due to
    652     # overloading, and thus are stored in temporary lists.
    653     # However, Named Constructors cannot be overloaded, and thus do not have
    654     # a list.
    655     # FIXME: move Constructor logic into separate function, instead of modifying
    656     #        extended attributes in-place.
    657     constructors = []
    658     custom_constructors = []
    659     extended_attributes = {}
    660 
    661     def child_node(extended_attribute_node):
    662         children = extended_attribute_node.GetChildren()
    663         if not children:
    664             return None
    665         if len(children) > 1:
    666             raise ValueError('ExtAttributes node with %s children, expected at most 1' % len(children))
    667         return children[0]
    668 
    669     extended_attribute_node_list = node.GetChildren()
    670     for extended_attribute_node in extended_attribute_node_list:
    671         name = extended_attribute_node.GetName()
    672         child = child_node(extended_attribute_node)
    673         child_class = child and child.GetClass()
    674         if name == 'Constructor':
    675             if child_class and child_class != 'Arguments':
    676                 raise ValueError('Constructor only supports Arguments as child, but has child of class: %s' % child_class)
    677             constructors.append(child)
    678         elif name == 'CustomConstructor':
    679             if child_class and child_class != 'Arguments':
    680                 raise ValueError('[CustomConstructor] only supports Arguments as child, but has child of class: %s' % child_class)
    681             custom_constructors.append(child)
    682         elif name == 'NamedConstructor':
    683             if child_class and child_class != 'Call':
    684                 raise ValueError('[NamedConstructor] only supports Call as child, but has child of class: %s' % child_class)
    685             extended_attributes[name] = child
    686         elif name == 'SetWrapperReferenceTo':
    687             if not child:
    688                 raise ValueError('[SetWrapperReferenceTo] requires a child, but has none.')
    689             if child_class != 'Arguments':
    690                 raise ValueError('[SetWrapperReferenceTo] only supports Arguments as child, but has child of class: %s' % child_class)
    691             extended_attributes[name] = arguments_node_to_arguments(idl_name, child)
    692         elif child:
    693             raise ValueError('ExtAttributes node with unexpected children: %s' % name)
    694         else:
    695             value = extended_attribute_node.GetProperty('VALUE')
    696             extended_attributes[name] = value
    697 
    698     # Store constructors and custom constructors in special list attributes,
    699     # which are deleted later. Note plural in key.
    700     if constructors:
    701         extended_attributes['Constructors'] = constructors
    702     if custom_constructors:
    703         extended_attributes['CustomConstructors'] = custom_constructors
    704 
    705     return extended_attributes
    706 
    707 
    708 def extended_attributes_to_constructors(idl_name, extended_attributes):
    709     """Returns constructors and custom_constructors (lists of IdlOperations).
    710 
    711     Auxiliary function for IdlInterface.__init__.
    712     """
    713 
    714     constructor_list = extended_attributes.get('Constructors', [])
    715     constructors = [
    716         IdlOperation.constructor_from_arguments_node('Constructor', idl_name, arguments_node)
    717         for arguments_node in constructor_list]
    718 
    719     custom_constructor_list = extended_attributes.get('CustomConstructors', [])
    720     custom_constructors = [
    721         IdlOperation.constructor_from_arguments_node('CustomConstructor', idl_name, arguments_node)
    722         for arguments_node in custom_constructor_list]
    723 
    724     if 'NamedConstructor' in extended_attributes:
    725         # FIXME: support overloaded named constructors, and make homogeneous
    726         name = 'NamedConstructor'
    727         call_node = extended_attributes['NamedConstructor']
    728         extended_attributes['NamedConstructor'] = call_node.GetName()
    729         children = call_node.GetChildren()
    730         if len(children) != 1:
    731             raise ValueError('NamedConstructor node expects 1 child, got %s.' % len(children))
    732         arguments_node = children[0]
    733         named_constructor = IdlOperation.constructor_from_arguments_node('NamedConstructor', idl_name, arguments_node)
    734         # FIXME: should return named_constructor separately; appended for Perl
    735         constructors.append(named_constructor)
    736 
    737     return constructors, custom_constructors
    738 
    739 
    740 def clear_constructor_attributes(extended_attributes):
    741     # Deletes Constructor*s* (plural), sets Constructor (singular)
    742     if 'Constructors' in extended_attributes:
    743         del extended_attributes['Constructors']
    744         extended_attributes['Constructor'] = None
    745     if 'CustomConstructors' in extended_attributes:
    746         del extended_attributes['CustomConstructors']
    747         extended_attributes['CustomConstructor'] = None
    748 
    749 
    750 ################################################################################
    751 # Types
    752 ################################################################################
    753 
    754 def type_node_to_type(node):
    755     children = node.GetChildren()
    756     if len(children) < 1 or len(children) > 2:
    757         raise ValueError('Type node expects 1 or 2 children (type + optional array []), got %s (multi-dimensional arrays are not supported).' % len(children))
    758 
    759     base_type = type_node_inner_to_type(children[0])
    760 
    761     if node.GetProperty('NULLABLE'):
    762         base_type = IdlNullableType(base_type)
    763 
    764     if len(children) == 2:
    765         array_node = children[1]
    766         array_node_class = array_node.GetClass()
    767         if array_node_class != 'Array':
    768             raise ValueError('Expected Array node as TypeSuffix, got %s node.' % array_node_class)
    769         array_type = IdlArrayType(base_type)
    770         if array_node.GetProperty('NULLABLE'):
    771             return IdlNullableType(array_type)
    772         return array_type
    773 
    774     return base_type
    775 
    776 
    777 def type_node_inner_to_type(node):
    778     node_class = node.GetClass()
    779     # Note Type*r*ef, not Typedef, meaning the type is an identifier, thus
    780     # either a typedef shorthand (but not a Typedef declaration itself) or an
    781     # interface type. We do not distinguish these, and just use the type name.
    782     if node_class in ['PrimitiveType', 'Typeref']:
    783         # unrestricted syntax: unrestricted double | unrestricted float
    784         is_unrestricted = node.GetProperty('UNRESTRICTED') or False
    785         return IdlType(node.GetName(), is_unrestricted=is_unrestricted)
    786     elif node_class == 'Any':
    787         return IdlType('any')
    788     elif node_class == 'Sequence':
    789         return sequence_node_to_type(node)
    790     elif node_class == 'UnionType':
    791         return union_type_node_to_idl_union_type(node)
    792     raise ValueError('Unrecognized node class: %s' % node_class)
    793 
    794 
    795 def sequence_node_to_type(node):
    796     children = node.GetChildren()
    797     if len(children) != 1:
    798         raise ValueError('Sequence node expects exactly 1 child, got %s' % len(children))
    799     sequence_child = children[0]
    800     sequence_child_class = sequence_child.GetClass()
    801     if sequence_child_class != 'Type':
    802         raise ValueError('Unrecognized node class: %s' % sequence_child_class)
    803     element_type = type_node_to_type(sequence_child)
    804     sequence_type = IdlSequenceType(element_type)
    805     if node.GetProperty('NULLABLE'):
    806         return IdlNullableType(sequence_type)
    807     return sequence_type
    808 
    809 
    810 def typedef_node_to_type(node):
    811     children = node.GetChildren()
    812     if len(children) != 1:
    813         raise ValueError('Typedef node with %s children, expected 1' % len(children))
    814     child = children[0]
    815     child_class = child.GetClass()
    816     if child_class != 'Type':
    817         raise ValueError('Unrecognized node class: %s' % child_class)
    818     return type_node_to_type(child)
    819 
    820 
    821 def union_type_node_to_idl_union_type(node):
    822     member_types = [type_node_to_type(member_type_node)
    823                     for member_type_node in node.GetChildren()]
    824     return IdlUnionType(member_types)
    825