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':
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_blacklisted_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_blacklisted_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 'Unforgeable' in self.extended_attributes:
421            raise ValueError('[Unforgeable] cannot appear on interfaces.')
422
423        if constructor_operations or custom_constructor_operations:
424            if self.constructors or self.custom_constructors:
425                raise ValueError('Detected mixed [Constructor] and consructor '
426                                 'operations. Do not use both in a single '
427                                 'interface.')
428            extended_attributes = (
429                convert_constructor_operations_extended_attributes(
430                    constructor_operations_extended_attributes))
431            if any(name in extended_attributes.keys()
432                   for name in self.extended_attributes.keys()):
433                raise ValueError('Detected mixed extended attributes for '
434                                 'both [Constructor] and constructor '
435                                 'operations. Do not use both in a single '
436                                 'interface')
437            self.constructors = constructor_operations
438            self.custom_constructors = custom_constructor_operations
439            self.extended_attributes.update(extended_attributes)
440
441    def accept(self, visitor):
442        visitor.visit_interface(self)
443        for attribute in self.attributes:
444            attribute.accept(visitor)
445        for constant in self.constants:
446            constant.accept(visitor)
447        for constructor in self.constructors:
448            constructor.accept(visitor)
449        for custom_constructor in self.custom_constructors:
450            custom_constructor.accept(visitor)
451        for operation in self.operations:
452            operation.accept(visitor)
453        if self.iterable:
454            self.iterable.accept(visitor)
455        elif self.maplike:
456            self.maplike.accept(visitor)
457        elif self.setlike:
458            self.setlike.accept(visitor)
459
460    def process_stringifier(self):
461        """Add the stringifier's attribute or named operation child, if it has
462        one, as a regular attribute/operation of this interface."""
463        if self.stringifier.attribute:
464            self.attributes.append(self.stringifier.attribute)
465        elif self.stringifier.operation:
466            self.operations.append(self.stringifier.operation)
467
468    def merge(self, other):
469        """Merge in another interface's members (e.g., partial interface)"""
470        self.attributes.extend(other.attributes)
471        self.constants.extend(other.constants)
472        self.operations.extend(other.operations)
473        if self.stringifier is None:
474            self.stringifier = other.stringifier
475
476
477################################################################################
478# Attributes
479################################################################################
480
481
482class IdlAttribute(TypedObject):
483    def __init__(self, node=None):
484        self.is_read_only = bool(
485            node.GetProperty('READONLY')) if node else False
486        self.is_static = bool(node.GetProperty('STATIC')) if node else False
487        self.name = node.GetName() if node else None
488        self.idl_type = None
489        self.extended_attributes = {}
490        # In what interface the attribute is (originally) defined when the
491        # attribute is inherited from an ancestor interface.
492        self.defined_in = None
493
494        if node:
495            children = node.GetChildren()
496            for child in children:
497                child_class = child.GetClass()
498                if child_class == 'Type':
499                    self.idl_type = type_node_to_type(child)
500                elif child_class == 'ExtAttributes':
501                    self.extended_attributes = ext_attributes_node_to_extended_attributes(
502                        child)
503                else:
504                    raise ValueError(
505                        'Unrecognized node class: %s' % child_class)
506
507        if 'Unforgeable' in self.extended_attributes and self.is_static:
508            raise ValueError(
509                '[Unforgeable] cannot appear on static attributes.')
510
511    def accept(self, visitor):
512        visitor.visit_attribute(self)
513
514
515################################################################################
516# Constants
517################################################################################
518
519
520class IdlConstant(TypedObject):
521    def __init__(self, node):
522        children = node.GetChildren()
523        num_children = len(children)
524        if num_children < 2 or num_children > 3:
525            raise ValueError('Expected 2 or 3 children, got %s' % num_children)
526        type_node = children[0]
527        value_node = children[1]
528        value_node_class = value_node.GetClass()
529        if value_node_class != 'Value':
530            raise ValueError('Expected Value node, got %s' % value_node_class)
531
532        self.name = node.GetName()
533        # ConstType is more limited than Type, so subtree is smaller and
534        # we don't use the full type_node_to_type function.
535        self.idl_type = type_node_inner_to_type(type_node)
536        self.value = value_node.GetProperty('VALUE')
537        # In what interface the attribute is (originally) defined when the
538        # attribute is inherited from an ancestor interface.
539        self.defined_in = None
540
541        if num_children == 3:
542            ext_attributes_node = children[2]
543            self.extended_attributes = ext_attributes_node_to_extended_attributes(
544                ext_attributes_node)
545        else:
546            self.extended_attributes = {}
547
548    def accept(self, visitor):
549        visitor.visit_constant(self)
550
551
552################################################################################
553# Literals
554################################################################################
555
556
557class IdlLiteral(object):
558    def __init__(self, idl_type, value):
559        self.idl_type = idl_type
560        self.value = value
561        self.is_null = False
562
563    def __str__(self):
564        if self.idl_type == 'DOMString':
565            if self.value:
566                return '"%s"' % self.value
567            else:
568                return 'WTF::g_empty_string'
569        if self.idl_type == 'integer':
570            return '%d' % self.value
571        if self.idl_type == 'float':
572            return '%g' % self.value
573        if self.idl_type == 'boolean':
574            return 'true' if self.value else 'false'
575        if self.idl_type == 'dictionary':
576            return self.value
577        raise ValueError('Unsupported literal type: %s' % self.idl_type)
578
579
580class IdlLiteralNull(IdlLiteral):
581    def __init__(self):
582        self.idl_type = 'NULL'
583        self.value = None
584        self.is_null = True
585
586    def __str__(self):
587        return 'nullptr'
588
589
590def default_node_to_idl_literal(node):
591    idl_type = node.GetProperty('TYPE')
592    value = node.GetProperty('VALUE')
593    if idl_type == 'DOMString':
594        if '"' in value or '\\' in value:
595            raise ValueError('Unsupported string value: %r' % value)
596        return IdlLiteral(idl_type, value)
597    if idl_type == 'integer':
598        return IdlLiteral(idl_type, int(value, base=0))
599    if idl_type == 'float':
600        return IdlLiteral(idl_type, float(value))
601    if idl_type in ['boolean', 'sequence']:
602        return IdlLiteral(idl_type, value)
603    if idl_type == 'NULL':
604        return IdlLiteralNull()
605    if idl_type == 'dictionary':
606        return IdlLiteral(idl_type, value)
607    raise ValueError('Unrecognized default value type: %s' % idl_type)
608
609
610################################################################################
611# Operations
612################################################################################
613
614
615class IdlOperation(TypedObject):
616    def __init__(self, node=None):
617        self.arguments = []
618        self.extended_attributes = {}
619        self.specials = []
620        self.is_constructor = False
621        self.idl_type = None
622        self.is_static = False
623        # In what interface the attribute is (originally) defined when the
624        # attribute is inherited from an ancestor interface.
625        self.defined_in = None
626
627        if not node:
628            return
629
630        self.name = node.GetName()
631
632        self.is_static = bool(node.GetProperty('STATIC'))
633        property_dictionary = node.GetProperties()
634        for special_keyword in SPECIAL_KEYWORD_LIST:
635            if special_keyword in property_dictionary:
636                self.specials.append(special_keyword.lower())
637
638        children = node.GetChildren()
639        for child in children:
640            child_class = child.GetClass()
641            if child_class == 'Arguments':
642                self.arguments = arguments_node_to_arguments(child)
643            elif child_class == 'Type':
644                self.idl_type = type_node_to_type(child)
645            elif child_class == 'ExtAttributes':
646                self.extended_attributes = ext_attributes_node_to_extended_attributes(
647                    child)
648            else:
649                raise ValueError('Unrecognized node class: %s' % child_class)
650
651        if 'Unforgeable' in self.extended_attributes and self.is_static:
652            raise ValueError(
653                '[Unforgeable] cannot appear on static operations.')
654
655    @classmethod
656    def constructor_from_arguments_node(cls, name, arguments_node):
657        constructor = cls()
658        constructor.name = name
659        constructor.arguments = arguments_node_to_arguments(arguments_node)
660        constructor.is_constructor = True
661        return constructor
662
663    def accept(self, visitor):
664        visitor.visit_operation(self)
665        for argument in self.arguments:
666            argument.accept(visitor)
667
668
669################################################################################
670# Arguments
671################################################################################
672
673
674class IdlArgument(TypedObject):
675    def __init__(self, node=None):
676        self.extended_attributes = {}
677        self.idl_type = None
678        self.is_optional = False  # syntax: (optional T)
679        self.is_variadic = False  # syntax: (T...)
680        self.default_value = None
681
682        if not node:
683            return
684
685        self.is_optional = node.GetProperty('OPTIONAL')
686        self.name = node.GetName()
687
688        children = node.GetChildren()
689        for child in children:
690            child_class = child.GetClass()
691            if child_class == 'Type':
692                self.idl_type = type_node_to_type(child)
693            elif child_class == 'ExtAttributes':
694                self.extended_attributes = ext_attributes_node_to_extended_attributes(
695                    child)
696            elif child_class == 'Argument':
697                child_name = child.GetName()
698                if child_name != '...':
699                    raise ValueError(
700                        'Unrecognized Argument node; expected "...", got "%s"'
701                        % child_name)
702                self.is_variadic = bool(child.GetProperty('ELLIPSIS'))
703            elif child_class == 'Default':
704                self.default_value = default_node_to_idl_literal(child)
705            else:
706                raise ValueError('Unrecognized node class: %s' % child_class)
707
708    def accept(self, visitor):
709        visitor.visit_argument(self)
710
711
712def arguments_node_to_arguments(node):
713    # [Constructor] and [CustomConstructor] without arguments (the bare form)
714    # have None instead of an arguments node, but have the same meaning as using
715    # an empty argument list, [Constructor()], so special-case this.
716    # http://www.w3.org/TR/WebIDL/#Constructor
717    if node is None:
718        return []
719    return [IdlArgument(argument_node) for argument_node in node.GetChildren()]
720
721
722################################################################################
723# Stringifiers
724################################################################################
725
726
727class IdlStringifier(object):
728    def __init__(self, node):
729        self.attribute = None
730        self.operation = None
731        self.extended_attributes = {}
732
733        for child in node.GetChildren():
734            child_class = child.GetClass()
735            if child_class == 'Attribute':
736                self.attribute = IdlAttribute(child)
737            elif child_class == 'Operation':
738                operation = IdlOperation(child)
739                if operation.name:
740                    self.operation = operation
741            elif child_class == 'ExtAttributes':
742                self.extended_attributes = ext_attributes_node_to_extended_attributes(
743                    child)
744            else:
745                raise ValueError('Unrecognized node class: %s' % child_class)
746
747        # Copy the stringifier's extended attributes (such as [Unforgable]) onto
748        # the underlying attribute or operation, if there is one.
749        if self.attribute or self.operation:
750            (self.attribute or self.operation).extended_attributes.update(
751                self.extended_attributes)
752
753
754################################################################################
755# Iterable, Maplike, Setlike
756################################################################################
757
758
759class IdlIterableOrMaplikeOrSetlike(TypedObject):
760    def __init__(self, node):
761        self.extended_attributes = {}
762        self.type_children = []
763
764        for child in node.GetChildren():
765            child_class = child.GetClass()
766            if child_class == 'ExtAttributes':
767                self.extended_attributes = ext_attributes_node_to_extended_attributes(
768                    child)
769            elif child_class == 'Type':
770                self.type_children.append(child)
771            else:
772                raise ValueError('Unrecognized node class: %s' % child_class)
773
774
775class IdlIterable(IdlIterableOrMaplikeOrSetlike):
776    idl_type_attributes = ('key_type', 'value_type')
777
778    def __init__(self, node):
779        super(IdlIterable, self).__init__(node)
780
781        if len(self.type_children) == 1:
782            self.key_type = None
783            self.value_type = type_node_to_type(self.type_children[0])
784        elif len(self.type_children) == 2:
785            self.key_type = type_node_to_type(self.type_children[0])
786            self.value_type = type_node_to_type(self.type_children[1])
787        else:
788            raise ValueError('Unexpected number of type children: %d' % len(
789                self.type_children))
790        del self.type_children
791
792    def accept(self, visitor):
793        visitor.visit_iterable(self)
794
795
796class IdlMaplike(IdlIterableOrMaplikeOrSetlike):
797    idl_type_attributes = ('key_type', 'value_type')
798
799    def __init__(self, node):
800        super(IdlMaplike, self).__init__(node)
801
802        self.is_read_only = bool(node.GetProperty('READONLY'))
803
804        if len(self.type_children) == 2:
805            self.key_type = type_node_to_type(self.type_children[0])
806            self.value_type = type_node_to_type(self.type_children[1])
807        else:
808            raise ValueError(
809                'Unexpected number of children: %d' % len(self.type_children))
810        del self.type_children
811
812    def accept(self, visitor):
813        visitor.visit_maplike(self)
814
815
816class IdlSetlike(IdlIterableOrMaplikeOrSetlike):
817    idl_type_attributes = ('value_type', )
818
819    def __init__(self, node):
820        super(IdlSetlike, self).__init__(node)
821
822        self.is_read_only = bool(node.GetProperty('READONLY'))
823
824        if len(self.type_children) == 1:
825            self.value_type = type_node_to_type(self.type_children[0])
826        else:
827            raise ValueError(
828                'Unexpected number of children: %d' % len(self.type_children))
829        del self.type_children
830
831    def accept(self, visitor):
832        visitor.visit_setlike(self)
833
834
835################################################################################
836# Includes statements
837################################################################################
838
839
840class IdlIncludes(object):
841    def __init__(self, node):
842        self.interface = node.GetName()
843        self.mixin = node.GetProperty('REFERENCE')
844
845    def accept(self, visitor):
846        visitor.visit_include(self)
847
848
849################################################################################
850# Extended attributes
851################################################################################
852
853
854class Exposure:
855    """An Exposure holds one Exposed or RuntimeEnabled condition.
856    Each exposure has two properties: exposed and runtime_enabled.
857    Exposure(e, r) corresponds to [Exposed(e r)]. Exposure(e) corresponds to
858    [Exposed=e].
859    """
860
861    def __init__(self, exposed, runtime_enabled=None):
862        self.exposed = exposed
863        self.runtime_enabled = runtime_enabled
864
865
866def ext_attributes_node_to_extended_attributes(node):
867    """
868    Returns:
869      Dictionary of {ExtAttributeName: ExtAttributeValue}.
870      Value is usually a string, with these exceptions:
871      Constructors: value is a list of Arguments nodes, corresponding to
872        possible signatures of the constructor.
873      CustomConstructors: value is a list of Arguments nodes, corresponding to
874        possible signatures of the custom constructor.
875      NamedConstructor: value is a Call node, corresponding to the single
876        signature of the named constructor.
877    """
878    # Primarily just make a dictionary from the children.
879    # The only complexity is handling various types of constructors:
880    # Constructors and Custom Constructors can have duplicate entries due to
881    # overloading, and thus are stored in temporary lists.
882    # However, Named Constructors cannot be overloaded, and thus do not have
883    # a list.
884    # TODO(bashi): Remove |constructors| and |custom_constructors|.
885    constructors = []
886    custom_constructors = []
887    extended_attributes = {}
888
889    def child_node(extended_attribute_node):
890        children = extended_attribute_node.GetChildren()
891        if not children:
892            return None
893        if len(children) > 1:
894            raise ValueError(
895                'ExtAttributes node with %s children, expected at most 1' %
896                len(children))
897        return children[0]
898
899    extended_attribute_node_list = node.GetChildren()
900    for extended_attribute_node in extended_attribute_node_list:
901        name = extended_attribute_node.GetName()
902        child = child_node(extended_attribute_node)
903        child_class = child and child.GetClass()
904        if name == 'Constructor':
905            raise ValueError('[Constructor] is deprecated. Use constructor '
906                             'operations')
907        elif name == 'CustomConstructor':
908            raise ValueError('[CustomConstructor] is deprecated. Use '
909                             'constructor operations with [Custom]')
910        elif name == 'NamedConstructor':
911            if child_class and child_class != 'Call':
912                raise ValueError(
913                    '[NamedConstructor] only supports Call as child, but has child of class: %s'
914                    % child_class)
915            extended_attributes[name] = child
916        elif name == 'Exposed':
917            if child_class and child_class != 'Arguments':
918                raise ValueError(
919                    '[Exposed] only supports Arguments as child, but has child of class: %s'
920                    % child_class)
921            exposures = []
922            if child_class == 'Arguments':
923                exposures = [
924                    Exposure(
925                        exposed=str(arg.idl_type), runtime_enabled=arg.name)
926                    for arg in arguments_node_to_arguments(child)
927                ]
928            else:
929                value = extended_attribute_node.GetProperty('VALUE')
930                if type(value) is str:
931                    exposures = [Exposure(exposed=value)]
932                else:
933                    exposures = [Exposure(exposed=v) for v in value]
934            extended_attributes[name] = exposures
935        elif child:
936            raise ValueError(
937                'ExtAttributes node with unexpected children: %s' % name)
938        else:
939            value = extended_attribute_node.GetProperty('VALUE')
940            extended_attributes[name] = value
941
942    # Store constructors and custom constructors in special list attributes,
943    # which are deleted later. Note plural in key.
944    if constructors:
945        extended_attributes['Constructors'] = constructors
946    if custom_constructors:
947        extended_attributes['CustomConstructors'] = custom_constructors
948
949    return extended_attributes
950
951
952def extended_attributes_to_constructors(extended_attributes):
953    """Returns constructors and custom_constructors (lists of IdlOperations).
954
955    Auxiliary function for IdlInterface.__init__.
956    """
957
958    # TODO(bashi): Remove 'Constructors' and 'CustomConstructors'.
959
960    constructor_list = extended_attributes.get('Constructors', [])
961    constructors = [
962        IdlOperation.constructor_from_arguments_node('Constructor',
963                                                     arguments_node)
964        for arguments_node in constructor_list
965    ]
966
967    custom_constructor_list = extended_attributes.get('CustomConstructors', [])
968    custom_constructors = [
969        IdlOperation.constructor_from_arguments_node('CustomConstructor',
970                                                     arguments_node)
971        for arguments_node in custom_constructor_list
972    ]
973
974    if 'NamedConstructor' in extended_attributes:
975        # FIXME: support overloaded named constructors, and make homogeneous
976        name = 'NamedConstructor'
977        call_node = extended_attributes['NamedConstructor']
978        extended_attributes['NamedConstructor'] = call_node.GetName()
979        children = call_node.GetChildren()
980        if len(children) != 1:
981            raise ValueError('NamedConstructor node expects 1 child, got %s.' %
982                             len(children))
983        arguments_node = children[0]
984        named_constructor = IdlOperation.constructor_from_arguments_node(
985            'NamedConstructor', arguments_node)
986        # FIXME: should return named_constructor separately; appended for Perl
987        constructors.append(named_constructor)
988
989    return constructors, custom_constructors
990
991
992class ConstructorOperation(object):
993    """Represents a constructor operation. This is a tentative object used to
994    create constructors in IdlInterface.
995    """
996
997    def __init__(self, constructor, extended_attributes, is_custom):
998        self.constructor = constructor
999        self.extended_attributes = extended_attributes
1000        self.is_custom = is_custom
1001
1002
1003def constructor_operation_from_node(node):
1004    """Creates a ConstructorOperation from the given |node|.
1005    """
1006
1007    arguments_node = None
1008    extended_attributes = {}
1009
1010    for child in node.GetChildren():
1011        child_class = child.GetClass()
1012        if child_class == 'Arguments':
1013            arguments_node = child
1014        elif child_class == 'ExtAttributes':
1015            extended_attributes = ext_attributes_node_to_extended_attributes(
1016                child)
1017        else:
1018            raise ValueError('Unrecognized node class: %s' % child_class)
1019
1020    if not arguments_node:
1021        raise ValueError('Expected Arguments node for constructor operation')
1022
1023    if 'Custom' in extended_attributes:
1024        if extended_attributes['Custom']:
1025            raise ValueError('[Custom] should not have a value on constructor '
1026                             'operations')
1027        del extended_attributes['Custom']
1028        constructor = IdlOperation.constructor_from_arguments_node(
1029            'CustomConstructor', arguments_node)
1030        return ConstructorOperation(
1031            constructor, extended_attributes, is_custom=True)
1032    else:
1033        constructor = IdlOperation.constructor_from_arguments_node(
1034            'Constructor', arguments_node)
1035        return ConstructorOperation(
1036            constructor, extended_attributes, is_custom=False)
1037
1038
1039def check_constructor_operations_extended_attributes(current_attrs, new_attrs):
1040    """Raises a ValueError if two extended attribute lists have different values
1041    of constructor related attributes.
1042    """
1043
1044    attrs_to_check = ['CallWith', 'RaisesException']
1045    for attr in attrs_to_check:
1046        if current_attrs.get(attr) != new_attrs.get(attr):
1047            raise ValueError('[{}] should have the same value on all '
1048                             'constructor operations'.format(attr))
1049
1050
1051def convert_constructor_operations_extended_attributes(extended_attributes):
1052    """Converts extended attributes specified on constructor operations to
1053    extended attributes for an interface definition (e.g. [ConstructorCallWith])
1054    """
1055
1056    converted = {}
1057    for name, value in extended_attributes.items():
1058        if name == "CallWith":
1059            converted["ConstructorCallWith"] = value
1060        elif name == "RaisesException":
1061            if value:
1062                raise ValueError(
1063                    '[RaisesException] should not have a value on '
1064                    'constructor operations')
1065            converted["RaisesException"] = 'Constructor'
1066        elif name == "MeasureAs":
1067            converted["MeasureAs"] = value
1068        elif name == "Measure":
1069            converted["Measure"] = None
1070        else:
1071            raise ValueError(
1072                '[{}] is not supported on constructor operations'.format(name))
1073
1074    return converted
1075
1076
1077def clear_constructor_attributes(extended_attributes):
1078    # Deletes Constructor*s* (plural), sets Constructor (singular)
1079    if 'Constructors' in extended_attributes:
1080        del extended_attributes['Constructors']
1081        extended_attributes['Constructor'] = None
1082    if 'CustomConstructors' in extended_attributes:
1083        del extended_attributes['CustomConstructors']
1084        extended_attributes['CustomConstructor'] = None
1085
1086
1087################################################################################
1088# Types
1089################################################################################
1090
1091
1092def type_node_to_type(node):
1093    children = node.GetChildren()
1094    if len(children) != 1 and len(children) != 2:
1095        raise ValueError(
1096            'Type node expects 1 or 2 child(ren), got %d.' % len(children))
1097
1098    base_type = type_node_inner_to_type(children[0])
1099    if len(children) == 2:
1100        extended_attributes = ext_attributes_node_to_extended_attributes(
1101            children[1])
1102        base_type = IdlAnnotatedType(base_type, extended_attributes)
1103
1104    if node.GetProperty('NULLABLE'):
1105        base_type = IdlNullableType(base_type)
1106
1107    return base_type
1108
1109
1110def type_node_inner_to_type(node):
1111    node_class = node.GetClass()
1112    # Note Type*r*ef, not Typedef, meaning the type is an identifier, thus
1113    # either a typedef shorthand (but not a Typedef declaration itself) or an
1114    # interface type. We do not distinguish these, and just use the type name.
1115    if node_class in ['PrimitiveType', 'StringType', 'Typeref']:
1116        # unrestricted syntax: unrestricted double | unrestricted float
1117        is_unrestricted = bool(node.GetProperty('UNRESTRICTED'))
1118        return IdlType(node.GetName(), is_unrestricted=is_unrestricted)
1119    elif node_class == 'Any':
1120        return IdlType('any')
1121    elif node_class in ['Sequence', 'FrozenArray']:
1122        return sequence_node_to_type(node)
1123    elif node_class == 'UnionType':
1124        return union_type_node_to_idl_union_type(node)
1125    elif node_class == 'Promise':
1126        return IdlType('Promise')
1127    elif node_class == 'Record':
1128        return record_node_to_type(node)
1129    raise ValueError('Unrecognized node class: %s' % node_class)
1130
1131
1132def record_node_to_type(node):
1133    children = node.GetChildren()
1134    if len(children) != 2:
1135        raise ValueError('record<K,V> node expects exactly 2 children, got %d'
1136                         % (len(children)))
1137    key_child = children[0]
1138    value_child = children[1]
1139    if key_child.GetClass() != 'StringType':
1140        raise ValueError('Keys in record<K,V> nodes must be string types.')
1141    if value_child.GetClass() != 'Type':
1142        raise ValueError('Unrecognized node class for record<K,V> value: %s' %
1143                         value_child.GetClass())
1144    return IdlRecordType(
1145        IdlType(key_child.GetName()), type_node_to_type(value_child))
1146
1147
1148def sequence_node_to_type(node):
1149    children = node.GetChildren()
1150    class_name = node.GetClass()
1151    if len(children) != 1:
1152        raise ValueError('%s node expects exactly 1 child, got %s' %
1153                         (class_name, len(children)))
1154    sequence_child = children[0]
1155    sequence_child_class = sequence_child.GetClass()
1156    if sequence_child_class != 'Type':
1157        raise ValueError('Unrecognized node class: %s' % sequence_child_class)
1158    element_type = type_node_to_type(sequence_child)
1159    if class_name == 'Sequence':
1160        sequence_type = IdlSequenceType(element_type)
1161    elif class_name == 'FrozenArray':
1162        sequence_type = IdlFrozenArrayType(element_type)
1163    else:
1164        raise ValueError('Unexpected node: %s' % class_name)
1165    if node.GetProperty('NULLABLE'):
1166        return IdlNullableType(sequence_type)
1167    return sequence_type
1168
1169
1170def typedef_node_to_type(node):
1171    children = node.GetChildren()
1172    if len(children) != 1:
1173        raise ValueError(
1174            'Typedef node with %s children, expected 1' % len(children))
1175    child = children[0]
1176    child_class = child.GetClass()
1177    if child_class != 'Type':
1178        raise ValueError('Unrecognized node class: %s' % child_class)
1179    return type_node_to_type(child)
1180
1181
1182def union_type_node_to_idl_union_type(node):
1183    member_types = [
1184        type_node_to_type(member_type_node)
1185        for member_type_node in node.GetChildren()
1186    ]
1187    return IdlUnionType(member_types)
1188
1189
1190################################################################################
1191# Visitor
1192################################################################################
1193
1194
1195class Visitor(object):
1196    """Abstract visitor class for IDL definitions traverse."""
1197
1198    def visit_definitions(self, definitions):
1199        pass
1200
1201    def visit_typed_object(self, typed_object):
1202        pass
1203
1204    def visit_callback_function(self, callback_function):
1205        self.visit_typed_object(callback_function)
1206
1207    def visit_dictionary(self, dictionary):
1208        pass
1209
1210    def visit_dictionary_member(self, member):
1211        self.visit_typed_object(member)
1212
1213    def visit_enumeration(self, enumeration):
1214        pass
1215
1216    def visit_include(self, include):
1217        pass
1218
1219    def visit_interface(self, interface):
1220        pass
1221
1222    def visit_typedef(self, typedef):
1223        self.visit_typed_object(typedef)
1224
1225    def visit_attribute(self, attribute):
1226        self.visit_typed_object(attribute)
1227
1228    def visit_constant(self, constant):
1229        self.visit_typed_object(constant)
1230
1231    def visit_operation(self, operation):
1232        self.visit_typed_object(operation)
1233
1234    def visit_argument(self, argument):
1235        self.visit_typed_object(argument)
1236
1237    def visit_iterable(self, iterable):
1238        self.visit_typed_object(iterable)
1239
1240    def visit_maplike(self, maplike):
1241        self.visit_typed_object(maplike)
1242
1243    def visit_setlike(self, setlike):
1244        self.visit_typed_object(setlike)
1245