• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
31Classes are primarily constructors, which build an IdlDefinitions object
32(and various contained objects) from an AST (produced by blink_idl_parser).
33
34This is in two steps:
35* Constructors walk the AST, creating objects.
36* Typedef resolution.
37
38Typedefs are all resolved here, and not stored in IR.
39
40Typedef resolution uses some auxiliary classes and OOP techniques to make this
41a generic call, via the resolve_typedefs() method.
42
43Class hierarchy (mostly containment, '<' for inheritance):
44
45IdlDefinitions
46    IdlCallbackFunction < TypedObject
47    IdlEnum :: FIXME: remove, just use a dict for enums
48    IdlInterface
49        IdlAttribute < TypedObject
50        IdlConstant < TypedObject
51        IdlOperation < TypedObject
52            IdlArgument < TypedObject
53    IdlException < IdlInterface
54        (same contents as IdlInterface)
55
56TypedObject :: mixin for typedef resolution
57
58Design doc: http://www.chromium.org/developers/design-documents/idl-compiler
59"""
60
61import abc
62
63from idl_types import IdlType, IdlUnionType
64
65SPECIAL_KEYWORD_LIST = ['GETTER', 'SETTER', 'DELETER']
66STANDARD_TYPEDEFS = {
67    # http://www.w3.org/TR/WebIDL/#common-DOMTimeStamp
68    'DOMTimeStamp': 'unsigned long long',
69}
70
71
72################################################################################
73# TypedObject (mixin for typedef resolution)
74################################################################################
75
76class TypedObject(object):
77    """Object with a type, such as an Attribute or Operation (return value).
78
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.
81    """
82    __metaclass__ = abc.ABCMeta
83    idl_type = None
84
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
88        # interface itself.
89        if not self.idl_type:
90            return
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)
94
95
96################################################################################
97# Definitions (main container class)
98################################################################################
99
100class 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 = {}
106        self.interfaces = {}
107
108        node_class = node.GetClass()
109        if node_class != 'File':
110            raise ValueError('Unrecognized node class: %s' % node_class)
111
112        typedefs = dict((typedef_name, IdlType(type_name))
113                        for typedef_name, type_name in
114                        STANDARD_TYPEDEFS.iteritems())
115
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
137                pass
138            elif child_class == 'Dictionary':
139                dictionary = IdlDictionary(child)
140                self.dictionaries[dictionary.name] = dictionary
141            else:
142                raise ValueError('Unrecognized node class: %s' % child_class)
143
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)
148
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)
154
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
161                continue
162
163            # Merge partial to existing interface
164            try:
165                self.interfaces[interface_name].merge(new_interface)
166            except KeyError:
167                raise Exception('Tried to merge partial interface for {0}, '
168                                'but no existing interface by that name'
169                                .format(interface_name))
170
171            # Merge callbacks and enumerations
172            self.enumerations.update(other.enumerations)
173            self.callback_functions.update(other.callback_functions)
174
175
176################################################################################
177# Callback Functions
178################################################################################
179
180class 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)
190
191        self.name = node.GetName()
192        self.idl_type = type_node_to_type(type_node)
193        self.arguments = arguments_node_to_arguments(arguments_node)
194
195    def resolve_typedefs(self, typedefs):
196        TypedObject.resolve_typedefs(self, typedefs)
197        for argument in self.arguments:
198            argument.resolve_typedefs(typedefs)
199
200
201################################################################################
202# Dictionary
203################################################################################
204
205class IdlDictionary(object):
206    def __init__(self, node):
207        self.parent = None
208        self.name = node.GetName()
209        self.members = []
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))
216            else:
217                raise ValueError('Unrecognized node class: %s' % child_class)
218
219
220class IdlDictionaryMember(object):
221    def __init__(self, node):
222        self.default_value = None
223        self.extended_attributes = {}
224        self.idl_type = None
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)
234            else:
235                raise ValueError('Unrecognized node class: %s' % child_class)
236
237
238################################################################################
239# Enumerations
240################################################################################
241
242class IdlEnum(object):
243    # FIXME: remove, just treat enums as a dictionary
244    def __init__(self, node):
245        self.name = node.GetName()
246        self.values = []
247        for child in node.GetChildren():
248            self.values.append(child.GetName())
249
250
251################################################################################
252# Interfaces and Exceptions
253################################################################################
254
255class IdlInterface(object):
256    def __init__(self, node=None):
257        self.attributes = []
258        self.constants = []
259        self.constructors = []
260        self.custom_constructors = []
261        self.extended_attributes = {}
262        self.operations = []
263        self.parent = None
264        if not node:  # Early exit for IdlException.__init__
265            return
266
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()
272
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()
290            else:
291                raise ValueError('Unrecognized node class: %s' % child_class)
292
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)
304
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)
310
311
312class 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()
326
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))
339            else:
340                raise ValueError('Unrecognized node class: %s' % child_class)
341
342
343################################################################################
344# Attributes
345################################################################################
346
347class 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
353        self.idl_type = None
354        self.extended_attributes = {}
355
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)
363            else:
364                raise ValueError('Unrecognized node class: %s' % child_class)
365
366
367################################################################################
368# Constants
369################################################################################
370
371class 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)
382
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()
388
389        if num_children == 3:
390            ext_attributes_node = children[2]
391            self.extended_attributes = ext_attributes_node_to_extended_attributes(ext_attributes_node)
392        else:
393            self.extended_attributes = {}
394
395
396################################################################################
397# Literals
398################################################################################
399
400class IdlLiteral(object):
401    def __init__(self, idl_type, value):
402        self.idl_type = idl_type
403        self.value = value
404        self.is_null = False
405
406    def __str__(self):
407        if self.idl_type == 'DOMString':
408            return 'String("%s")' % self.value
409        if self.idl_type == 'integer':
410            return '%d' % self.value
411        if self.idl_type == 'float':
412            return '%g' % self.value
413        if self.idl_type == 'boolean':
414            return 'true' if self.value else 'false'
415        raise ValueError('Unsupported literal type: %s' % self.idl_type)
416
417
418class IdlLiteralNull(IdlLiteral):
419    def __init__(self):
420        self.idl_type = 'NULL'
421        self.value = None
422        self.is_null = True
423
424    def __str__(self):
425        return 'nullptr'
426
427
428def default_node_to_idl_literal(node):
429    # FIXME: This code is unnecessarily complicated due to the rather
430    # inconsistent way the upstream IDL parser outputs default values.
431    # http://crbug.com/374178
432    idl_type = node.GetProperty('TYPE')
433    if idl_type == 'DOMString':
434        value = node.GetProperty('NAME')
435        if '"' in value or '\\' in value:
436            raise ValueError('Unsupported string value: %r' % value)
437        return IdlLiteral(idl_type, value)
438    if idl_type == 'integer':
439        return IdlLiteral(idl_type, int(node.GetProperty('NAME')))
440    if idl_type == 'float':
441        return IdlLiteral(idl_type, float(node.GetProperty('VALUE')))
442    if idl_type == 'boolean':
443        return IdlLiteral(idl_type, node.GetProperty('VALUE'))
444    if idl_type == 'NULL':
445        return IdlLiteralNull()
446    raise ValueError('Unrecognized default value type: %s' % idl_type)
447
448
449################################################################################
450# Operations
451################################################################################
452
453class IdlOperation(TypedObject):
454    def __init__(self, node=None):
455        self.arguments = []
456        self.extended_attributes = {}
457        self.specials = []
458        self.is_constructor = False
459
460        if not node:
461            self.is_static = False
462            return
463        self.name = node.GetName()  # FIXME: should just be: or ''
464        # FIXME: AST should use None internally
465        if self.name == '_unnamed_':
466            self.name = ''
467
468        self.is_static = node.GetProperty('STATIC') or False
469        property_dictionary = node.GetProperties()
470        for special_keyword in SPECIAL_KEYWORD_LIST:
471            if special_keyword in property_dictionary:
472                self.specials.append(special_keyword.lower())
473
474        self.idl_type = None
475        children = node.GetChildren()
476        for child in children:
477            child_class = child.GetClass()
478            if child_class == 'Arguments':
479                self.arguments = arguments_node_to_arguments(child)
480            elif child_class == 'Type':
481                self.idl_type = type_node_to_type(child)
482            elif child_class == 'ExtAttributes':
483                self.extended_attributes = ext_attributes_node_to_extended_attributes(child)
484            else:
485                raise ValueError('Unrecognized node class: %s' % child_class)
486
487    @classmethod
488    def from_exception_operation_node(cls, node):
489        # Needed to handle one case in DOMException.idl:
490        # // Override in a Mozilla compatible format
491        # [NotEnumerable] DOMString toString();
492        # FIXME: can we remove this? replace with a stringifier?
493        operation = cls()
494        operation.name = node.GetName()
495        children = node.GetChildren()
496        if len(children) < 1 or len(children) > 2:
497            raise ValueError('ExceptionOperation node with %s children, expected 1 or 2' % len(children))
498
499        type_node = children[0]
500        operation.idl_type = type_node_to_type(type_node)
501
502        if len(children) > 1:
503            ext_attributes_node = children[1]
504            operation.extended_attributes = ext_attributes_node_to_extended_attributes(ext_attributes_node)
505
506        return operation
507
508    @classmethod
509    def constructor_from_arguments_node(cls, name, arguments_node):
510        constructor = cls()
511        constructor.name = name
512        constructor.arguments = arguments_node_to_arguments(arguments_node)
513        constructor.is_constructor = True
514        return constructor
515
516    def resolve_typedefs(self, typedefs):
517        TypedObject.resolve_typedefs(self, typedefs)
518        for argument in self.arguments:
519            argument.resolve_typedefs(typedefs)
520
521
522################################################################################
523# Arguments
524################################################################################
525
526class IdlArgument(TypedObject):
527    def __init__(self, node):
528        self.extended_attributes = {}
529        self.idl_type = None
530        self.is_optional = node.GetProperty('OPTIONAL')  # syntax: (optional T)
531        self.is_variadic = False  # syntax: (T...)
532        self.name = node.GetName()
533        self.default_value = None
534
535        children = node.GetChildren()
536        for child in children:
537            child_class = child.GetClass()
538            if child_class == 'Type':
539                self.idl_type = type_node_to_type(child)
540            elif child_class == 'ExtAttributes':
541                self.extended_attributes = ext_attributes_node_to_extended_attributes(child)
542            elif child_class == 'Argument':
543                child_name = child.GetName()
544                if child_name != '...':
545                    raise ValueError('Unrecognized Argument node; expected "...", got "%s"' % child_name)
546                self.is_variadic = child.GetProperty('ELLIPSIS') or False
547            elif child_class == 'Default':
548                self.default_value = default_node_to_idl_literal(child)
549            else:
550                raise ValueError('Unrecognized node class: %s' % child_class)
551
552
553def arguments_node_to_arguments(node):
554    # [Constructor] and [CustomConstructor] without arguments (the bare form)
555    # have None instead of an arguments node, but have the same meaning as using
556    # an empty argument list, [Constructor()], so special-case this.
557    # http://www.w3.org/TR/WebIDL/#Constructor
558    if node is None:
559        return []
560    return [IdlArgument(argument_node)
561            for argument_node in node.GetChildren()]
562
563
564################################################################################
565# Extended attributes
566################################################################################
567
568def ext_attributes_node_to_extended_attributes(node):
569    """
570    Returns:
571      Dictionary of {ExtAttributeName: ExtAttributeValue}.
572      Value is usually a string, with three exceptions:
573      Constructors: value is a list of Arguments nodes, corresponding to
574        possible signatures of the constructor.
575      CustomConstructors: value is a list of Arguments nodes, corresponding to
576        possible signatures of the custom constructor.
577      NamedConstructor: value is a Call node, corresponding to the single
578        signature of the named constructor.
579    """
580    # Primarily just make a dictionary from the children.
581    # The only complexity is handling various types of constructors:
582    # Constructors and Custom Constructors can have duplicate entries due to
583    # overloading, and thus are stored in temporary lists.
584    # However, Named Constructors cannot be overloaded, and thus do not have
585    # a list.
586    # FIXME: move Constructor logic into separate function, instead of modifying
587    #        extended attributes in-place.
588    constructors = []
589    custom_constructors = []
590    extended_attributes = {}
591
592    def child_node(extended_attribute_node):
593        children = extended_attribute_node.GetChildren()
594        if not children:
595            return None
596        if len(children) > 1:
597            raise ValueError('ExtAttributes node with %s children, expected at most 1' % len(children))
598        return children[0]
599
600    extended_attribute_node_list = node.GetChildren()
601    for extended_attribute_node in extended_attribute_node_list:
602        name = extended_attribute_node.GetName()
603        child = child_node(extended_attribute_node)
604        child_class = child and child.GetClass()
605        if name == 'Constructor':
606            if child_class and child_class != 'Arguments':
607                raise ValueError('Constructor only supports Arguments as child, but has child of class: %s' % child_class)
608            constructors.append(child)
609        elif name == 'CustomConstructor':
610            if child_class and child_class != 'Arguments':
611                raise ValueError('[CustomConstructor] only supports Arguments as child, but has child of class: %s' % child_class)
612            custom_constructors.append(child)
613        elif name == 'NamedConstructor':
614            if child_class and child_class != 'Call':
615                raise ValueError('[NamedConstructor] only supports Call as child, but has child of class: %s' % child_class)
616            extended_attributes[name] = child
617        elif name == 'SetWrapperReferenceTo':
618            if not child:
619                raise ValueError('[SetWrapperReferenceTo] requires a child, but has none.')
620            if child_class != 'Arguments':
621                raise ValueError('[SetWrapperReferenceTo] only supports Arguments as child, but has child of class: %s' % child_class)
622            extended_attributes[name] = arguments_node_to_arguments(child)
623        elif child:
624            raise ValueError('ExtAttributes node with unexpected children: %s' % name)
625        else:
626            value = extended_attribute_node.GetProperty('VALUE')
627            extended_attributes[name] = value
628
629    # Store constructors and custom constructors in special list attributes,
630    # which are deleted later. Note plural in key.
631    if constructors:
632        extended_attributes['Constructors'] = constructors
633    if custom_constructors:
634        extended_attributes['CustomConstructors'] = custom_constructors
635
636    return extended_attributes
637
638
639def extended_attributes_to_constructors(extended_attributes):
640    """Returns constructors and custom_constructors (lists of IdlOperations).
641
642    Auxiliary function for IdlInterface.__init__.
643    """
644
645    constructor_list = extended_attributes.get('Constructors', [])
646    constructors = [
647        IdlOperation.constructor_from_arguments_node('Constructor', arguments_node)
648        for arguments_node in constructor_list]
649
650    custom_constructor_list = extended_attributes.get('CustomConstructors', [])
651    custom_constructors = [
652        IdlOperation.constructor_from_arguments_node('CustomConstructor', arguments_node)
653        for arguments_node in custom_constructor_list]
654
655    if 'NamedConstructor' in extended_attributes:
656        # FIXME: support overloaded named constructors, and make homogeneous
657        name = 'NamedConstructor'
658        call_node = extended_attributes['NamedConstructor']
659        extended_attributes['NamedConstructor'] = call_node.GetName()
660        children = call_node.GetChildren()
661        if len(children) != 1:
662            raise ValueError('NamedConstructor node expects 1 child, got %s.' % len(children))
663        arguments_node = children[0]
664        named_constructor = IdlOperation.constructor_from_arguments_node('NamedConstructor', arguments_node)
665        # FIXME: should return named_constructor separately; appended for Perl
666        constructors.append(named_constructor)
667
668    return constructors, custom_constructors
669
670
671def clear_constructor_attributes(extended_attributes):
672    # Deletes Constructor*s* (plural), sets Constructor (singular)
673    if 'Constructors' in extended_attributes:
674        del extended_attributes['Constructors']
675        extended_attributes['Constructor'] = None
676    if 'CustomConstructors' in extended_attributes:
677        del extended_attributes['CustomConstructors']
678        extended_attributes['CustomConstructor'] = None
679
680
681################################################################################
682# Types
683################################################################################
684
685def type_node_to_type(node):
686    children = node.GetChildren()
687    if len(children) < 1 or len(children) > 2:
688        raise ValueError('Type node expects 1 or 2 children (type + optional array []), got %s (multi-dimensional arrays are not supported).' % len(children))
689
690    type_node_child = children[0]
691
692    if len(children) == 2:
693        array_node = children[1]
694        array_node_class = array_node.GetClass()
695        if array_node_class != 'Array':
696            raise ValueError('Expected Array node as TypeSuffix, got %s node.' % array_node_class)
697        # FIXME: use IdlArrayType instead of is_array, once have that
698        is_array = True
699    else:
700        is_array = False
701
702    is_nullable = node.GetProperty('NULLABLE') or False  # syntax: T?
703
704    return type_node_inner_to_type(type_node_child, is_array=is_array, is_nullable=is_nullable)
705
706
707def type_node_inner_to_type(node, is_array=False, is_nullable=False):
708    # FIXME: remove is_array and is_nullable once have IdlArrayType and IdlNullableType
709    node_class = node.GetClass()
710    # Note Type*r*ef, not Typedef, meaning the type is an identifier, thus
711    # either a typedef shorthand (but not a Typedef declaration itself) or an
712    # interface type. We do not distinguish these, and just use the type name.
713    if node_class in ['PrimitiveType', 'Typeref']:
714        # unrestricted syntax: unrestricted double | unrestricted float
715        is_unrestricted = node.GetProperty('UNRESTRICTED') or False
716        return IdlType(node.GetName(), is_array=is_array, is_nullable=is_nullable, is_unrestricted=is_unrestricted)
717    elif node_class == 'Any':
718        return IdlType('any', is_array=is_array, is_nullable=is_nullable)
719    elif node_class == 'Sequence':
720        if is_array:
721            raise ValueError('Arrays of sequences are not supported')
722        return sequence_node_to_type(node, is_nullable=is_nullable)
723    elif node_class == 'UnionType':
724        if is_array:
725            raise ValueError('Arrays of unions are not supported')
726        return union_type_node_to_idl_union_type(node, is_nullable=is_nullable)
727    raise ValueError('Unrecognized node class: %s' % node_class)
728
729
730def sequence_node_to_type(node, is_nullable=False):
731    children = node.GetChildren()
732    if len(children) != 1:
733        raise ValueError('Sequence node expects exactly 1 child, got %s' % len(children))
734    sequence_child = children[0]
735    sequence_child_class = sequence_child.GetClass()
736    if sequence_child_class != 'Type':
737        raise ValueError('Unrecognized node class: %s' % sequence_child_class)
738    element_type = type_node_to_type(sequence_child).base_type
739    return IdlType(element_type, is_sequence=True, is_nullable=is_nullable)
740
741
742def typedef_node_to_type(node):
743    children = node.GetChildren()
744    if len(children) != 1:
745        raise ValueError('Typedef node with %s children, expected 1' % len(children))
746    child = children[0]
747    child_class = child.GetClass()
748    if child_class != 'Type':
749        raise ValueError('Unrecognized node class: %s' % child_class)
750    return type_node_to_type(child)
751
752
753def union_type_node_to_idl_union_type(node, is_nullable=False):
754    member_types = [type_node_to_type(member_type_node)
755                    for member_type_node in node.GetChildren()]
756    return IdlUnionType(member_types, is_nullable=is_nullable)
757