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# pylint: disable=relative-import
30"""Blink IDL Intermediate Representation (IR) classes.
31
32Classes are primarily constructors, which build an IdlDefinitions object
33(and various contained objects) from an AST (produced by blink_idl_parser).
34
35IR stores typedefs and they are resolved by the code generator.
36
37Typedef resolution uses some auxiliary classes and OOP techniques to make this
38a generic call. See TypedefResolver class in code_generator_v8.py.
39
40Class hierarchy (mostly containment, '<' for inheritance):
41
42IdlDefinitions
43    IdlCallbackFunction < TypedObject
44    IdlEnum :: FIXME: remove, just use a dict for enums
45    IdlInterface
46        IdlAttribute < TypedObject
47        IdlConstant < TypedObject
48        IdlLiteral
49        IdlOperation < TypedObject
50            IdlArgument < TypedObject
51        IdlStringifier
52        IdlIterable < IdlIterableOrMaplikeOrSetlike
53        IdlMaplike < IdlIterableOrMaplikeOrSetlike
54        IdlSetlike < IdlIterableOrMaplikeOrSetlike
55
56TypedObject :: Object with one or more attributes that is a type.
57
58IdlArgument is 'picklable', as it is stored in interfaces_info.
59
60Design doc: http://www.chromium.org/developers/design-documents/idl-compiler
61"""
62
63import abc
64
65from idl_types import IdlAnnotatedType
66from idl_types import IdlFrozenArrayType
67from idl_types import IdlNullableType
68from idl_types import IdlRecordType
69from idl_types import IdlSequenceType
70from idl_types import IdlType
71from idl_types import IdlUnionType
72
73SPECIAL_KEYWORD_LIST = ['GETTER', 'SETTER', 'DELETER']
74
75################################################################################
76# TypedObject
77################################################################################
78
79
80class TypedObject(object):
81    """Object with a type, such as an Attribute or Operation (return value).
82
83    The type can be an actual type, or can be a typedef, which must be resolved
84    by the TypedefResolver before passing data to the code generator.
85    """
86    __metaclass__ = abc.ABCMeta
87    idl_type_attributes = ('idl_type', )
88
89
90################################################################################
91# Definitions (main container class)
92################################################################################
93
94
95class IdlDefinitions(object):
96    def __init__(self, node):
97        """Args: node: AST root node, class == 'File'"""
98        self.callback_functions = {}
99        self.dictionaries = {}
100        self.enumerations = {}
101        self.includes = []
102        self.interfaces = {}
103        self.first_name = None
104        self.typedefs = {}
105
106        node_class = node.GetClass()
107        if node_class != 'File':
108            raise ValueError('Unrecognized node class: %s' % node_class)
109
110        children = node.GetChildren()
111        for child in children:
112            child_class = child.GetClass()
113            if child_class == 'Interface' or child_class == 'Namespace':
114                interface = IdlInterface(child)
115                self.interfaces[interface.name] = interface
116                if not self.first_name:
117                    self.first_name = interface.name
118            elif child_class == 'Typedef':
119                typedef = IdlTypedef(child)
120                self.typedefs[typedef.name] = typedef
121            elif child_class == 'Enum':
122                enumeration = IdlEnum(child)
123                self.enumerations[enumeration.name] = enumeration
124            elif child_class == 'Callback':
125                callback_function = IdlCallbackFunction(child)
126                self.callback_functions[callback_function.
127                                        name] = callback_function
128            elif child_class == 'Includes':
129                self.includes.append(IdlIncludes(child))
130            elif child_class == 'Dictionary':
131                dictionary = IdlDictionary(child)
132                self.dictionaries[dictionary.name] = dictionary
133                if not self.first_name:
134                    self.first_name = dictionary.name
135            else:
136                raise ValueError('Unrecognized node class: %s' % child_class)
137
138    def accept(self, visitor):
139        visitor.visit_definitions(self)
140        for interface in self.interfaces.values():
141            interface.accept(visitor)
142        for callback_function in self.callback_functions.values():
143            callback_function.accept(visitor)
144        for dictionary in self.dictionaries.values():
145            dictionary.accept(visitor)
146        for enumeration in self.enumerations.values():
147            enumeration.accept(visitor)
148        for include in self.includes:
149            include.accept(visitor)
150        for typedef in self.typedefs.values():
151            typedef.accept(visitor)
152
153    def update(self, other):
154        """Update with additional IdlDefinitions."""
155        for interface_name, new_interface in other.interfaces.items():
156            if not new_interface.is_partial:
157                # Add as new interface
158                self.interfaces[interface_name] = new_interface
159                continue
160
161            # Merge partial to existing interface
162            try:
163                self.interfaces[interface_name].merge(new_interface)
164            except KeyError:
165                raise Exception('Tried to merge partial interface for {0}, '
166                                'but no existing interface by that name'.
167                                format(interface_name))
168
169            # Merge callbacks and enumerations
170            self.enumerations.update(other.enumerations)
171            self.callback_functions.update(other.callback_functions)
172
173
174################################################################################
175# Callback Functions
176################################################################################
177
178
179class IdlCallbackFunction(TypedObject):
180    def __init__(self, node):
181        children = node.GetChildren()
182        num_children = len(children)
183        if num_children < 2 or num_children > 3:
184            raise ValueError('Expected 2 or 3 children, got %s' % num_children)
185        type_node = children[0]
186        arguments_node = children[1]
187        if num_children == 3:
188            ext_attributes_node = children[2]
189            self.extended_attributes = (
190                ext_attributes_node_to_extended_attributes(ext_attributes_node)
191            )
192        else:
193            self.extended_attributes = {}
194        arguments_node_class = arguments_node.GetClass()
195        if arguments_node_class != 'Arguments':
196            raise ValueError(
197                'Expected Arguments node, got %s' % arguments_node_class)
198
199        self.name = node.GetName()
200        self.idl_type = type_node_to_type(type_node)
201        self.arguments = arguments_node_to_arguments(arguments_node)
202
203    def accept(self, visitor):
204        visitor.visit_callback_function(self)
205        for argument in self.arguments:
206            argument.accept(visitor)
207
208
209################################################################################
210# Dictionary
211################################################################################
212
213
214class IdlDictionary(object):
215    def __init__(self, node):
216        self.extended_attributes = {}
217        self.is_partial = bool(node.GetProperty('PARTIAL'))
218        self.name = node.GetName()
219        self.members = []
220        self.parent = None
221        for child in node.GetChildren():
222            child_class = child.GetClass()
223            if child_class == 'Inherit':
224                self.parent = child.GetName()
225            elif child_class == 'Key':
226                self.members.append(IdlDictionaryMember(child))
227            elif child_class == 'ExtAttributes':
228                self.extended_attributes = (
229                    ext_attributes_node_to_extended_attributes(child))
230            else:
231                raise ValueError('Unrecognized node class: %s' % child_class)
232
233    def accept(self, visitor):
234        visitor.visit_dictionary(self)
235        for member in self.members:
236            member.accept(visitor)
237
238
239class IdlDictionaryMember(TypedObject):
240    def __init__(self, node):
241        self.default_value = None
242        self.extended_attributes = {}
243        self.idl_type = None
244        self.is_required = bool(node.GetProperty('REQUIRED'))
245        self.name = node.GetName()
246        for child in node.GetChildren():
247            child_class = child.GetClass()
248            if child_class == 'Type':
249                self.idl_type = type_node_to_type(child)
250            elif child_class == 'Default':
251                self.default_value = default_node_to_idl_literal(child)
252            elif child_class == 'ExtAttributes':
253                self.extended_attributes = (
254                    ext_attributes_node_to_extended_attributes(child))
255            else:
256                raise ValueError('Unrecognized node class: %s' % child_class)
257
258    def accept(self, visitor):
259        visitor.visit_dictionary_member(self)
260
261
262################################################################################
263# Enumerations
264################################################################################
265
266
267class IdlEnum(object):
268    def __init__(self, node):
269        self.name = node.GetName()
270        self.values = []
271        for child in node.GetChildren():
272            self.values.append(child.GetName())
273
274    def accept(self, visitor):
275        visitor.visit_enumeration(self)
276
277
278################################################################################
279# Typedefs
280################################################################################
281
282
283class IdlTypedef(object):
284    idl_type_attributes = ('idl_type', )
285
286    def __init__(self, node):
287        self.name = node.GetName()
288        self.idl_type = typedef_node_to_type(node)
289
290    def accept(self, visitor):
291        visitor.visit_typedef(self)
292
293
294################################################################################
295# Interfaces
296################################################################################
297
298
299class IdlInterface(object):
300    def __init__(self, node):
301        self.attributes = []
302        self.constants = []
303        self.constructors = []
304        self.custom_constructors = []
305        self.extended_attributes = {}
306        self.operations = []
307        self.parent = None
308        self.stringifier = None
309        self.iterable = None
310        self.has_indexed_elements = False
311        self.has_named_property_getter = False
312        self.maplike = None
313        self.setlike = None
314        self.original_interface = None
315        self.partial_interfaces = []
316
317        self.is_callback = bool(node.GetProperty('CALLBACK'))
318        self.is_partial = bool(node.GetProperty('PARTIAL'))
319        self.is_mixin = bool(node.GetProperty('MIXIN'))
320        self.name = node.GetName()
321        self.idl_type = IdlType(self.name)
322
323        has_indexed_property_getter = False
324        has_integer_typed_length = False
325
326        # These are used to support both constructor operations and old style
327        # [Constructor] extended attributes. Ideally we should do refactoring
328        # for constructor code generation but we will use a new code generator
329        # soon so this kind of workaround should be fine.
330        constructor_operations = []
331        custom_constructor_operations = []
332        constructor_operations_extended_attributes = {}
333
334        def is_invalid_attribute_type(idl_type):
335            return idl_type.is_callback_function or \
336                idl_type.is_dictionary or \
337                idl_type.is_record_type or \
338                idl_type.is_sequence_type
339
340        children = node.GetChildren()
341        for child in children:
342            child_class = child.GetClass()
343            if child_class == 'Attribute':
344                attr = IdlAttribute(child)
345                if is_invalid_attribute_type(attr.idl_type):
346                    raise ValueError(
347                        'Type "%s" cannot be used as an attribute.' %
348                        attr.idl_type)
349                if attr.idl_type.is_integer_type and attr.name == 'length':
350                    has_integer_typed_length = True
351                self.attributes.append(attr)
352            elif child_class == 'Const':
353                self.constants.append(IdlConstant(child))
354            elif child_class == 'ExtAttributes':
355                extended_attributes = ext_attributes_node_to_extended_attributes(
356                    child)
357                self.constructors, self.custom_constructors = (
358                    extended_attributes_to_constructors(extended_attributes))
359                clear_constructor_attributes(extended_attributes)
360                self.extended_attributes = extended_attributes
361            elif child_class == 'Operation':
362                op = IdlOperation(child)
363                if 'getter' in op.specials:
364                    if str(op.arguments[0].idl_type) == 'unsigned long':
365                        has_indexed_property_getter = True
366                    elif str(op.arguments[0].idl_type) == 'DOMString':
367                        self.has_named_property_getter = True
368                self.operations.append(op)
369            elif child_class == 'Constructor':
370                operation = constructor_operation_from_node(child)
371                if operation.is_custom:
372                    custom_constructor_operations.append(operation.constructor)
373                else:
374                    # Check extended attributes consistency when we previously
375                    # handle constructor operations.
376                    if constructor_operations:
377                        check_constructor_operations_extended_attributes(
378                            constructor_operations_extended_attributes,
379                            operation.extended_attributes)
380                    constructor_operations.append(operation.constructor)
381                    constructor_operations_extended_attributes.update(
382                        operation.extended_attributes)
383            elif child_class == 'Inherit':
384                self.parent = child.GetName()
385            elif child_class == 'Stringifier':
386                self.stringifier = IdlStringifier(child)
387                self.process_stringifier()
388            elif child_class == 'Iterable':
389                self.iterable = IdlIterable(child)
390            elif child_class == 'Maplike':
391                self.maplike = IdlMaplike(child)
392            elif child_class == 'Setlike':
393                self.setlike = IdlSetlike(child)
394            else:
395                raise ValueError('Unrecognized node class: %s' % child_class)
396
397        if len(filter(None, [self.iterable, self.maplike, self.setlike])) > 1:
398            raise ValueError(
399                'Interface can only have one of iterable<>, maplike<> and setlike<>.'
400            )
401
402        # TODO(rakuco): This validation logic should be in v8_interface according to bashi@.
403        # At the moment, doing so does not work because several IDL files are partial Window
404        # interface definitions, and interface_dependency_resolver.py doesn't seem to have any logic
405        # to prevent these partial interfaces from resetting has_named_property to False.
406        if 'LegacyUnenumerableNamedProperties' in self.extended_attributes and \
407           not self.has_named_property_getter:
408            raise ValueError(
409                '[LegacyUnenumerableNamedProperties] can be used only in interfaces '
410                'that support named properties.')
411
412        if has_integer_typed_length and has_indexed_property_getter:
413            self.has_indexed_elements = True
414        else:
415            if self.iterable is not None and self.iterable.key_type is None:
416                raise ValueError(
417                    'Value iterators (iterable<V>) must be accompanied by an indexed '
418                    'property getter and an integer-typed length attribute.')
419
420        if 'LegacyUnforgeable' in self.extended_attributes:
421            raise ValueError(
422                '[LegacyUnforgeable] cannot appear on interfaces.')
423
424        if constructor_operations or custom_constructor_operations:
425            if self.constructors or self.custom_constructors:
426                raise ValueError('Detected mixed [Constructor] and consructor '
427                                 'operations. Do not use both in a single '
428                                 'interface.')
429            extended_attributes = (
430                convert_constructor_operations_extended_attributes(
431                    constructor_operations_extended_attributes))
432            if any(name in extended_attributes.keys()
433                   for name in self.extended_attributes.keys()):
434                raise ValueError('Detected mixed extended attributes for '
435                                 'both [Constructor] and constructor '
436                                 'operations. Do not use both in a single '
437                                 'interface')
438            self.constructors = constructor_operations
439            self.custom_constructors = custom_constructor_operations
440            self.extended_attributes.update(extended_attributes)
441
442    def accept(self, visitor):
443        visitor.visit_interface(self)
444        for attribute in self.attributes:
445            attribute.accept(visitor)
446        for constant in self.constants:
447            constant.accept(visitor)
448        for constructor in self.constructors:
449            constructor.accept(visitor)
450        for custom_constructor in self.custom_constructors:
451            custom_constructor.accept(visitor)
452        for operation in self.operations:
453            operation.accept(visitor)
454        if self.iterable:
455            self.iterable.accept(visitor)
456        elif self.maplike:
457            self.maplike.accept(visitor)
458        elif self.setlike:
459            self.setlike.accept(visitor)
460
461    def process_stringifier(self):
462        """Add the stringifier's attribute or named operation child, if it has
463        one, as a regular attribute/operation of this interface."""
464        if self.stringifier.attribute:
465            self.attributes.append(self.stringifier.attribute)
466        elif self.stringifier.operation:
467            self.operations.append(self.stringifier.operation)
468
469    def merge(self, other):
470        """Merge in another interface's members (e.g., partial interface)"""
471        self.attributes.extend(other.attributes)
472        self.constants.extend(other.constants)
473        self.operations.extend(other.operations)
474        if self.stringifier is None:
475            self.stringifier = other.stringifier
476
477
478################################################################################
479# Attributes
480################################################################################
481
482
483class IdlAttribute(TypedObject):
484    def __init__(self, node=None):
485        self.is_read_only = bool(
486            node.GetProperty('READONLY')) if node else False
487        self.is_static = bool(node.GetProperty('STATIC')) if node else False
488        self.name = node.GetName() if node else None
489        self.idl_type = None
490        self.extended_attributes = {}
491        # In what interface the attribute is (originally) defined when the
492        # attribute is inherited from an ancestor interface.
493        self.defined_in = None
494
495        if node:
496            children = node.GetChildren()
497            for child in children:
498                child_class = child.GetClass()
499                if child_class == 'Type':
500                    self.idl_type = type_node_to_type(child)
501                elif child_class == 'ExtAttributes':
502                    self.extended_attributes = ext_attributes_node_to_extended_attributes(
503                        child)
504                else:
505                    raise ValueError(
506                        'Unrecognized node class: %s' % child_class)
507
508        if 'LegacyUnforgeable' in self.extended_attributes and self.is_static:
509            raise ValueError(
510                '[LegacyUnforgeable] cannot appear on static attributes.')
511
512    def accept(self, visitor):
513        visitor.visit_attribute(self)
514
515
516################################################################################
517# Constants
518################################################################################
519
520
521class IdlConstant(TypedObject):
522    def __init__(self, node):
523        children = node.GetChildren()
524        num_children = len(children)
525        if num_children < 2 or num_children > 3:
526            raise ValueError('Expected 2 or 3 children, got %s' % num_children)
527        type_node = children[0]
528        value_node = children[1]
529        value_node_class = value_node.GetClass()
530        if value_node_class != 'Value':
531            raise ValueError('Expected Value node, got %s' % value_node_class)
532
533        self.name = node.GetName()
534        # ConstType is more limited than Type, so subtree is smaller and
535        # we don't use the full type_node_to_type function.
536        self.idl_type = type_node_inner_to_type(type_node)
537        self.value = value_node.GetProperty('VALUE')
538        # In what interface the attribute is (originally) defined when the
539        # attribute is inherited from an ancestor interface.
540        self.defined_in = None
541
542        if num_children == 3:
543            ext_attributes_node = children[2]
544            self.extended_attributes = ext_attributes_node_to_extended_attributes(
545                ext_attributes_node)
546        else:
547            self.extended_attributes = {}
548
549    def accept(self, visitor):
550        visitor.visit_constant(self)
551
552
553################################################################################
554# Literals
555################################################################################
556
557
558class IdlLiteral(object):
559    def __init__(self, idl_type, value):
560        self.idl_type = idl_type
561        self.value = value
562        self.is_null = False
563
564    def __str__(self):
565        if self.idl_type == 'DOMString':
566            if self.value:
567                return '"%s"' % self.value
568            else:
569                return 'WTF::g_empty_string'
570        if self.idl_type == 'integer':
571            return '%d' % self.value
572        if self.idl_type == 'float':
573            return '%g' % self.value
574        if self.idl_type == 'boolean':
575            return 'true' if self.value else 'false'
576        if self.idl_type == 'dictionary':
577            return self.value
578        raise ValueError('Unsupported literal type: %s' % self.idl_type)
579
580
581class IdlLiteralNull(IdlLiteral):
582    def __init__(self):
583        self.idl_type = 'NULL'
584        self.value = None
585        self.is_null = True
586
587    def __str__(self):
588        return 'nullptr'
589
590
591def default_node_to_idl_literal(node):
592    idl_type = node.GetProperty('TYPE')
593    value = node.GetProperty('VALUE')
594    if idl_type == 'DOMString':
595        if '"' in value or '\\' in value:
596            raise ValueError('Unsupported string value: %r' % value)
597        return IdlLiteral(idl_type, value)
598    if idl_type == 'integer':
599        return IdlLiteral(idl_type, int(value, base=0))
600    if idl_type == 'float':
601        return IdlLiteral(idl_type, float(value))
602    if idl_type in ['boolean', 'sequence']:
603        return IdlLiteral(idl_type, value)
604    if idl_type == 'NULL':
605        return IdlLiteralNull()
606    if idl_type == 'dictionary':
607        return IdlLiteral(idl_type, value)
608    raise ValueError('Unrecognized default value type: %s' % idl_type)
609
610
611################################################################################
612# Operations
613################################################################################
614
615
616class IdlOperation(TypedObject):
617    def __init__(self, node=None):
618        self.arguments = []
619        self.extended_attributes = {}
620        self.specials = []
621        self.is_constructor = False
622        self.idl_type = None
623        self.is_static = False
624        # In what interface the attribute is (originally) defined when the
625        # attribute is inherited from an ancestor interface.
626        self.defined_in = None
627
628        if not node:
629            return
630
631        self.name = node.GetName()
632
633        self.is_static = bool(node.GetProperty('STATIC'))
634        property_dictionary = node.GetProperties()
635        for special_keyword in SPECIAL_KEYWORD_LIST:
636            if special_keyword in property_dictionary:
637                self.specials.append(special_keyword.lower())
638
639        children = node.GetChildren()
640        for child in children:
641            child_class = child.GetClass()
642            if child_class == 'Arguments':
643                self.arguments = arguments_node_to_arguments(child)
644            elif child_class == 'Type':
645                self.idl_type = type_node_to_type(child)
646            elif child_class == 'ExtAttributes':
647                self.extended_attributes = ext_attributes_node_to_extended_attributes(
648                    child)
649            else:
650                raise ValueError('Unrecognized node class: %s' % child_class)
651
652        if 'LegacyUnforgeable' in self.extended_attributes and self.is_static:
653            raise ValueError(
654                '[LegacyUnforgeable] cannot appear on static operations.')
655
656    @classmethod
657    def constructor_from_arguments_node(cls, name, arguments_node):
658        constructor = cls()
659        constructor.name = name
660        constructor.arguments = arguments_node_to_arguments(arguments_node)
661        constructor.is_constructor = True
662        return constructor
663
664    def accept(self, visitor):
665        visitor.visit_operation(self)
666        for argument in self.arguments:
667            argument.accept(visitor)
668
669
670################################################################################
671# Arguments
672################################################################################
673
674
675class IdlArgument(TypedObject):
676    def __init__(self, node=None):
677        self.extended_attributes = {}
678        self.idl_type = None
679        self.is_optional = False  # syntax: (optional T)
680        self.is_variadic = False  # syntax: (T...)
681        self.default_value = None
682
683        if not node:
684            return
685
686        self.is_optional = node.GetProperty('OPTIONAL')
687        self.name = node.GetName()
688
689        children = node.GetChildren()
690        for child in children:
691            child_class = child.GetClass()
692            if child_class == 'Type':
693                self.idl_type = type_node_to_type(child)
694            elif child_class == 'ExtAttributes':
695                self.extended_attributes = ext_attributes_node_to_extended_attributes(
696                    child)
697            elif child_class == 'Argument':
698                child_name = child.GetName()
699                if child_name != '...':
700                    raise ValueError(
701                        'Unrecognized Argument node; expected "...", got "%s"'
702                        % child_name)
703                self.is_variadic = bool(child.GetProperty('ELLIPSIS'))
704            elif child_class == 'Default':
705                self.default_value = default_node_to_idl_literal(child)
706            else:
707                raise ValueError('Unrecognized node class: %s' % child_class)
708
709    def accept(self, visitor):
710        visitor.visit_argument(self)
711
712
713def arguments_node_to_arguments(node):
714    # [Constructor] and [CustomConstructor] without arguments (the bare form)
715    # have None instead of an arguments node, but have the same meaning as using
716    # an empty argument list, [Constructor()], so special-case this.
717    # http://www.w3.org/TR/WebIDL/#Constructor
718    if node is None:
719        return []
720    return [IdlArgument(argument_node) for argument_node in node.GetChildren()]
721
722
723################################################################################
724# Stringifiers
725################################################################################
726
727
728class IdlStringifier(object):
729    def __init__(self, node):
730        self.attribute = None
731        self.operation = None
732        self.extended_attributes = {}
733
734        for child in node.GetChildren():
735            child_class = child.GetClass()
736            if child_class == 'Attribute':
737                self.attribute = IdlAttribute(child)
738            elif child_class == 'Operation':
739                operation = IdlOperation(child)
740                if operation.name:
741                    self.operation = operation
742            elif child_class == 'ExtAttributes':
743                self.extended_attributes = ext_attributes_node_to_extended_attributes(
744                    child)
745            else:
746                raise ValueError('Unrecognized node class: %s' % child_class)
747
748        # Copy the stringifier's extended attributes (such as [Unforgable]) onto
749        # the underlying attribute or operation, if there is one.
750        if self.attribute or self.operation:
751            (self.attribute or self.operation).extended_attributes.update(
752                self.extended_attributes)
753
754
755################################################################################
756# Iterable, Maplike, Setlike
757################################################################################
758
759
760class IdlIterableOrMaplikeOrSetlike(TypedObject):
761    def __init__(self, node):
762        self.extended_attributes = {}
763        self.type_children = []
764
765        for child in node.GetChildren():
766            child_class = child.GetClass()
767            if child_class == 'ExtAttributes':
768                self.extended_attributes = ext_attributes_node_to_extended_attributes(
769                    child)
770            elif child_class == 'Type':
771                self.type_children.append(child)
772            else:
773                raise ValueError('Unrecognized node class: %s' % child_class)
774
775
776class IdlIterable(IdlIterableOrMaplikeOrSetlike):
777    idl_type_attributes = ('key_type', 'value_type')
778
779    def __init__(self, node):
780        super(IdlIterable, self).__init__(node)
781
782        if len(self.type_children) == 1:
783            self.key_type = None
784            self.value_type = type_node_to_type(self.type_children[0])
785        elif len(self.type_children) == 2:
786            self.key_type = type_node_to_type(self.type_children[0])
787            self.value_type = type_node_to_type(self.type_children[1])
788        else:
789            raise ValueError('Unexpected number of type children: %d' % len(
790                self.type_children))
791        del self.type_children
792
793    def accept(self, visitor):
794        visitor.visit_iterable(self)
795
796
797class IdlMaplike(IdlIterableOrMaplikeOrSetlike):
798    idl_type_attributes = ('key_type', 'value_type')
799
800    def __init__(self, node):
801        super(IdlMaplike, self).__init__(node)
802
803        self.is_read_only = bool(node.GetProperty('READONLY'))
804
805        if len(self.type_children) == 2:
806            self.key_type = type_node_to_type(self.type_children[0])
807            self.value_type = type_node_to_type(self.type_children[1])
808        else:
809            raise ValueError(
810                'Unexpected number of children: %d' % len(self.type_children))
811        del self.type_children
812
813    def accept(self, visitor):
814        visitor.visit_maplike(self)
815
816
817class IdlSetlike(IdlIterableOrMaplikeOrSetlike):
818    idl_type_attributes = ('value_type', )
819
820    def __init__(self, node):
821        super(IdlSetlike, self).__init__(node)
822
823        self.is_read_only = bool(node.GetProperty('READONLY'))
824
825        if len(self.type_children) == 1:
826            self.value_type = type_node_to_type(self.type_children[0])
827        else:
828            raise ValueError(
829                'Unexpected number of children: %d' % len(self.type_children))
830        del self.type_children
831
832    def accept(self, visitor):
833        visitor.visit_setlike(self)
834
835
836################################################################################
837# Includes statements
838################################################################################
839
840
841class IdlIncludes(object):
842    def __init__(self, node):
843        self.interface = node.GetName()
844        self.mixin = node.GetProperty('REFERENCE')
845
846    def accept(self, visitor):
847        visitor.visit_include(self)
848
849
850################################################################################
851# Extended attributes
852################################################################################
853
854
855class Exposure:
856    """An Exposure holds one Exposed or RuntimeEnabled condition.
857    Each exposure has two properties: exposed and runtime_enabled.
858    Exposure(e, r) corresponds to [Exposed(e r)]. Exposure(e) corresponds to
859    [Exposed=e].
860    """
861
862    def __init__(self, exposed, runtime_enabled=None):
863        self.exposed = exposed
864        self.runtime_enabled = runtime_enabled
865
866
867def ext_attributes_node_to_extended_attributes(node):
868    """
869    Returns:
870      Dictionary of {ExtAttributeName: ExtAttributeValue}.
871      Value is usually a string, with these exceptions:
872      Constructors: value is a list of Arguments nodes, corresponding to
873        possible signatures of the constructor.
874      CustomConstructors: value is a list of Arguments nodes, corresponding to
875        possible signatures of the custom constructor.
876      NamedConstructor: value is a Call node, corresponding to the single
877        signature of the named constructor.
878    """
879    # Primarily just make a dictionary from the children.
880    # The only complexity is handling various types of constructors:
881    # Constructors and Custom Constructors can have duplicate entries due to
882    # overloading, and thus are stored in temporary lists.
883    # However, Named Constructors cannot be overloaded, and thus do not have
884    # a list.
885    # TODO(bashi): Remove |constructors| and |custom_constructors|.
886    constructors = []
887    custom_constructors = []
888    extended_attributes = {}
889
890    def child_node(extended_attribute_node):
891        children = extended_attribute_node.GetChildren()
892        if not children:
893            return None
894        if len(children) > 1:
895            raise ValueError(
896                'ExtAttributes node with %s children, expected at most 1' %
897                len(children))
898        return children[0]
899
900    extended_attribute_node_list = node.GetChildren()
901    for extended_attribute_node in extended_attribute_node_list:
902        name = extended_attribute_node.GetName()
903        child = child_node(extended_attribute_node)
904        child_class = child and child.GetClass()
905        if name == 'Constructor':
906            raise ValueError('[Constructor] is deprecated. Use constructor '
907                             'operations')
908        elif name == 'CustomConstructor':
909            raise ValueError('[CustomConstructor] is deprecated. Use '
910                             'constructor operations with [Custom]')
911        elif name == 'NamedConstructor':
912            if child_class and child_class != 'Call':
913                raise ValueError(
914                    '[NamedConstructor] only supports Call as child, but has child of class: %s'
915                    % child_class)
916            extended_attributes[name] = child
917        elif name == 'Exposed':
918            if child_class and child_class != 'Arguments':
919                raise ValueError(
920                    '[Exposed] only supports Arguments as child, but has child of class: %s'
921                    % child_class)
922            exposures = []
923            if child_class == 'Arguments':
924                exposures = [
925                    Exposure(
926                        exposed=str(arg.idl_type), runtime_enabled=arg.name)
927                    for arg in arguments_node_to_arguments(child)
928                ]
929            else:
930                value = extended_attribute_node.GetProperty('VALUE')
931                if type(value) is str:
932                    exposures = [Exposure(exposed=value)]
933                else:
934                    exposures = [Exposure(exposed=v) for v in value]
935            extended_attributes[name] = exposures
936        elif child:
937            raise ValueError(
938                'ExtAttributes node with unexpected children: %s' % name)
939        else:
940            value = extended_attribute_node.GetProperty('VALUE')
941            extended_attributes[name] = value
942
943    # Store constructors and custom constructors in special list attributes,
944    # which are deleted later. Note plural in key.
945    if constructors:
946        extended_attributes['Constructors'] = constructors
947    if custom_constructors:
948        extended_attributes['CustomConstructors'] = custom_constructors
949
950    return extended_attributes
951
952
953def extended_attributes_to_constructors(extended_attributes):
954    """Returns constructors and custom_constructors (lists of IdlOperations).
955
956    Auxiliary function for IdlInterface.__init__.
957    """
958
959    # TODO(bashi): Remove 'Constructors' and 'CustomConstructors'.
960
961    constructor_list = extended_attributes.get('Constructors', [])
962    constructors = [
963        IdlOperation.constructor_from_arguments_node('Constructor',
964                                                     arguments_node)
965        for arguments_node in constructor_list
966    ]
967
968    custom_constructor_list = extended_attributes.get('CustomConstructors', [])
969    custom_constructors = [
970        IdlOperation.constructor_from_arguments_node('CustomConstructor',
971                                                     arguments_node)
972        for arguments_node in custom_constructor_list
973    ]
974
975    if 'NamedConstructor' in extended_attributes:
976        # FIXME: support overloaded named constructors, and make homogeneous
977        name = 'NamedConstructor'
978        call_node = extended_attributes['NamedConstructor']
979        extended_attributes['NamedConstructor'] = call_node.GetName()
980        children = call_node.GetChildren()
981        if len(children) != 1:
982            raise ValueError('NamedConstructor node expects 1 child, got %s.' %
983                             len(children))
984        arguments_node = children[0]
985        named_constructor = IdlOperation.constructor_from_arguments_node(
986            'NamedConstructor', arguments_node)
987        # FIXME: should return named_constructor separately; appended for Perl
988        constructors.append(named_constructor)
989
990    return constructors, custom_constructors
991
992
993class ConstructorOperation(object):
994    """Represents a constructor operation. This is a tentative object used to
995    create constructors in IdlInterface.
996    """
997
998    def __init__(self, constructor, extended_attributes, is_custom):
999        self.constructor = constructor
1000        self.extended_attributes = extended_attributes
1001        self.is_custom = is_custom
1002
1003
1004def constructor_operation_from_node(node):
1005    """Creates a ConstructorOperation from the given |node|.
1006    """
1007
1008    arguments_node = None
1009    extended_attributes = {}
1010
1011    for child in node.GetChildren():
1012        child_class = child.GetClass()
1013        if child_class == 'Arguments':
1014            arguments_node = child
1015        elif child_class == 'ExtAttributes':
1016            extended_attributes = ext_attributes_node_to_extended_attributes(
1017                child)
1018        else:
1019            raise ValueError('Unrecognized node class: %s' % child_class)
1020
1021    if not arguments_node:
1022        raise ValueError('Expected Arguments node for constructor operation')
1023
1024    if 'Custom' in extended_attributes:
1025        if extended_attributes['Custom']:
1026            raise ValueError('[Custom] should not have a value on constructor '
1027                             'operations')
1028        del extended_attributes['Custom']
1029        constructor = IdlOperation.constructor_from_arguments_node(
1030            'CustomConstructor', arguments_node)
1031        return ConstructorOperation(
1032            constructor, extended_attributes, is_custom=True)
1033    else:
1034        constructor = IdlOperation.constructor_from_arguments_node(
1035            'Constructor', arguments_node)
1036        return ConstructorOperation(
1037            constructor, extended_attributes, is_custom=False)
1038
1039
1040def check_constructor_operations_extended_attributes(current_attrs, new_attrs):
1041    """Raises a ValueError if two extended attribute lists have different values
1042    of constructor related attributes.
1043    """
1044
1045    attrs_to_check = ['CallWith', 'RaisesException']
1046    for attr in attrs_to_check:
1047        if current_attrs.get(attr) != new_attrs.get(attr):
1048            raise ValueError('[{}] should have the same value on all '
1049                             'constructor operations'.format(attr))
1050
1051
1052def convert_constructor_operations_extended_attributes(extended_attributes):
1053    """Converts extended attributes specified on constructor operations to
1054    extended attributes for an interface definition (e.g. [ConstructorCallWith])
1055    """
1056
1057    converted = {}
1058    for name, value in extended_attributes.items():
1059        if name == "CallWith":
1060            converted["ConstructorCallWith"] = value
1061        elif name == "RaisesException":
1062            if value:
1063                raise ValueError(
1064                    '[RaisesException] should not have a value on '
1065                    'constructor operations')
1066            converted["RaisesException"] = 'Constructor'
1067        elif name == "MeasureAs":
1068            converted["MeasureAs"] = value
1069        elif name == "Measure":
1070            converted["Measure"] = None
1071        else:
1072            raise ValueError(
1073                '[{}] is not supported on constructor operations'.format(name))
1074
1075    return converted
1076
1077
1078def clear_constructor_attributes(extended_attributes):
1079    # Deletes Constructor*s* (plural), sets Constructor (singular)
1080    if 'Constructors' in extended_attributes:
1081        del extended_attributes['Constructors']
1082        extended_attributes['Constructor'] = None
1083    if 'CustomConstructors' in extended_attributes:
1084        del extended_attributes['CustomConstructors']
1085        extended_attributes['CustomConstructor'] = None
1086
1087
1088################################################################################
1089# Types
1090################################################################################
1091
1092
1093def type_node_to_type(node):
1094    children = node.GetChildren()
1095    if len(children) != 1 and len(children) != 2:
1096        raise ValueError(
1097            'Type node expects 1 or 2 child(ren), got %d.' % len(children))
1098
1099    base_type = type_node_inner_to_type(children[0])
1100    if len(children) == 2:
1101        extended_attributes = ext_attributes_node_to_extended_attributes(
1102            children[1])
1103        base_type = IdlAnnotatedType(base_type, extended_attributes)
1104
1105    if node.GetProperty('NULLABLE'):
1106        base_type = IdlNullableType(base_type)
1107
1108    return base_type
1109
1110
1111def type_node_inner_to_type(node):
1112    node_class = node.GetClass()
1113    # Note Type*r*ef, not Typedef, meaning the type is an identifier, thus
1114    # either a typedef shorthand (but not a Typedef declaration itself) or an
1115    # interface type. We do not distinguish these, and just use the type name.
1116    if node_class in ['PrimitiveType', 'StringType', 'Typeref']:
1117        # unrestricted syntax: unrestricted double | unrestricted float
1118        is_unrestricted = bool(node.GetProperty('UNRESTRICTED'))
1119        return IdlType(node.GetName(), is_unrestricted=is_unrestricted)
1120    elif node_class == 'Any':
1121        return IdlType('any')
1122    elif node_class in ['Sequence', 'FrozenArray']:
1123        return sequence_node_to_type(node)
1124    elif node_class == 'UnionType':
1125        return union_type_node_to_idl_union_type(node)
1126    elif node_class == 'Promise':
1127        return IdlType('Promise')
1128    elif node_class == 'Record':
1129        return record_node_to_type(node)
1130    raise ValueError('Unrecognized node class: %s' % node_class)
1131
1132
1133def record_node_to_type(node):
1134    children = node.GetChildren()
1135    if len(children) != 2:
1136        raise ValueError('record<K,V> node expects exactly 2 children, got %d'
1137                         % (len(children)))
1138    key_child = children[0]
1139    value_child = children[1]
1140    if key_child.GetClass() != 'StringType':
1141        raise ValueError('Keys in record<K,V> nodes must be string types.')
1142    if value_child.GetClass() != 'Type':
1143        raise ValueError('Unrecognized node class for record<K,V> value: %s' %
1144                         value_child.GetClass())
1145    return IdlRecordType(
1146        IdlType(key_child.GetName()), type_node_to_type(value_child))
1147
1148
1149def sequence_node_to_type(node):
1150    children = node.GetChildren()
1151    class_name = node.GetClass()
1152    if len(children) != 1:
1153        raise ValueError('%s node expects exactly 1 child, got %s' %
1154                         (class_name, len(children)))
1155    sequence_child = children[0]
1156    sequence_child_class = sequence_child.GetClass()
1157    if sequence_child_class != 'Type':
1158        raise ValueError('Unrecognized node class: %s' % sequence_child_class)
1159    element_type = type_node_to_type(sequence_child)
1160    if class_name == 'Sequence':
1161        sequence_type = IdlSequenceType(element_type)
1162    elif class_name == 'FrozenArray':
1163        sequence_type = IdlFrozenArrayType(element_type)
1164    else:
1165        raise ValueError('Unexpected node: %s' % class_name)
1166    if node.GetProperty('NULLABLE'):
1167        return IdlNullableType(sequence_type)
1168    return sequence_type
1169
1170
1171def typedef_node_to_type(node):
1172    children = node.GetChildren()
1173    if len(children) != 1:
1174        raise ValueError(
1175            'Typedef node with %s children, expected 1' % len(children))
1176    child = children[0]
1177    child_class = child.GetClass()
1178    if child_class != 'Type':
1179        raise ValueError('Unrecognized node class: %s' % child_class)
1180    return type_node_to_type(child)
1181
1182
1183def union_type_node_to_idl_union_type(node):
1184    member_types = [
1185        type_node_to_type(member_type_node)
1186        for member_type_node in node.GetChildren()
1187    ]
1188    return IdlUnionType(member_types)
1189
1190
1191################################################################################
1192# Visitor
1193################################################################################
1194
1195
1196class Visitor(object):
1197    """Abstract visitor class for IDL definitions traverse."""
1198
1199    def visit_definitions(self, definitions):
1200        pass
1201
1202    def visit_typed_object(self, typed_object):
1203        pass
1204
1205    def visit_callback_function(self, callback_function):
1206        self.visit_typed_object(callback_function)
1207
1208    def visit_dictionary(self, dictionary):
1209        pass
1210
1211    def visit_dictionary_member(self, member):
1212        self.visit_typed_object(member)
1213
1214    def visit_enumeration(self, enumeration):
1215        pass
1216
1217    def visit_include(self, include):
1218        pass
1219
1220    def visit_interface(self, interface):
1221        pass
1222
1223    def visit_typedef(self, typedef):
1224        self.visit_typed_object(typedef)
1225
1226    def visit_attribute(self, attribute):
1227        self.visit_typed_object(attribute)
1228
1229    def visit_constant(self, constant):
1230        self.visit_typed_object(constant)
1231
1232    def visit_operation(self, operation):
1233        self.visit_typed_object(operation)
1234
1235    def visit_argument(self, argument):
1236        self.visit_typed_object(argument)
1237
1238    def visit_iterable(self, iterable):
1239        self.visit_typed_object(iterable)
1240
1241    def visit_maplike(self, maplike):
1242        self.visit_typed_object(maplike)
1243
1244    def visit_setlike(self, setlike):
1245        self.visit_typed_object(setlike)
1246