1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5""" A WebIDL parser. """
6
7from __future__ import print_function
8from ply import lex, yacc
9import re
10import os
11import traceback
12import math
13import string
14from collections import defaultdict, OrderedDict
15from itertools import chain
16
17# Machinery
18
19
20def parseInt(literal):
21    string = literal
22    sign = 0
23    base = 0
24
25    if string[0] == "-":
26        sign = -1
27        string = string[1:]
28    else:
29        sign = 1
30
31    if string[0] == "0" and len(string) > 1:
32        if string[1] == "x" or string[1] == "X":
33            base = 16
34            string = string[2:]
35        else:
36            base = 8
37            string = string[1:]
38    else:
39        base = 10
40
41    value = int(string, base)
42    return value * sign
43
44
45def enum(*names, **kw):
46    class Foo(object):
47        attrs = OrderedDict()
48
49        def __init__(self, names):
50            for v, k in enumerate(names):
51                self.attrs[k] = v
52
53        def __getattr__(self, attr):
54            if attr in self.attrs:
55                return self.attrs[attr]
56            raise AttributeError
57
58        def __setattr__(self, name, value):  # this makes it read-only
59            raise NotImplementedError
60
61    if "base" not in kw:
62        return Foo(names)
63    return Foo(chain(kw["base"].attrs.keys(), names))
64
65
66class WebIDLError(Exception):
67    def __init__(self, message, locations, warning=False):
68        self.message = message
69        self.locations = [str(loc) for loc in locations]
70        self.warning = warning
71
72    def __str__(self):
73        return "%s: %s%s%s" % (
74            self.warning and "warning" or "error",
75            self.message,
76            ", " if len(self.locations) != 0 else "",
77            "\n".join(self.locations),
78        )
79
80
81class Location(object):
82    def __init__(self, lexer, lineno, lexpos, filename):
83        self._line = None
84        self._lineno = lineno
85        self._lexpos = lexpos
86        self._lexdata = lexer.lexdata
87        self._file = filename if filename else "<unknown>"
88
89    def __eq__(self, other):
90        return self._lexpos == other._lexpos and self._file == other._file
91
92    def filename(self):
93        return self._file
94
95    def resolve(self):
96        if self._line:
97            return
98
99        startofline = self._lexdata.rfind("\n", 0, self._lexpos) + 1
100        endofline = self._lexdata.find("\n", self._lexpos, self._lexpos + 80)
101        if endofline != -1:
102            self._line = self._lexdata[startofline:endofline]
103        else:
104            self._line = self._lexdata[startofline:]
105        self._colno = self._lexpos - startofline
106
107        # Our line number seems to point to the start of self._lexdata
108        self._lineno += self._lexdata.count("\n", 0, startofline)
109
110    def get(self):
111        self.resolve()
112        return "%s line %s:%s" % (self._file, self._lineno, self._colno)
113
114    def _pointerline(self):
115        return " " * self._colno + "^"
116
117    def __str__(self):
118        self.resolve()
119        return "%s line %s:%s\n%s\n%s" % (
120            self._file,
121            self._lineno,
122            self._colno,
123            self._line,
124            self._pointerline(),
125        )
126
127
128class BuiltinLocation(object):
129    def __init__(self, text):
130        self.msg = text + "\n"
131
132    def __eq__(self, other):
133        return isinstance(other, BuiltinLocation) and self.msg == other.msg
134
135    def filename(self):
136        return "<builtin>"
137
138    def resolve(self):
139        pass
140
141    def get(self):
142        return self.msg
143
144    def __str__(self):
145        return self.get()
146
147
148# Data Model
149
150
151class IDLObject(object):
152    def __init__(self, location):
153        self.location = location
154        self.userData = dict()
155
156    def filename(self):
157        return self.location.filename()
158
159    def isInterface(self):
160        return False
161
162    def isNamespace(self):
163        return False
164
165    def isInterfaceMixin(self):
166        return False
167
168    def isEnum(self):
169        return False
170
171    def isCallback(self):
172        return False
173
174    def isType(self):
175        return False
176
177    def isDictionary(self):
178        return False
179
180    def isUnion(self):
181        return False
182
183    def isTypedef(self):
184        return False
185
186    def getUserData(self, key, default):
187        return self.userData.get(key, default)
188
189    def setUserData(self, key, value):
190        self.userData[key] = value
191
192    def addExtendedAttributes(self, attrs):
193        assert False  # Override me!
194
195    def handleExtendedAttribute(self, attr):
196        assert False  # Override me!
197
198    def _getDependentObjects(self):
199        assert False  # Override me!
200
201    def getDeps(self, visited=None):
202        """Return a set of files that this object depends on.  If any of
203        these files are changed the parser needs to be rerun to regenerate
204        a new IDLObject.
205
206        The visited argument is a set of all the objects already visited.
207        We must test to see if we are in it, and if so, do nothing.  This
208        prevents infinite recursion."""
209
210        # NB: We can't use visited=set() above because the default value is
211        # evaluated when the def statement is evaluated, not when the function
212        # is executed, so there would be one set for all invocations.
213        if visited is None:
214            visited = set()
215
216        if self in visited:
217            return set()
218
219        visited.add(self)
220
221        deps = set()
222        if self.filename() != "<builtin>":
223            deps.add(self.filename())
224
225        for d in self._getDependentObjects():
226            deps.update(d.getDeps(visited))
227
228        return deps
229
230
231class IDLScope(IDLObject):
232    def __init__(self, location, parentScope, identifier):
233        IDLObject.__init__(self, location)
234
235        self.parentScope = parentScope
236        if identifier:
237            assert isinstance(identifier, IDLIdentifier)
238            self._name = identifier
239        else:
240            self._name = None
241
242        self._dict = {}
243        self.globalNames = set()
244        # A mapping from global name to the set of global interfaces
245        # that have that global name.
246        self.globalNameMapping = defaultdict(set)
247
248    def __str__(self):
249        return self.QName()
250
251    def QName(self):
252        # It's possible for us to be called before __init__ has been called, for
253        # the IDLObjectWithScope case.  In that case, self._name won't be set yet.
254        if hasattr(self, "_name"):
255            name = self._name
256        else:
257            name = None
258        if name:
259            return name.QName() + "::"
260        return "::"
261
262    def ensureUnique(self, identifier, object):
263        """
264        Ensure that there is at most one 'identifier' in scope ('self').
265        Note that object can be None.  This occurs if we end up here for an
266        interface type we haven't seen yet.
267        """
268        assert isinstance(identifier, IDLUnresolvedIdentifier)
269        assert not object or isinstance(object, IDLObjectWithIdentifier)
270        assert not object or object.identifier == identifier
271
272        if identifier.name in self._dict:
273            if not object:
274                return
275
276            # ensureUnique twice with the same object is not allowed
277            assert id(object) != id(self._dict[identifier.name])
278
279            replacement = self.resolveIdentifierConflict(
280                self, identifier, self._dict[identifier.name], object
281            )
282            self._dict[identifier.name] = replacement
283            return
284
285        assert object
286
287        self._dict[identifier.name] = object
288
289    def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
290        if (
291            isinstance(originalObject, IDLExternalInterface)
292            and isinstance(newObject, IDLExternalInterface)
293            and originalObject.identifier.name == newObject.identifier.name
294        ):
295            return originalObject
296
297        if isinstance(originalObject, IDLExternalInterface) or isinstance(
298            newObject, IDLExternalInterface
299        ):
300            raise WebIDLError(
301                "Name collision between "
302                "interface declarations for identifier '%s' at '%s' and '%s'"
303                % (identifier.name, originalObject.location, newObject.location),
304                [],
305            )
306
307        if isinstance(originalObject, IDLDictionary) or isinstance(
308            newObject, IDLDictionary
309        ):
310            raise WebIDLError(
311                "Name collision between dictionary declarations for "
312                "identifier '%s'.\n%s\n%s"
313                % (identifier.name, originalObject.location, newObject.location),
314                [],
315            )
316
317        # We do the merging of overloads here as opposed to in IDLInterface
318        # because we need to merge overloads of LegacyFactoryFunctions and we need to
319        # detect conflicts in those across interfaces. See also the comment in
320        # IDLInterface.addExtendedAttributes for "LegacyFactoryFunction".
321        if isinstance(originalObject, IDLMethod) and isinstance(newObject, IDLMethod):
322            return originalObject.addOverload(newObject)
323
324        # Default to throwing, derived classes can override.
325        conflictdesc = "\n\t%s at %s\n\t%s at %s" % (
326            originalObject,
327            originalObject.location,
328            newObject,
329            newObject.location,
330        )
331
332        raise WebIDLError(
333            "Multiple unresolvable definitions of identifier '%s' in scope '%s'%s"
334            % (identifier.name, str(self), conflictdesc),
335            [],
336        )
337
338    def _lookupIdentifier(self, identifier):
339        return self._dict[identifier.name]
340
341    def lookupIdentifier(self, identifier):
342        assert isinstance(identifier, IDLIdentifier)
343        assert identifier.scope == self
344        return self._lookupIdentifier(identifier)
345
346    def addIfaceGlobalNames(self, interfaceName, globalNames):
347        """Record the global names (from |globalNames|) that can be used in
348        [Exposed] to expose things in a global named |interfaceName|"""
349        self.globalNames.update(globalNames)
350        for name in globalNames:
351            self.globalNameMapping[name].add(interfaceName)
352
353
354class IDLIdentifier(IDLObject):
355    def __init__(self, location, scope, name):
356        IDLObject.__init__(self, location)
357
358        self.name = name
359        assert isinstance(scope, IDLScope)
360        self.scope = scope
361
362    def __str__(self):
363        return self.QName()
364
365    def QName(self):
366        return self.scope.QName() + self.name
367
368    def __hash__(self):
369        return self.QName().__hash__()
370
371    def __eq__(self, other):
372        return self.QName() == other.QName()
373
374    def object(self):
375        return self.scope.lookupIdentifier(self)
376
377
378class IDLUnresolvedIdentifier(IDLObject):
379    def __init__(
380        self, location, name, allowDoubleUnderscore=False, allowForbidden=False
381    ):
382        IDLObject.__init__(self, location)
383
384        assert len(name) > 0
385
386        if name == "__noSuchMethod__":
387            raise WebIDLError("__noSuchMethod__ is deprecated", [location])
388
389        if name[:2] == "__" and not allowDoubleUnderscore:
390            raise WebIDLError("Identifiers beginning with __ are reserved", [location])
391        if name[0] == "_" and not allowDoubleUnderscore:
392            name = name[1:]
393        if name in ["constructor", "toString"] and not allowForbidden:
394            raise WebIDLError(
395                "Cannot use reserved identifier '%s'" % (name), [location]
396            )
397
398        self.name = name
399
400    def __str__(self):
401        return self.QName()
402
403    def QName(self):
404        return "<unresolved scope>::" + self.name
405
406    def resolve(self, scope, object):
407        assert isinstance(scope, IDLScope)
408        assert not object or isinstance(object, IDLObjectWithIdentifier)
409        assert not object or object.identifier == self
410
411        scope.ensureUnique(self, object)
412
413        identifier = IDLIdentifier(self.location, scope, self.name)
414        if object:
415            object.identifier = identifier
416        return identifier
417
418    def finish(self):
419        assert False  # Should replace with a resolved identifier first.
420
421
422class IDLObjectWithIdentifier(IDLObject):
423    def __init__(self, location, parentScope, identifier):
424        IDLObject.__init__(self, location)
425
426        assert isinstance(identifier, IDLUnresolvedIdentifier)
427
428        self.identifier = identifier
429
430        if parentScope:
431            self.resolve(parentScope)
432
433    def resolve(self, parentScope):
434        assert isinstance(parentScope, IDLScope)
435        assert isinstance(self.identifier, IDLUnresolvedIdentifier)
436        self.identifier.resolve(parentScope, self)
437
438
439class IDLObjectWithScope(IDLObjectWithIdentifier, IDLScope):
440    def __init__(self, location, parentScope, identifier):
441        assert isinstance(identifier, IDLUnresolvedIdentifier)
442
443        IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
444        IDLScope.__init__(self, location, parentScope, self.identifier)
445
446
447class IDLIdentifierPlaceholder(IDLObjectWithIdentifier):
448    def __init__(self, location, identifier):
449        assert isinstance(identifier, IDLUnresolvedIdentifier)
450        IDLObjectWithIdentifier.__init__(self, location, None, identifier)
451
452    def finish(self, scope):
453        try:
454            scope._lookupIdentifier(self.identifier)
455        except:
456            raise WebIDLError(
457                "Unresolved type '%s'." % self.identifier, [self.location]
458            )
459
460        obj = self.identifier.resolve(scope, None)
461        return scope.lookupIdentifier(obj)
462
463
464class IDLExposureMixins:
465    def __init__(self, location):
466        # _exposureGlobalNames are the global names listed in our [Exposed]
467        # extended attribute.  exposureSet is the exposure set as defined in the
468        # Web IDL spec: it contains interface names.
469        self._exposureGlobalNames = set()
470        self.exposureSet = set()
471        self._location = location
472        self._globalScope = None
473
474    def finish(self, scope):
475        assert scope.parentScope is None
476        self._globalScope = scope
477
478        # Verify that our [Exposed] value, if any, makes sense.
479        for globalName in self._exposureGlobalNames:
480            if globalName not in scope.globalNames:
481                raise WebIDLError(
482                    "Unknown [Exposed] value %s" % globalName, [self._location]
483                )
484
485        # Verify that we are exposed _somwhere_ if we have some place to be
486        # exposed.  We don't want to assert that we're definitely exposed
487        # because a lot of our parser tests have small-enough IDL snippets that
488        # they don't include any globals, and we don't really want to go through
489        # and add global interfaces and [Exposed] annotations to all those
490        # tests.
491        if len(scope.globalNames) != 0:
492            if len(self._exposureGlobalNames) == 0:
493                raise WebIDLError(
494                    (
495                        "'%s' is not exposed anywhere even though we have "
496                        "globals to be exposed to"
497                    )
498                    % self,
499                    [self.location],
500                )
501
502        globalNameSetToExposureSet(scope, self._exposureGlobalNames, self.exposureSet)
503
504    def isExposedInWindow(self):
505        return "Window" in self.exposureSet
506
507    def isExposedInAnyWorker(self):
508        return len(self.getWorkerExposureSet()) > 0
509
510    def isExposedInWorkerDebugger(self):
511        return len(self.getWorkerDebuggerExposureSet()) > 0
512
513    def isExposedInAnyWorklet(self):
514        return len(self.getWorkletExposureSet()) > 0
515
516    def isExposedInSomeButNotAllWorkers(self):
517        """
518        Returns true if the Exposed extended attribute for this interface
519        exposes it in some worker globals but not others.  The return value does
520        not depend on whether the interface is exposed in Window or System
521        globals.
522        """
523        if not self.isExposedInAnyWorker():
524            return False
525        workerScopes = self.parentScope.globalNameMapping["Worker"]
526        return len(workerScopes.difference(self.exposureSet)) > 0
527
528    def getWorkerExposureSet(self):
529        workerScopes = self._globalScope.globalNameMapping["Worker"]
530        return workerScopes.intersection(self.exposureSet)
531
532    def getWorkletExposureSet(self):
533        workletScopes = self._globalScope.globalNameMapping["Worklet"]
534        return workletScopes.intersection(self.exposureSet)
535
536    def getWorkerDebuggerExposureSet(self):
537        workerDebuggerScopes = self._globalScope.globalNameMapping["WorkerDebugger"]
538        return workerDebuggerScopes.intersection(self.exposureSet)
539
540
541class IDLExternalInterface(IDLObjectWithIdentifier):
542    def __init__(self, location, parentScope, identifier):
543        assert isinstance(identifier, IDLUnresolvedIdentifier)
544        assert isinstance(parentScope, IDLScope)
545        self.parent = None
546        IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
547        IDLObjectWithIdentifier.resolve(self, parentScope)
548
549    def finish(self, scope):
550        pass
551
552    def validate(self):
553        pass
554
555    def isIteratorInterface(self):
556        return False
557
558    def isExternal(self):
559        return True
560
561    def isInterface(self):
562        return True
563
564    def addExtendedAttributes(self, attrs):
565        if len(attrs) != 0:
566            raise WebIDLError(
567                "There are no extended attributes that are "
568                "allowed on external interfaces",
569                [attrs[0].location, self.location],
570            )
571
572    def resolve(self, parentScope):
573        pass
574
575    def getJSImplementation(self):
576        return None
577
578    def isJSImplemented(self):
579        return False
580
581    def hasProbablyShortLivingWrapper(self):
582        return False
583
584    def _getDependentObjects(self):
585        return set()
586
587
588class IDLPartialDictionary(IDLObject):
589    def __init__(self, location, name, members, nonPartialDictionary):
590        assert isinstance(name, IDLUnresolvedIdentifier)
591
592        IDLObject.__init__(self, location)
593        self.identifier = name
594        self.members = members
595        self._nonPartialDictionary = nonPartialDictionary
596        self._finished = False
597        nonPartialDictionary.addPartialDictionary(self)
598
599    def addExtendedAttributes(self, attrs):
600        pass
601
602    def finish(self, scope):
603        if self._finished:
604            return
605        self._finished = True
606
607        # Need to make sure our non-partial dictionary gets
608        # finished so it can report cases when we only have partial
609        # dictionaries.
610        self._nonPartialDictionary.finish(scope)
611
612    def validate(self):
613        pass
614
615
616class IDLPartialInterfaceOrNamespace(IDLObject):
617    def __init__(self, location, name, members, nonPartialInterfaceOrNamespace):
618        assert isinstance(name, IDLUnresolvedIdentifier)
619
620        IDLObject.__init__(self, location)
621        self.identifier = name
622        self.members = members
623        # propagatedExtendedAttrs are the ones that should get
624        # propagated to our non-partial interface.
625        self.propagatedExtendedAttrs = []
626        self._haveSecureContextExtendedAttribute = False
627        self._nonPartialInterfaceOrNamespace = nonPartialInterfaceOrNamespace
628        self._finished = False
629        nonPartialInterfaceOrNamespace.addPartial(self)
630
631    def addExtendedAttributes(self, attrs):
632        for attr in attrs:
633            identifier = attr.identifier()
634
635            if identifier == "LegacyFactoryFunction":
636                self.propagatedExtendedAttrs.append(attr)
637            elif identifier == "SecureContext":
638                self._haveSecureContextExtendedAttribute = True
639                # This gets propagated to all our members.
640                for member in self.members:
641                    if member.getExtendedAttribute("SecureContext"):
642                        raise WebIDLError(
643                            "[SecureContext] specified on both a "
644                            "partial interface member and on the "
645                            "partial interface itself",
646                            [member.location, attr.location],
647                        )
648                    member.addExtendedAttributes([attr])
649            elif identifier == "Exposed":
650                # This just gets propagated to all our members.
651                for member in self.members:
652                    if len(member._exposureGlobalNames) != 0:
653                        raise WebIDLError(
654                            "[Exposed] specified on both a "
655                            "partial interface member and on the "
656                            "partial interface itself",
657                            [member.location, attr.location],
658                        )
659                    member.addExtendedAttributes([attr])
660            else:
661                raise WebIDLError(
662                    "Unknown extended attribute %s on partial "
663                    "interface" % identifier,
664                    [attr.location],
665                )
666
667    def finish(self, scope):
668        if self._finished:
669            return
670        self._finished = True
671        if (
672            not self._haveSecureContextExtendedAttribute
673            and self._nonPartialInterfaceOrNamespace.getExtendedAttribute(
674                "SecureContext"
675            )
676        ):
677            # This gets propagated to all our members.
678            for member in self.members:
679                if member.getExtendedAttribute("SecureContext"):
680                    raise WebIDLError(
681                        "[SecureContext] specified on both a "
682                        "partial interface member and on the "
683                        "non-partial interface",
684                        [
685                            member.location,
686                            self._nonPartialInterfaceOrNamespace.location,
687                        ],
688                    )
689                member.addExtendedAttributes(
690                    [
691                        IDLExtendedAttribute(
692                            self._nonPartialInterfaceOrNamespace.location,
693                            ("SecureContext",),
694                        )
695                    ]
696                )
697        # Need to make sure our non-partial interface or namespace gets
698        # finished so it can report cases when we only have partial
699        # interfaces/namespaces.
700        self._nonPartialInterfaceOrNamespace.finish(scope)
701
702    def validate(self):
703        pass
704
705
706def convertExposedAttrToGlobalNameSet(exposedAttr, targetSet):
707    assert len(targetSet) == 0
708    if exposedAttr.hasValue():
709        targetSet.add(exposedAttr.value())
710    else:
711        assert exposedAttr.hasArgs()
712        targetSet.update(exposedAttr.args())
713
714
715def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
716    for name in nameSet:
717        exposureSet.update(globalScope.globalNameMapping[name])
718
719
720class IDLInterfaceOrInterfaceMixinOrNamespace(IDLObjectWithScope, IDLExposureMixins):
721    def __init__(self, location, parentScope, name):
722        assert isinstance(parentScope, IDLScope)
723        assert isinstance(name, IDLUnresolvedIdentifier)
724
725        self._finished = False
726        self.members = []
727        self._partials = []
728        self._extendedAttrDict = {}
729        self._isKnownNonPartial = False
730
731        IDLObjectWithScope.__init__(self, location, parentScope, name)
732        IDLExposureMixins.__init__(self, location)
733
734    def finish(self, scope):
735        if not self._isKnownNonPartial:
736            raise WebIDLError(
737                "%s does not have a non-partial declaration" % str(self),
738                [self.location],
739            )
740
741        IDLExposureMixins.finish(self, scope)
742
743        # Now go ahead and merge in our partials.
744        for partial in self._partials:
745            partial.finish(scope)
746            self.addExtendedAttributes(partial.propagatedExtendedAttrs)
747            self.members.extend(partial.members)
748
749    def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
750        assert isinstance(scope, IDLScope)
751        assert isinstance(originalObject, IDLInterfaceMember)
752        assert isinstance(newObject, IDLInterfaceMember)
753
754        retval = IDLScope.resolveIdentifierConflict(
755            self, scope, identifier, originalObject, newObject
756        )
757
758        # Might be a ctor, which isn't in self.members
759        if newObject in self.members:
760            self.members.remove(newObject)
761        return retval
762
763    def typeName(self):
764        if self.isInterface():
765            return "interface"
766        if self.isNamespace():
767            return "namespace"
768        assert self.isInterfaceMixin()
769        return "interface mixin"
770
771    def getExtendedAttribute(self, name):
772        return self._extendedAttrDict.get(name, None)
773
774    def setNonPartial(self, location, members):
775        if self._isKnownNonPartial:
776            raise WebIDLError(
777                "Two non-partial definitions for the " "same %s" % self.typeName(),
778                [location, self.location],
779            )
780        self._isKnownNonPartial = True
781        # Now make it look like we were parsed at this new location, since
782        # that's the place where the interface is "really" defined
783        self.location = location
784        # Put the new members at the beginning
785        self.members = members + self.members
786
787    def addPartial(self, partial):
788        assert self.identifier.name == partial.identifier.name
789        self._partials.append(partial)
790
791    def getPartials(self):
792        # Don't let people mutate our guts.
793        return list(self._partials)
794
795    def finishMembers(self, scope):
796        # Assuming we've merged in our partials, set the _exposureGlobalNames on
797        # any members that don't have it set yet.  Note that any partial
798        # interfaces that had [Exposed] set have already set up
799        # _exposureGlobalNames on all the members coming from them, so this is
800        # just implementing the "members default to interface or interface mixin
801        # that defined them" and "partial interfaces or interface mixins default
802        # to interface or interface mixin they're a partial for" rules from the
803        # spec.
804        for m in self.members:
805            # If m, or the partial m came from, had [Exposed]
806            # specified, it already has a nonempty exposure global names set.
807            if len(m._exposureGlobalNames) == 0:
808                m._exposureGlobalNames.update(self._exposureGlobalNames)
809            if m.isAttr() and m.stringifier:
810                m.expand(self.members)
811
812        # resolve() will modify self.members, so we need to iterate
813        # over a copy of the member list here.
814        for member in list(self.members):
815            member.resolve(self)
816
817        for member in self.members:
818            member.finish(scope)
819
820        # Now that we've finished our members, which has updated their exposure
821        # sets, make sure they aren't exposed in places where we are not.
822        for member in self.members:
823            if not member.exposureSet.issubset(self.exposureSet):
824                raise WebIDLError(
825                    "Interface or interface mixin member has "
826                    "larger exposure set than its container",
827                    [member.location, self.location],
828                )
829
830    def isExternal(self):
831        return False
832
833
834class IDLInterfaceMixin(IDLInterfaceOrInterfaceMixinOrNamespace):
835    def __init__(self, location, parentScope, name, members, isKnownNonPartial):
836        self.actualExposureGlobalNames = set()
837
838        assert isKnownNonPartial or len(members) == 0
839        IDLInterfaceOrInterfaceMixinOrNamespace.__init__(
840            self, location, parentScope, name
841        )
842
843        if isKnownNonPartial:
844            self.setNonPartial(location, members)
845
846    def __str__(self):
847        return "Interface mixin '%s'" % self.identifier.name
848
849    def isInterfaceMixin(self):
850        return True
851
852    def finish(self, scope):
853        if self._finished:
854            return
855        self._finished = True
856
857        # Expose to the globals of interfaces that includes this mixin if this
858        # mixin has no explicit [Exposed] so that its members can be exposed
859        # based on the base interface exposure set.
860        #
861        # Make sure this is done before IDLExposureMixins.finish call, since
862        # that converts our set of exposure global names to an actual exposure
863        # set.
864        hasImplicitExposure = len(self._exposureGlobalNames) == 0
865        if hasImplicitExposure:
866            self._exposureGlobalNames.update(self.actualExposureGlobalNames)
867
868        IDLInterfaceOrInterfaceMixinOrNamespace.finish(self, scope)
869
870        self.finishMembers(scope)
871
872    def validate(self):
873        for member in self.members:
874
875            if member.isAttr():
876                if member.inherit:
877                    raise WebIDLError(
878                        "Interface mixin member cannot include "
879                        "an inherited attribute",
880                        [member.location, self.location],
881                    )
882                if member.isStatic():
883                    raise WebIDLError(
884                        "Interface mixin member cannot include " "a static member",
885                        [member.location, self.location],
886                    )
887
888            if member.isMethod():
889                if member.isStatic():
890                    raise WebIDLError(
891                        "Interface mixin member cannot include " "a static operation",
892                        [member.location, self.location],
893                    )
894                if (
895                    member.isGetter()
896                    or member.isSetter()
897                    or member.isDeleter()
898                    or member.isLegacycaller()
899                ):
900                    raise WebIDLError(
901                        "Interface mixin member cannot include a " "special operation",
902                        [member.location, self.location],
903                    )
904
905    def addExtendedAttributes(self, attrs):
906        for attr in attrs:
907            identifier = attr.identifier()
908
909            if identifier == "SecureContext":
910                if not attr.noArguments():
911                    raise WebIDLError(
912                        "[%s] must take no arguments" % identifier, [attr.location]
913                    )
914                # This gets propagated to all our members.
915                for member in self.members:
916                    if member.getExtendedAttribute("SecureContext"):
917                        raise WebIDLError(
918                            "[SecureContext] specified on both "
919                            "an interface mixin member and on"
920                            "the interface mixin itself",
921                            [member.location, attr.location],
922                        )
923                    member.addExtendedAttributes([attr])
924            elif identifier == "Exposed":
925                convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
926            else:
927                raise WebIDLError(
928                    "Unknown extended attribute %s on interface" % identifier,
929                    [attr.location],
930                )
931
932            attrlist = attr.listValue()
933            self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
934
935    def _getDependentObjects(self):
936        return set(self.members)
937
938
939class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
940    def __init__(self, location, parentScope, name, parent, members, isKnownNonPartial):
941        assert isKnownNonPartial or not parent
942        assert isKnownNonPartial or len(members) == 0
943
944        self.parent = None
945        self._callback = False
946        self.maplikeOrSetlikeOrIterable = None
947        # namedConstructors needs deterministic ordering because bindings code
948        # outputs the constructs in the order that namedConstructors enumerates
949        # them.
950        self.legacyFactoryFunctions = list()
951        self.legacyWindowAliases = []
952        self.includedMixins = set()
953        # self.interfacesBasedOnSelf is the set of interfaces that inherit from
954        # self, including self itself.
955        # Used for distinguishability checking.
956        self.interfacesBasedOnSelf = set([self])
957        self._hasChildInterfaces = False
958        self._isOnGlobalProtoChain = False
959        # Tracking of the number of reserved slots we need for our
960        # members and those of ancestor interfaces.
961        self.totalMembersInSlots = 0
962        # Tracking of the number of own own members we have in slots
963        self._ownMembersInSlots = 0
964        # If this is an iterator interface, we need to know what iterable
965        # interface we're iterating for in order to get its nativeType.
966        self.iterableInterface = None
967        # True if we have cross-origin members.
968        self.hasCrossOriginMembers = False
969        # True if some descendant (including ourselves) has cross-origin members
970        self.hasDescendantWithCrossOriginMembers = False
971
972        IDLInterfaceOrInterfaceMixinOrNamespace.__init__(
973            self, location, parentScope, name
974        )
975
976        if isKnownNonPartial:
977            self.setNonPartial(location, parent, members)
978
979    def ctor(self):
980        identifier = IDLUnresolvedIdentifier(
981            self.location, "constructor", allowForbidden=True
982        )
983        try:
984            return self._lookupIdentifier(identifier)
985        except:
986            return None
987
988    def isIterable(self):
989        return (
990            self.maplikeOrSetlikeOrIterable
991            and self.maplikeOrSetlikeOrIterable.isIterable()
992        )
993
994    def isIteratorInterface(self):
995        return self.iterableInterface is not None
996
997    def getClassName(self):
998        return self.identifier.name
999
1000    def finish(self, scope):
1001        if self._finished:
1002            return
1003
1004        self._finished = True
1005
1006        IDLInterfaceOrInterfaceMixinOrNamespace.finish(self, scope)
1007
1008        if len(self.legacyWindowAliases) > 0:
1009            if not self.hasInterfaceObject():
1010                raise WebIDLError(
1011                    "Interface %s unexpectedly has [LegacyWindowAlias] "
1012                    "and [LegacyNoInterfaceObject] together" % self.identifier.name,
1013                    [self.location],
1014                )
1015            if not self.isExposedInWindow():
1016                raise WebIDLError(
1017                    "Interface %s has [LegacyWindowAlias] "
1018                    "but not exposed in Window" % self.identifier.name,
1019                    [self.location],
1020                )
1021
1022        # Generate maplike/setlike interface members. Since generated members
1023        # need to be treated like regular interface members, do this before
1024        # things like exposure setting.
1025        for member in self.members:
1026            if member.isMaplikeOrSetlikeOrIterable():
1027                if self.isJSImplemented():
1028                    raise WebIDLError(
1029                        "%s declaration used on "
1030                        "interface that is implemented in JS"
1031                        % (member.maplikeOrSetlikeOrIterableType),
1032                        [member.location],
1033                    )
1034                # Check that we only have one interface declaration (currently
1035                # there can only be one maplike/setlike declaration per
1036                # interface)
1037                if self.maplikeOrSetlikeOrIterable:
1038                    raise WebIDLError(
1039                        "%s declaration used on "
1040                        "interface that already has %s "
1041                        "declaration"
1042                        % (
1043                            member.maplikeOrSetlikeOrIterableType,
1044                            self.maplikeOrSetlikeOrIterable.maplikeOrSetlikeOrIterableType,
1045                        ),
1046                        [self.maplikeOrSetlikeOrIterable.location, member.location],
1047                    )
1048                self.maplikeOrSetlikeOrIterable = member
1049                # If we've got a maplike or setlike declaration, we'll be building all of
1050                # our required methods in Codegen. Generate members now.
1051                self.maplikeOrSetlikeOrIterable.expand(self.members)
1052
1053        assert not self.parent or isinstance(self.parent, IDLIdentifierPlaceholder)
1054        parent = self.parent.finish(scope) if self.parent else None
1055        if parent and isinstance(parent, IDLExternalInterface):
1056            raise WebIDLError(
1057                "%s inherits from %s which does not have "
1058                "a definition" % (self.identifier.name, self.parent.identifier.name),
1059                [self.location],
1060            )
1061        if parent and not isinstance(parent, IDLInterface):
1062            raise WebIDLError(
1063                "%s inherits from %s which is not an interface "
1064                % (self.identifier.name, self.parent.identifier.name),
1065                [self.location, parent.location],
1066            )
1067
1068        self.parent = parent
1069
1070        assert iter(self.members)
1071
1072        if self.isNamespace():
1073            assert not self.parent
1074            for m in self.members:
1075                if m.isAttr() or m.isMethod():
1076                    if m.isStatic():
1077                        raise WebIDLError(
1078                            "Don't mark things explicitly static " "in namespaces",
1079                            [self.location, m.location],
1080                        )
1081                    # Just mark all our methods/attributes as static.  The other
1082                    # option is to duplicate the relevant InterfaceMembers
1083                    # production bits but modified to produce static stuff to
1084                    # start with, but that sounds annoying.
1085                    m.forceStatic()
1086
1087        if self.parent:
1088            self.parent.finish(scope)
1089            self.parent._hasChildInterfaces = True
1090
1091            self.totalMembersInSlots = self.parent.totalMembersInSlots
1092
1093            # Interfaces with [Global] must not have anything inherit from them
1094            if self.parent.getExtendedAttribute("Global"):
1095                # Note: This is not a self.parent.isOnGlobalProtoChain() check
1096                # because ancestors of a [Global] interface can have other
1097                # descendants.
1098                raise WebIDLError(
1099                    "[Global] interface has another interface " "inheriting from it",
1100                    [self.location, self.parent.location],
1101                )
1102
1103            # Make sure that we're not exposed in places where our parent is not
1104            if not self.exposureSet.issubset(self.parent.exposureSet):
1105                raise WebIDLError(
1106                    "Interface %s is exposed in globals where its "
1107                    "parent interface %s is not exposed."
1108                    % (self.identifier.name, self.parent.identifier.name),
1109                    [self.location, self.parent.location],
1110                )
1111
1112            # Callbacks must not inherit from non-callbacks.
1113            # XXXbz Can non-callbacks inherit from callbacks?  Spec issue pending.
1114            if self.isCallback():
1115                if not self.parent.isCallback():
1116                    raise WebIDLError(
1117                        "Callback interface %s inheriting from "
1118                        "non-callback interface %s"
1119                        % (self.identifier.name, self.parent.identifier.name),
1120                        [self.location, self.parent.location],
1121                    )
1122            elif self.parent.isCallback():
1123                raise WebIDLError(
1124                    "Non-callback interface %s inheriting from "
1125                    "callback interface %s"
1126                    % (self.identifier.name, self.parent.identifier.name),
1127                    [self.location, self.parent.location],
1128                )
1129
1130            # Interfaces which have interface objects can't inherit
1131            # from [LegacyNoInterfaceObject] interfaces.
1132            if self.parent.getExtendedAttribute(
1133                "LegacyNoInterfaceObject"
1134            ) and not self.getExtendedAttribute("LegacyNoInterfaceObject"):
1135                raise WebIDLError(
1136                    "Interface %s does not have "
1137                    "[LegacyNoInterfaceObject] but inherits from "
1138                    "interface %s which does"
1139                    % (self.identifier.name, self.parent.identifier.name),
1140                    [self.location, self.parent.location],
1141                )
1142
1143            # Interfaces that are not [SecureContext] can't inherit
1144            # from [SecureContext] interfaces.
1145            if self.parent.getExtendedAttribute(
1146                "SecureContext"
1147            ) and not self.getExtendedAttribute("SecureContext"):
1148                raise WebIDLError(
1149                    "Interface %s does not have "
1150                    "[SecureContext] but inherits from "
1151                    "interface %s which does"
1152                    % (self.identifier.name, self.parent.identifier.name),
1153                    [self.location, self.parent.location],
1154                )
1155
1156        for mixin in self.includedMixins:
1157            mixin.finish(scope)
1158
1159        cycleInGraph = self.findInterfaceLoopPoint(self)
1160        if cycleInGraph:
1161            raise WebIDLError(
1162                "Interface %s has itself as ancestor" % self.identifier.name,
1163                [self.location, cycleInGraph.location],
1164            )
1165
1166        self.finishMembers(scope)
1167
1168        ctor = self.ctor()
1169        if ctor is not None:
1170            if not self.hasInterfaceObject():
1171                raise WebIDLError(
1172                    "Can't have both a constructor and [LegacyNoInterfaceObject]",
1173                    [self.location, ctor.location],
1174                )
1175
1176            if self.globalNames:
1177                raise WebIDLError(
1178                    "Can't have both a constructor and [Global]",
1179                    [self.location, ctor.location],
1180                )
1181
1182            assert ctor._exposureGlobalNames == self._exposureGlobalNames
1183            ctor._exposureGlobalNames.update(self._exposureGlobalNames)
1184            # Remove the constructor operation from our member list so
1185            # it doesn't get in the way later.
1186            self.members.remove(ctor)
1187
1188        for ctor in self.legacyFactoryFunctions:
1189            if self.globalNames:
1190                raise WebIDLError(
1191                    "Can't have both a legacy factory function and [Global]",
1192                    [self.location, ctor.location],
1193                )
1194            assert len(ctor._exposureGlobalNames) == 0
1195            ctor._exposureGlobalNames.update(self._exposureGlobalNames)
1196            ctor.finish(scope)
1197
1198        # Make a copy of our member list, so things that implement us
1199        # can get those without all the stuff we implement ourselves
1200        # admixed.
1201        self.originalMembers = list(self.members)
1202
1203        for mixin in sorted(self.includedMixins, key=lambda x: x.identifier.name):
1204            for mixinMember in mixin.members:
1205                for member in self.members:
1206                    if mixinMember.identifier.name == member.identifier.name:
1207                        raise WebIDLError(
1208                            "Multiple definitions of %s on %s coming from 'includes' statements"
1209                            % (member.identifier.name, self),
1210                            [mixinMember.location, member.location],
1211                        )
1212            self.members.extend(mixin.members)
1213
1214        for ancestor in self.getInheritedInterfaces():
1215            ancestor.interfacesBasedOnSelf.add(self)
1216            if (
1217                ancestor.maplikeOrSetlikeOrIterable is not None
1218                and self.maplikeOrSetlikeOrIterable is not None
1219            ):
1220                raise WebIDLError(
1221                    "Cannot have maplike/setlike on %s that "
1222                    "inherits %s, which is already "
1223                    "maplike/setlike"
1224                    % (self.identifier.name, ancestor.identifier.name),
1225                    [
1226                        self.maplikeOrSetlikeOrIterable.location,
1227                        ancestor.maplikeOrSetlikeOrIterable.location,
1228                    ],
1229                )
1230
1231        # Deal with interfaces marked [LegacyUnforgeable], now that we have our full
1232        # member list, except unforgeables pulled in from parents.  We want to
1233        # do this before we set "originatingInterface" on our unforgeable
1234        # members.
1235        if self.getExtendedAttribute("LegacyUnforgeable"):
1236            # Check that the interface already has all the things the
1237            # spec would otherwise require us to synthesize and is
1238            # missing the ones we plan to synthesize.
1239            if not any(m.isMethod() and m.isStringifier() for m in self.members):
1240                raise WebIDLError(
1241                    "LegacyUnforgeable interface %s does not have a "
1242                    "stringifier" % self.identifier.name,
1243                    [self.location],
1244                )
1245
1246            for m in self.members:
1247                if m.identifier.name == "toJSON":
1248                    raise WebIDLError(
1249                        "LegacyUnforgeable interface %s has a "
1250                        "toJSON so we won't be able to add "
1251                        "one ourselves" % self.identifier.name,
1252                        [self.location, m.location],
1253                    )
1254
1255                if m.identifier.name == "valueOf" and not m.isStatic():
1256                    raise WebIDLError(
1257                        "LegacyUnforgeable interface %s has a valueOf "
1258                        "member so we won't be able to add one "
1259                        "ourselves" % self.identifier.name,
1260                        [self.location, m.location],
1261                    )
1262
1263        for member in self.members:
1264            if (
1265                (member.isAttr() or member.isMethod())
1266                and member.isLegacyUnforgeable()
1267                and not hasattr(member, "originatingInterface")
1268            ):
1269                member.originatingInterface = self
1270
1271        for member in self.members:
1272            if (
1273                member.isMethod() and member.getExtendedAttribute("CrossOriginCallable")
1274            ) or (
1275                member.isAttr()
1276                and (
1277                    member.getExtendedAttribute("CrossOriginReadable")
1278                    or member.getExtendedAttribute("CrossOriginWritable")
1279                )
1280            ):
1281                self.hasCrossOriginMembers = True
1282                break
1283
1284        if self.hasCrossOriginMembers:
1285            parent = self
1286            while parent:
1287                parent.hasDescendantWithCrossOriginMembers = True
1288                parent = parent.parent
1289
1290        # Compute slot indices for our members before we pull in unforgeable
1291        # members from our parent. Also, maplike/setlike declarations get a
1292        # slot to hold their backing object.
1293        for member in self.members:
1294            if (
1295                member.isAttr()
1296                and (
1297                    member.getExtendedAttribute("StoreInSlot")
1298                    or member.getExtendedAttribute("Cached")
1299                )
1300            ) or member.isMaplikeOrSetlike():
1301                if self.isJSImplemented() and not member.isMaplikeOrSetlike():
1302                    raise WebIDLError(
1303                        "Interface %s is JS-implemented and we "
1304                        "don't support [Cached] or [StoreInSlot] "
1305                        "on JS-implemented interfaces" % self.identifier.name,
1306                        [self.location, member.location],
1307                    )
1308                if member.slotIndices is None:
1309                    member.slotIndices = dict()
1310                member.slotIndices[self.identifier.name] = self.totalMembersInSlots
1311                self.totalMembersInSlots += 1
1312                if member.getExtendedAttribute("StoreInSlot"):
1313                    self._ownMembersInSlots += 1
1314
1315        if self.parent:
1316            # Make sure we don't shadow any of the [LegacyUnforgeable] attributes on our
1317            # ancestor interfaces.  We don't have to worry about mixins here, because
1318            # those have already been imported into the relevant .members lists.  And
1319            # we don't have to worry about anything other than our parent, because it
1320            # has already imported its ancestors' unforgeable attributes into its
1321            # member list.
1322            for unforgeableMember in (
1323                member
1324                for member in self.parent.members
1325                if (member.isAttr() or member.isMethod())
1326                and member.isLegacyUnforgeable()
1327            ):
1328                shadows = [
1329                    m
1330                    for m in self.members
1331                    if (m.isAttr() or m.isMethod())
1332                    and not m.isStatic()
1333                    and m.identifier.name == unforgeableMember.identifier.name
1334                ]
1335                if len(shadows) != 0:
1336                    locs = [unforgeableMember.location] + [s.location for s in shadows]
1337                    raise WebIDLError(
1338                        "Interface %s shadows [LegacyUnforgeable] "
1339                        "members of %s"
1340                        % (self.identifier.name, ancestor.identifier.name),
1341                        locs,
1342                    )
1343                # And now just stick it in our members, since we won't be
1344                # inheriting this down the proto chain.  If we really cared we
1345                # could try to do something where we set up the unforgeable
1346                # attributes/methods of ancestor interfaces, with their
1347                # corresponding getters, on our interface, but that gets pretty
1348                # complicated and seems unnecessary.
1349                self.members.append(unforgeableMember)
1350
1351        # At this point, we have all of our members. If the current interface
1352        # uses maplike/setlike, check for collisions anywhere in the current
1353        # interface or higher in the inheritance chain.
1354        if self.maplikeOrSetlikeOrIterable:
1355            testInterface = self
1356            isAncestor = False
1357            while testInterface:
1358                self.maplikeOrSetlikeOrIterable.checkCollisions(
1359                    testInterface.members, isAncestor
1360                )
1361                isAncestor = True
1362                testInterface = testInterface.parent
1363
1364        # Ensure that there's at most one of each {named,indexed}
1365        # {getter,setter,deleter}, at most one stringifier,
1366        # and at most one legacycaller.  Note that this last is not
1367        # quite per spec, but in practice no one overloads
1368        # legacycallers.  Also note that in practice we disallow
1369        # indexed deleters, but it simplifies some other code to
1370        # treat deleter analogously to getter/setter by
1371        # prefixing it with "named".
1372        specialMembersSeen = {}
1373        for member in self.members:
1374            if not member.isMethod():
1375                continue
1376
1377            if member.isGetter():
1378                memberType = "getters"
1379            elif member.isSetter():
1380                memberType = "setters"
1381            elif member.isDeleter():
1382                memberType = "deleters"
1383            elif member.isStringifier():
1384                memberType = "stringifiers"
1385            elif member.isLegacycaller():
1386                memberType = "legacycallers"
1387            else:
1388                continue
1389
1390            if memberType != "stringifiers" and memberType != "legacycallers":
1391                if member.isNamed():
1392                    memberType = "named " + memberType
1393                else:
1394                    assert member.isIndexed()
1395                    memberType = "indexed " + memberType
1396
1397            if memberType in specialMembersSeen:
1398                raise WebIDLError(
1399                    "Multiple " + memberType + " on %s" % (self),
1400                    [
1401                        self.location,
1402                        specialMembersSeen[memberType].location,
1403                        member.location,
1404                    ],
1405                )
1406
1407            specialMembersSeen[memberType] = member
1408
1409        if self.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
1410            # Check that we have a named getter.
1411            if "named getters" not in specialMembersSeen:
1412                raise WebIDLError(
1413                    "Interface with [LegacyUnenumerableNamedProperties] does "
1414                    "not have a named getter",
1415                    [self.location],
1416                )
1417            ancestor = self.parent
1418            while ancestor:
1419                if ancestor.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
1420                    raise WebIDLError(
1421                        "Interface with [LegacyUnenumerableNamedProperties] "
1422                        "inherits from another interface with "
1423                        "[LegacyUnenumerableNamedProperties]",
1424                        [self.location, ancestor.location],
1425                    )
1426                ancestor = ancestor.parent
1427
1428        if self._isOnGlobalProtoChain:
1429            # Make sure we have no named setters or deleters
1430            for memberType in ["setter", "deleter"]:
1431                memberId = "named " + memberType + "s"
1432                if memberId in specialMembersSeen:
1433                    raise WebIDLError(
1434                        "Interface with [Global] has a named %s" % memberType,
1435                        [self.location, specialMembersSeen[memberId].location],
1436                    )
1437            # Make sure we're not [LegacyOverrideBuiltIns]
1438            if self.getExtendedAttribute("LegacyOverrideBuiltIns"):
1439                raise WebIDLError(
1440                    "Interface with [Global] also has " "[LegacyOverrideBuiltIns]",
1441                    [self.location],
1442                )
1443            # Mark all of our ancestors as being on the global's proto chain too
1444            parent = self.parent
1445            while parent:
1446                # Must not inherit from an interface with [LegacyOverrideBuiltIns]
1447                if parent.getExtendedAttribute("LegacyOverrideBuiltIns"):
1448                    raise WebIDLError(
1449                        "Interface with [Global] inherits from "
1450                        "interface with [LegacyOverrideBuiltIns]",
1451                        [self.location, parent.location],
1452                    )
1453                parent._isOnGlobalProtoChain = True
1454                parent = parent.parent
1455
1456    def validate(self):
1457        def checkDuplicateNames(member, name, attributeName):
1458            for m in self.members:
1459                if m.identifier.name == name:
1460                    raise WebIDLError(
1461                        "[%s=%s] has same name as interface member"
1462                        % (attributeName, name),
1463                        [member.location, m.location],
1464                    )
1465                if m.isMethod() and m != member and name in m.aliases:
1466                    raise WebIDLError(
1467                        "conflicting [%s=%s] definitions" % (attributeName, name),
1468                        [member.location, m.location],
1469                    )
1470                if m.isAttr() and m != member and name in m.bindingAliases:
1471                    raise WebIDLError(
1472                        "conflicting [%s=%s] definitions" % (attributeName, name),
1473                        [member.location, m.location],
1474                    )
1475
1476        # We also don't support inheriting from unforgeable interfaces.
1477        if self.getExtendedAttribute("LegacyUnforgeable") and self.hasChildInterfaces():
1478            locations = [self.location] + list(
1479                i.location for i in self.interfacesBasedOnSelf if i.parent == self
1480            )
1481            raise WebIDLError(
1482                "%s is an unforgeable ancestor interface" % self.identifier.name,
1483                locations,
1484            )
1485
1486        ctor = self.ctor()
1487        if ctor is not None:
1488            ctor.validate()
1489        for namedCtor in self.legacyFactoryFunctions:
1490            namedCtor.validate()
1491
1492        indexedGetter = None
1493        hasLengthAttribute = False
1494        for member in self.members:
1495            member.validate()
1496
1497            if self.isCallback() and member.getExtendedAttribute("Replaceable"):
1498                raise WebIDLError(
1499                    "[Replaceable] used on an attribute on "
1500                    "interface %s which is a callback interface" % self.identifier.name,
1501                    [self.location, member.location],
1502                )
1503
1504            # Check that PutForwards refers to another attribute and that no
1505            # cycles exist in forwarded assignments.  Also check for a
1506            # integer-typed "length" attribute.
1507            if member.isAttr():
1508                if member.identifier.name == "length" and member.type.isInteger():
1509                    hasLengthAttribute = True
1510
1511                iface = self
1512                attr = member
1513                putForwards = attr.getExtendedAttribute("PutForwards")
1514                if putForwards and self.isCallback():
1515                    raise WebIDLError(
1516                        "[PutForwards] used on an attribute "
1517                        "on interface %s which is a callback "
1518                        "interface" % self.identifier.name,
1519                        [self.location, member.location],
1520                    )
1521
1522                while putForwards is not None:
1523                    forwardIface = attr.type.unroll().inner
1524                    fowardAttr = None
1525
1526                    for forwardedMember in forwardIface.members:
1527                        if (
1528                            not forwardedMember.isAttr()
1529                            or forwardedMember.identifier.name != putForwards[0]
1530                        ):
1531                            continue
1532                        if forwardedMember == member:
1533                            raise WebIDLError(
1534                                "Cycle detected in forwarded "
1535                                "assignments for attribute %s on "
1536                                "%s" % (member.identifier.name, self),
1537                                [member.location],
1538                            )
1539                        fowardAttr = forwardedMember
1540                        break
1541
1542                    if fowardAttr is None:
1543                        raise WebIDLError(
1544                            "Attribute %s on %s forwards to "
1545                            "missing attribute %s"
1546                            % (attr.identifier.name, iface, putForwards),
1547                            [attr.location],
1548                        )
1549
1550                    iface = forwardIface
1551                    attr = fowardAttr
1552                    putForwards = attr.getExtendedAttribute("PutForwards")
1553
1554            # Check that the name of an [Alias] doesn't conflict with an
1555            # interface member and whether we support indexed properties.
1556            if member.isMethod():
1557                if member.isGetter() and member.isIndexed():
1558                    indexedGetter = member
1559
1560                for alias in member.aliases:
1561                    if self.isOnGlobalProtoChain():
1562                        raise WebIDLError(
1563                            "[Alias] must not be used on a "
1564                            "[Global] interface operation",
1565                            [member.location],
1566                        )
1567                    if (
1568                        member.getExtendedAttribute("Exposed")
1569                        or member.getExtendedAttribute("ChromeOnly")
1570                        or member.getExtendedAttribute("Pref")
1571                        or member.getExtendedAttribute("Func")
1572                        or member.getExtendedAttribute("SecureContext")
1573                    ):
1574                        raise WebIDLError(
1575                            "[Alias] must not be used on a "
1576                            "conditionally exposed operation",
1577                            [member.location],
1578                        )
1579                    if member.isStatic():
1580                        raise WebIDLError(
1581                            "[Alias] must not be used on a " "static operation",
1582                            [member.location],
1583                        )
1584                    if member.isIdentifierLess():
1585                        raise WebIDLError(
1586                            "[Alias] must not be used on an "
1587                            "identifierless operation",
1588                            [member.location],
1589                        )
1590                    if member.isLegacyUnforgeable():
1591                        raise WebIDLError(
1592                            "[Alias] must not be used on an "
1593                            "[LegacyUnforgeable] operation",
1594                            [member.location],
1595                        )
1596
1597                    checkDuplicateNames(member, alias, "Alias")
1598
1599            # Check that the name of a [BindingAlias] doesn't conflict with an
1600            # interface member.
1601            if member.isAttr():
1602                for bindingAlias in member.bindingAliases:
1603                    checkDuplicateNames(member, bindingAlias, "BindingAlias")
1604
1605        # Conditional exposure makes no sense for interfaces with no
1606        # interface object.
1607        # And SecureContext makes sense for interfaces with no interface object,
1608        # since it is also propagated to interface members.
1609        if (
1610            self.isExposedConditionally(exclusions=["SecureContext"])
1611            and not self.hasInterfaceObject()
1612        ):
1613            raise WebIDLError(
1614                "Interface with no interface object is " "exposed conditionally",
1615                [self.location],
1616            )
1617
1618        # Value iterators are only allowed on interfaces with indexed getters,
1619        # and pair iterators are only allowed on interfaces without indexed
1620        # getters.
1621        if self.isIterable():
1622            iterableDecl = self.maplikeOrSetlikeOrIterable
1623            if iterableDecl.isValueIterator():
1624                if not indexedGetter:
1625                    raise WebIDLError(
1626                        "Interface with value iterator does not "
1627                        "support indexed properties",
1628                        [self.location, iterableDecl.location],
1629                    )
1630
1631                if iterableDecl.valueType != indexedGetter.signatures()[0][0]:
1632                    raise WebIDLError(
1633                        "Iterable type does not match indexed " "getter type",
1634                        [iterableDecl.location, indexedGetter.location],
1635                    )
1636
1637                if not hasLengthAttribute:
1638                    raise WebIDLError(
1639                        "Interface with value iterator does not "
1640                        'have an integer-typed "length" attribute',
1641                        [self.location, iterableDecl.location],
1642                    )
1643            else:
1644                assert iterableDecl.isPairIterator()
1645                if indexedGetter:
1646                    raise WebIDLError(
1647                        "Interface with pair iterator supports " "indexed properties",
1648                        [self.location, iterableDecl.location, indexedGetter.location],
1649                    )
1650
1651        if indexedGetter and not hasLengthAttribute:
1652            raise WebIDLError(
1653                "Interface with an indexed getter does not have "
1654                'an integer-typed "length" attribute',
1655                [self.location, indexedGetter.location],
1656            )
1657
1658    def setCallback(self, value):
1659        self._callback = value
1660
1661    def isCallback(self):
1662        return self._callback
1663
1664    def isSingleOperationInterface(self):
1665        assert self.isCallback() or self.isJSImplemented()
1666        return (
1667            # JS-implemented things should never need the
1668            # this-handling weirdness of single-operation interfaces.
1669            not self.isJSImplemented()
1670            and
1671            # Not inheriting from another interface
1672            not self.parent
1673            and
1674            # No attributes of any kinds
1675            not any(m.isAttr() for m in self.members)
1676            and
1677            # There is at least one regular operation, and all regular
1678            # operations have the same identifier
1679            len(
1680                set(
1681                    m.identifier.name
1682                    for m in self.members
1683                    if m.isMethod() and not m.isStatic()
1684                )
1685            )
1686            == 1
1687        )
1688
1689    def inheritanceDepth(self):
1690        depth = 0
1691        parent = self.parent
1692        while parent:
1693            depth = depth + 1
1694            parent = parent.parent
1695        return depth
1696
1697    def hasConstants(self):
1698        return any(m.isConst() for m in self.members)
1699
1700    def hasInterfaceObject(self):
1701        if self.isCallback():
1702            return self.hasConstants()
1703        return not hasattr(self, "_noInterfaceObject")
1704
1705    def hasInterfacePrototypeObject(self):
1706        return (
1707            not self.isCallback()
1708            and not self.isNamespace()
1709            and self.getUserData("hasConcreteDescendant", False)
1710        )
1711
1712    def addIncludedMixin(self, includedMixin):
1713        assert isinstance(includedMixin, IDLInterfaceMixin)
1714        self.includedMixins.add(includedMixin)
1715
1716    def getInheritedInterfaces(self):
1717        """
1718        Returns a list of the interfaces this interface inherits from
1719        (not including this interface itself).  The list is in order
1720        from most derived to least derived.
1721        """
1722        assert self._finished
1723        if not self.parent:
1724            return []
1725        parentInterfaces = self.parent.getInheritedInterfaces()
1726        parentInterfaces.insert(0, self.parent)
1727        return parentInterfaces
1728
1729    def findInterfaceLoopPoint(self, otherInterface):
1730        """
1731        Finds an interface amongst our ancestors that inherits from otherInterface.
1732        If there is no such interface, returns None.
1733        """
1734        if self.parent:
1735            if self.parent == otherInterface:
1736                return self
1737            loopPoint = self.parent.findInterfaceLoopPoint(otherInterface)
1738            if loopPoint:
1739                return loopPoint
1740        return None
1741
1742    def setNonPartial(self, location, parent, members):
1743        assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
1744        IDLInterfaceOrInterfaceMixinOrNamespace.setNonPartial(self, location, members)
1745        assert not self.parent
1746        self.parent = parent
1747
1748    def getJSImplementation(self):
1749        classId = self.getExtendedAttribute("JSImplementation")
1750        if not classId:
1751            return classId
1752        assert isinstance(classId, list)
1753        assert len(classId) == 1
1754        return classId[0]
1755
1756    def isJSImplemented(self):
1757        return bool(self.getJSImplementation())
1758
1759    def hasProbablyShortLivingWrapper(self):
1760        current = self
1761        while current:
1762            if current.getExtendedAttribute("ProbablyShortLivingWrapper"):
1763                return True
1764            current = current.parent
1765        return False
1766
1767    def hasChildInterfaces(self):
1768        return self._hasChildInterfaces
1769
1770    def isOnGlobalProtoChain(self):
1771        return self._isOnGlobalProtoChain
1772
1773    def _getDependentObjects(self):
1774        deps = set(self.members)
1775        deps.update(self.includedMixins)
1776        if self.parent:
1777            deps.add(self.parent)
1778        return deps
1779
1780    def hasMembersInSlots(self):
1781        return self._ownMembersInSlots != 0
1782
1783    conditionExtendedAttributes = ["Pref", "ChromeOnly", "Func", "SecureContext"]
1784
1785    def isExposedConditionally(self, exclusions=[]):
1786        return any(
1787            ((not a in exclusions) and self.getExtendedAttribute(a))
1788            for a in self.conditionExtendedAttributes
1789        )
1790
1791
1792class IDLInterface(IDLInterfaceOrNamespace):
1793    def __init__(
1794        self,
1795        location,
1796        parentScope,
1797        name,
1798        parent,
1799        members,
1800        isKnownNonPartial,
1801        classNameOverride=None,
1802    ):
1803        IDLInterfaceOrNamespace.__init__(
1804            self, location, parentScope, name, parent, members, isKnownNonPartial
1805        )
1806        self.classNameOverride = classNameOverride
1807
1808    def __str__(self):
1809        return "Interface '%s'" % self.identifier.name
1810
1811    def isInterface(self):
1812        return True
1813
1814    def getClassName(self):
1815        if self.classNameOverride:
1816            return self.classNameOverride
1817        return IDLInterfaceOrNamespace.getClassName(self)
1818
1819    def addExtendedAttributes(self, attrs):
1820        for attr in attrs:
1821            identifier = attr.identifier()
1822
1823            # Special cased attrs
1824            if identifier == "TreatNonCallableAsNull":
1825                raise WebIDLError(
1826                    "TreatNonCallableAsNull cannot be specified on interfaces",
1827                    [attr.location, self.location],
1828                )
1829            if identifier == "LegacyTreatNonObjectAsNull":
1830                raise WebIDLError(
1831                    "LegacyTreatNonObjectAsNull cannot be specified on interfaces",
1832                    [attr.location, self.location],
1833                )
1834            elif identifier == "LegacyNoInterfaceObject":
1835                if not attr.noArguments():
1836                    raise WebIDLError(
1837                        "[LegacyNoInterfaceObject] must take no arguments",
1838                        [attr.location],
1839                    )
1840
1841                self._noInterfaceObject = True
1842            elif identifier == "LegacyFactoryFunction":
1843                if not attr.hasValue():
1844                    raise WebIDLError(
1845                        "LegacyFactoryFunction must either take an identifier or take a named argument list",
1846                        [attr.location],
1847                    )
1848
1849                args = attr.args() if attr.hasArgs() else []
1850
1851                retType = IDLWrapperType(self.location, self)
1852
1853                method = IDLConstructor(attr.location, args, attr.value())
1854                method.reallyInit(self)
1855
1856                # Named constructors are always assumed to be able to
1857                # throw (since there's no way to indicate otherwise).
1858                method.addExtendedAttributes(
1859                    [IDLExtendedAttribute(self.location, ("Throws",))]
1860                )
1861
1862                # We need to detect conflicts for LegacyFactoryFunctions across
1863                # interfaces. We first call resolve on the parentScope,
1864                # which will merge all LegacyFactoryFunctions with the same
1865                # identifier accross interfaces as overloads.
1866                method.resolve(self.parentScope)
1867
1868                # Then we look up the identifier on the parentScope. If the
1869                # result is the same as the method we're adding then it
1870                # hasn't been added as an overload and it's the first time
1871                # we've encountered a LegacyFactoryFunction with that identifier.
1872                # If the result is not the same as the method we're adding
1873                # then it has been added as an overload and we need to check
1874                # whether the result is actually one of our existing
1875                # LegacyFactoryFunctions.
1876                newMethod = self.parentScope.lookupIdentifier(method.identifier)
1877                if newMethod == method:
1878                    self.legacyFactoryFunctions.append(method)
1879                elif newMethod not in self.legacyFactoryFunctions:
1880                    raise WebIDLError(
1881                        "LegacyFactoryFunction conflicts with a "
1882                        "LegacyFactoryFunction of a different interface",
1883                        [method.location, newMethod.location],
1884                    )
1885            elif identifier == "ExceptionClass":
1886                if not attr.noArguments():
1887                    raise WebIDLError(
1888                        "[ExceptionClass] must take no arguments", [attr.location]
1889                    )
1890                if self.parent:
1891                    raise WebIDLError(
1892                        "[ExceptionClass] must not be specified on "
1893                        "an interface with inherited interfaces",
1894                        [attr.location, self.location],
1895                    )
1896            elif identifier == "Global":
1897                if attr.hasValue():
1898                    self.globalNames = [attr.value()]
1899                elif attr.hasArgs():
1900                    self.globalNames = attr.args()
1901                else:
1902                    self.globalNames = [self.identifier.name]
1903                self.parentScope.addIfaceGlobalNames(
1904                    self.identifier.name, self.globalNames
1905                )
1906                self._isOnGlobalProtoChain = True
1907            elif identifier == "LegacyWindowAlias":
1908                if attr.hasValue():
1909                    self.legacyWindowAliases = [attr.value()]
1910                elif attr.hasArgs():
1911                    self.legacyWindowAliases = attr.args()
1912                else:
1913                    raise WebIDLError(
1914                        "[%s] must either take an identifier "
1915                        "or take an identifier list" % identifier,
1916                        [attr.location],
1917                    )
1918                for alias in self.legacyWindowAliases:
1919                    unresolved = IDLUnresolvedIdentifier(attr.location, alias)
1920                    IDLObjectWithIdentifier(attr.location, self.parentScope, unresolved)
1921            elif identifier == "SecureContext":
1922                if not attr.noArguments():
1923                    raise WebIDLError(
1924                        "[%s] must take no arguments" % identifier, [attr.location]
1925                    )
1926                # This gets propagated to all our members.
1927                for member in self.members:
1928                    if member.getExtendedAttribute("SecureContext"):
1929                        raise WebIDLError(
1930                            "[SecureContext] specified on both "
1931                            "an interface member and on the "
1932                            "interface itself",
1933                            [member.location, attr.location],
1934                        )
1935                    member.addExtendedAttributes([attr])
1936            elif (
1937                identifier == "NeedResolve"
1938                or identifier == "LegacyOverrideBuiltIns"
1939                or identifier == "ChromeOnly"
1940                or identifier == "LegacyUnforgeable"
1941                or identifier == "LegacyEventInit"
1942                or identifier == "ProbablyShortLivingWrapper"
1943                or identifier == "LegacyUnenumerableNamedProperties"
1944                or identifier == "RunConstructorInCallerCompartment"
1945                or identifier == "WantsEventListenerHooks"
1946                or identifier == "Serializable"
1947            ):
1948                # Known extended attributes that do not take values
1949                if not attr.noArguments():
1950                    raise WebIDLError(
1951                        "[%s] must take no arguments" % identifier, [attr.location]
1952                    )
1953            elif identifier == "Exposed":
1954                convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
1955            elif (
1956                identifier == "Pref"
1957                or identifier == "JSImplementation"
1958                or identifier == "HeaderFile"
1959                or identifier == "Func"
1960                or identifier == "Deprecated"
1961            ):
1962                # Known extended attributes that take a string value
1963                if not attr.hasValue():
1964                    raise WebIDLError(
1965                        "[%s] must have a value" % identifier, [attr.location]
1966                    )
1967            elif identifier == "InstrumentedProps":
1968                # Known extended attributes that take a list
1969                if not attr.hasArgs():
1970                    raise WebIDLError(
1971                        "[%s] must have arguments" % identifier, [attr.location]
1972                    )
1973            else:
1974                raise WebIDLError(
1975                    "Unknown extended attribute %s on interface" % identifier,
1976                    [attr.location],
1977                )
1978
1979            attrlist = attr.listValue()
1980            self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
1981
1982    def validate(self):
1983        IDLInterfaceOrNamespace.validate(self)
1984        if self.parent and self.isSerializable() and not self.parent.isSerializable():
1985            raise WebIDLError(
1986                "Serializable interface inherits from non-serializable "
1987                "interface.  Per spec, that means the object should not be "
1988                "serializable, so chances are someone made a mistake here "
1989                "somewhere.",
1990                [self.location, self.parent.location],
1991            )
1992
1993    def isSerializable(self):
1994        return self.getExtendedAttribute("Serializable")
1995
1996    def setNonPartial(self, location, parent, members):
1997        # Before we do anything else, finish initializing any constructors that
1998        # might be in "members", so we don't have partially-initialized objects
1999        # hanging around.  We couldn't do it before now because we needed to have
2000        # to have the IDLInterface on hand to properly set the return type.
2001        for member in members:
2002            if isinstance(member, IDLConstructor):
2003                member.reallyInit(self)
2004
2005        IDLInterfaceOrNamespace.setNonPartial(self, location, parent, members)
2006
2007
2008class IDLNamespace(IDLInterfaceOrNamespace):
2009    def __init__(self, location, parentScope, name, members, isKnownNonPartial):
2010        IDLInterfaceOrNamespace.__init__(
2011            self, location, parentScope, name, None, members, isKnownNonPartial
2012        )
2013
2014    def __str__(self):
2015        return "Namespace '%s'" % self.identifier.name
2016
2017    def isNamespace(self):
2018        return True
2019
2020    def addExtendedAttributes(self, attrs):
2021        # The set of things namespaces support is small enough it's simpler
2022        # to factor out into a separate method than it is to sprinkle
2023        # isNamespace() checks all through
2024        # IDLInterfaceOrNamespace.addExtendedAttributes.
2025        for attr in attrs:
2026            identifier = attr.identifier()
2027
2028            if identifier == "Exposed":
2029                convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
2030            elif identifier == "ClassString":
2031                # Takes a string value to override the default "Object" if
2032                # desired.
2033                if not attr.hasValue():
2034                    raise WebIDLError(
2035                        "[%s] must have a value" % identifier, [attr.location]
2036                    )
2037            elif identifier == "ProtoObjectHack" or identifier == "ChromeOnly":
2038                if not attr.noArguments():
2039                    raise WebIDLError(
2040                        "[%s] must not have arguments" % identifier, [attr.location]
2041                    )
2042            elif (
2043                identifier == "Pref"
2044                or identifier == "HeaderFile"
2045                or identifier == "Func"
2046            ):
2047                # Known extended attributes that take a string value
2048                if not attr.hasValue():
2049                    raise WebIDLError(
2050                        "[%s] must have a value" % identifier, [attr.location]
2051                    )
2052            else:
2053                raise WebIDLError(
2054                    "Unknown extended attribute %s on namespace" % identifier,
2055                    [attr.location],
2056                )
2057
2058            attrlist = attr.listValue()
2059            self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
2060
2061    def isSerializable(self):
2062        return False
2063
2064
2065class IDLDictionary(IDLObjectWithScope):
2066    def __init__(self, location, parentScope, name, parent, members):
2067        assert isinstance(parentScope, IDLScope)
2068        assert isinstance(name, IDLUnresolvedIdentifier)
2069        assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
2070
2071        self.parent = parent
2072        self._finished = False
2073        self.members = list(members)
2074        self._partialDictionaries = []
2075        self._extendedAttrDict = {}
2076        self.needsConversionToJS = False
2077        self.needsConversionFromJS = False
2078
2079        IDLObjectWithScope.__init__(self, location, parentScope, name)
2080
2081    def __str__(self):
2082        return "Dictionary '%s'" % self.identifier.name
2083
2084    def isDictionary(self):
2085        return True
2086
2087    def canBeEmpty(self):
2088        """
2089        Returns true if this dictionary can be empty (that is, it has no
2090        required members and neither do any of its ancestors).
2091        """
2092        return all(member.optional for member in self.members) and (
2093            not self.parent or self.parent.canBeEmpty()
2094        )
2095
2096    def finish(self, scope):
2097        if self._finished:
2098            return
2099
2100        self._finished = True
2101
2102        if self.parent:
2103            assert isinstance(self.parent, IDLIdentifierPlaceholder)
2104            oldParent = self.parent
2105            self.parent = self.parent.finish(scope)
2106            if not isinstance(self.parent, IDLDictionary):
2107                raise WebIDLError(
2108                    "Dictionary %s has parent that is not a dictionary"
2109                    % self.identifier.name,
2110                    [oldParent.location, self.parent.location],
2111                )
2112
2113            # Make sure the parent resolves all its members before we start
2114            # looking at them.
2115            self.parent.finish(scope)
2116
2117        # Now go ahead and merge in our partial dictionaries.
2118        for partial in self._partialDictionaries:
2119            partial.finish(scope)
2120            self.members.extend(partial.members)
2121
2122        for member in self.members:
2123            member.resolve(self)
2124            if not member.isComplete():
2125                member.complete(scope)
2126                assert member.type.isComplete()
2127
2128        # Members of a dictionary are sorted in lexicographic order
2129        self.members.sort(key=lambda x: x.identifier.name)
2130
2131        inheritedMembers = []
2132        ancestor = self.parent
2133        while ancestor:
2134            if ancestor == self:
2135                raise WebIDLError(
2136                    "Dictionary %s has itself as an ancestor" % self.identifier.name,
2137                    [self.identifier.location],
2138                )
2139            inheritedMembers.extend(ancestor.members)
2140            ancestor = ancestor.parent
2141
2142        # Catch name duplication
2143        for inheritedMember in inheritedMembers:
2144            for member in self.members:
2145                if member.identifier.name == inheritedMember.identifier.name:
2146                    raise WebIDLError(
2147                        "Dictionary %s has two members with name %s"
2148                        % (self.identifier.name, member.identifier.name),
2149                        [member.location, inheritedMember.location],
2150                    )
2151
2152    def validate(self):
2153        def typeContainsDictionary(memberType, dictionary):
2154            """
2155            Returns a tuple whose:
2156
2157                - First element is a Boolean value indicating whether
2158                  memberType contains dictionary.
2159
2160                - Second element is:
2161                    A list of locations that leads from the type that was passed in
2162                    the memberType argument, to the dictionary being validated,
2163                    if the boolean value in the first element is True.
2164
2165                    None, if the boolean value in the first element is False.
2166            """
2167
2168            if (
2169                memberType.nullable()
2170                or memberType.isSequence()
2171                or memberType.isRecord()
2172            ):
2173                return typeContainsDictionary(memberType.inner, dictionary)
2174
2175            if memberType.isDictionary():
2176                if memberType.inner == dictionary:
2177                    return (True, [memberType.location])
2178
2179                (contains, locations) = dictionaryContainsDictionary(
2180                    memberType.inner, dictionary
2181                )
2182                if contains:
2183                    return (True, [memberType.location] + locations)
2184
2185            if memberType.isUnion():
2186                for member in memberType.flatMemberTypes:
2187                    (contains, locations) = typeContainsDictionary(member, dictionary)
2188                    if contains:
2189                        return (True, locations)
2190
2191            return (False, None)
2192
2193        def dictionaryContainsDictionary(dictMember, dictionary):
2194            for member in dictMember.members:
2195                (contains, locations) = typeContainsDictionary(member.type, dictionary)
2196                if contains:
2197                    return (True, [member.location] + locations)
2198
2199            if dictMember.parent:
2200                if dictMember.parent == dictionary:
2201                    return (True, [dictMember.location])
2202                else:
2203                    (contains, locations) = dictionaryContainsDictionary(
2204                        dictMember.parent, dictionary
2205                    )
2206                    if contains:
2207                        return (True, [dictMember.location] + locations)
2208
2209            return (False, None)
2210
2211        for member in self.members:
2212            if member.type.isDictionary() and member.type.nullable():
2213                raise WebIDLError(
2214                    "Dictionary %s has member with nullable "
2215                    "dictionary type" % self.identifier.name,
2216                    [member.location],
2217                )
2218            (contains, locations) = typeContainsDictionary(member.type, self)
2219            if contains:
2220                raise WebIDLError(
2221                    "Dictionary %s has member with itself as type."
2222                    % self.identifier.name,
2223                    [member.location] + locations,
2224                )
2225
2226    def getExtendedAttribute(self, name):
2227        return self._extendedAttrDict.get(name, None)
2228
2229    def addExtendedAttributes(self, attrs):
2230        for attr in attrs:
2231            identifier = attr.identifier()
2232
2233            if identifier == "GenerateInitFromJSON" or identifier == "GenerateInit":
2234                if not attr.noArguments():
2235                    raise WebIDLError(
2236                        "[%s] must not have arguments" % identifier, [attr.location]
2237                    )
2238                self.needsConversionFromJS = True
2239            elif (
2240                identifier == "GenerateConversionToJS" or identifier == "GenerateToJSON"
2241            ):
2242                if not attr.noArguments():
2243                    raise WebIDLError(
2244                        "[%s] must not have arguments" % identifier, [attr.location]
2245                    )
2246                # ToJSON methods require to-JS conversion, because we
2247                # implement ToJSON by converting to a JS object and
2248                # then using JSON.stringify.
2249                self.needsConversionToJS = True
2250            else:
2251                raise WebIDLError(
2252                    "[%s] extended attribute not allowed on "
2253                    "dictionaries" % identifier,
2254                    [attr.location],
2255                )
2256
2257            self._extendedAttrDict[identifier] = True
2258
2259    def _getDependentObjects(self):
2260        deps = set(self.members)
2261        if self.parent:
2262            deps.add(self.parent)
2263        return deps
2264
2265    def addPartialDictionary(self, partial):
2266        assert self.identifier.name == partial.identifier.name
2267        self._partialDictionaries.append(partial)
2268
2269
2270class IDLEnum(IDLObjectWithIdentifier):
2271    def __init__(self, location, parentScope, name, values):
2272        assert isinstance(parentScope, IDLScope)
2273        assert isinstance(name, IDLUnresolvedIdentifier)
2274
2275        if len(values) != len(set(values)):
2276            raise WebIDLError(
2277                "Enum %s has multiple identical strings" % name.name, [location]
2278            )
2279
2280        IDLObjectWithIdentifier.__init__(self, location, parentScope, name)
2281        self._values = values
2282
2283    def values(self):
2284        return self._values
2285
2286    def finish(self, scope):
2287        pass
2288
2289    def validate(self):
2290        pass
2291
2292    def isEnum(self):
2293        return True
2294
2295    def addExtendedAttributes(self, attrs):
2296        if len(attrs) != 0:
2297            raise WebIDLError(
2298                "There are no extended attributes that are " "allowed on enums",
2299                [attrs[0].location, self.location],
2300            )
2301
2302    def _getDependentObjects(self):
2303        return set()
2304
2305
2306class IDLType(IDLObject):
2307    Tags = enum(
2308        # The integer types
2309        "int8",
2310        "uint8",
2311        "int16",
2312        "uint16",
2313        "int32",
2314        "uint32",
2315        "int64",
2316        "uint64",
2317        # Additional primitive types
2318        "bool",
2319        "unrestricted_float",
2320        "float",
2321        "unrestricted_double",
2322        # "double" last primitive type to match IDLBuiltinType
2323        "double",
2324        # Other types
2325        "any",
2326        "domstring",
2327        "bytestring",
2328        "usvstring",
2329        "utf8string",
2330        "jsstring",
2331        "object",
2332        "void",
2333        # Funny stuff
2334        "interface",
2335        "dictionary",
2336        "enum",
2337        "callback",
2338        "union",
2339        "sequence",
2340        "record",
2341        "promise",
2342    )
2343
2344    def __init__(self, location, name):
2345        IDLObject.__init__(self, location)
2346        self.name = name
2347        self.builtin = False
2348        self.legacyNullToEmptyString = False
2349        self._clamp = False
2350        self._enforceRange = False
2351        self._allowShared = False
2352        self._extendedAttrDict = {}
2353
2354    def __hash__(self):
2355        return (
2356            hash(self.builtin)
2357            + hash(self.name)
2358            + hash(self._clamp)
2359            + hash(self._enforceRange)
2360            + hash(self.legacyNullToEmptyString)
2361            + hash(self._allowShared)
2362        )
2363
2364    def __eq__(self, other):
2365        return (
2366            other
2367            and self.builtin == other.builtin
2368            and self.name == other.name
2369            and self._clamp == other.hasClamp()
2370            and self._enforceRange == other.hasEnforceRange()
2371            and self.legacyNullToEmptyString == other.legacyNullToEmptyString
2372            and self._allowShared == other.hasAllowShared()
2373        )
2374
2375    def __ne__(self, other):
2376        return not self == other
2377
2378    def __str__(self):
2379        return str(self.name)
2380
2381    def prettyName(self):
2382        """
2383        A name that looks like what this type is named in the IDL spec.  By default
2384        this is just our .name, but types that have more interesting spec
2385        representations should override this.
2386        """
2387        return str(self.name)
2388
2389    def isType(self):
2390        return True
2391
2392    def nullable(self):
2393        return False
2394
2395    def isPrimitive(self):
2396        return False
2397
2398    def isBoolean(self):
2399        return False
2400
2401    def isNumeric(self):
2402        return False
2403
2404    def isString(self):
2405        return False
2406
2407    def isByteString(self):
2408        return False
2409
2410    def isDOMString(self):
2411        return False
2412
2413    def isUSVString(self):
2414        return False
2415
2416    def isUTF8String(self):
2417        return False
2418
2419    def isJSString(self):
2420        return False
2421
2422    def isVoid(self):
2423        return self.name == "Void"
2424
2425    def isSequence(self):
2426        return False
2427
2428    def isRecord(self):
2429        return False
2430
2431    def isReadableStream(self):
2432        return False
2433
2434    def isArrayBuffer(self):
2435        return False
2436
2437    def isArrayBufferView(self):
2438        return False
2439
2440    def isTypedArray(self):
2441        return False
2442
2443    def isBufferSource(self):
2444        return self.isArrayBuffer() or self.isArrayBufferView() or self.isTypedArray()
2445
2446    def isCallbackInterface(self):
2447        return False
2448
2449    def isNonCallbackInterface(self):
2450        return False
2451
2452    def isGeckoInterface(self):
2453        """Returns a boolean indicating whether this type is an 'interface'
2454        type that is implemented in Gecko. At the moment, this returns
2455        true for all interface types that are not types from the TypedArray
2456        spec."""
2457        return self.isInterface() and not self.isSpiderMonkeyInterface()
2458
2459    def isSpiderMonkeyInterface(self):
2460        """Returns a boolean indicating whether this type is an 'interface'
2461        type that is implemented in SpiderMonkey."""
2462        return self.isInterface() and (self.isBufferSource() or self.isReadableStream())
2463
2464    def isAny(self):
2465        return self.tag() == IDLType.Tags.any
2466
2467    def isObject(self):
2468        return self.tag() == IDLType.Tags.object
2469
2470    def isPromise(self):
2471        return False
2472
2473    def isComplete(self):
2474        return True
2475
2476    def includesRestrictedFloat(self):
2477        return False
2478
2479    def isFloat(self):
2480        return False
2481
2482    def isUnrestricted(self):
2483        # Should only call this on float types
2484        assert self.isFloat()
2485
2486    def isJSONType(self):
2487        return False
2488
2489    def hasClamp(self):
2490        return self._clamp
2491
2492    def hasEnforceRange(self):
2493        return self._enforceRange
2494
2495    def hasAllowShared(self):
2496        return self._allowShared
2497
2498    def tag(self):
2499        assert False  # Override me!
2500
2501    def treatNonCallableAsNull(self):
2502        assert self.tag() == IDLType.Tags.callback
2503        return self.nullable() and self.inner.callback._treatNonCallableAsNull
2504
2505    def treatNonObjectAsNull(self):
2506        assert self.tag() == IDLType.Tags.callback
2507        return self.nullable() and self.inner.callback._treatNonObjectAsNull
2508
2509    def withExtendedAttributes(self, attrs):
2510        if len(attrs) > 0:
2511            raise WebIDLError(
2512                "Extended attributes on types only supported for builtins",
2513                [attrs[0].location, self.location],
2514            )
2515        return self
2516
2517    def getExtendedAttribute(self, name):
2518        return self._extendedAttrDict.get(name, None)
2519
2520    def resolveType(self, parentScope):
2521        pass
2522
2523    def unroll(self):
2524        return self
2525
2526    def isDistinguishableFrom(self, other):
2527        raise TypeError(
2528            "Can't tell whether a generic type is or is not "
2529            "distinguishable from other things"
2530        )
2531
2532    def isExposedInAllOf(self, exposureSet):
2533        return True
2534
2535
2536class IDLUnresolvedType(IDLType):
2537    """
2538    Unresolved types are interface types
2539    """
2540
2541    def __init__(self, location, name, attrs=[]):
2542        IDLType.__init__(self, location, name)
2543        self.extraTypeAttributes = attrs
2544
2545    def isComplete(self):
2546        return False
2547
2548    def complete(self, scope):
2549        obj = None
2550        try:
2551            obj = scope._lookupIdentifier(self.name)
2552        except:
2553            raise WebIDLError("Unresolved type '%s'." % self.name, [self.location])
2554
2555        assert obj
2556        if obj.isType():
2557            print(obj)
2558        assert not obj.isType()
2559        if obj.isTypedef():
2560            assert self.name.name == obj.identifier.name
2561            typedefType = IDLTypedefType(self.location, obj.innerType, obj.identifier)
2562            assert not typedefType.isComplete()
2563            return typedefType.complete(scope).withExtendedAttributes(
2564                self.extraTypeAttributes
2565            )
2566        elif obj.isCallback() and not obj.isInterface():
2567            assert self.name.name == obj.identifier.name
2568            return IDLCallbackType(self.location, obj)
2569
2570        name = self.name.resolve(scope, None)
2571        return IDLWrapperType(self.location, obj)
2572
2573    def withExtendedAttributes(self, attrs):
2574        return IDLUnresolvedType(self.location, self.name, attrs)
2575
2576    def isDistinguishableFrom(self, other):
2577        raise TypeError(
2578            "Can't tell whether an unresolved type is or is not "
2579            "distinguishable from other things"
2580        )
2581
2582
2583class IDLParametrizedType(IDLType):
2584    def __init__(self, location, name, innerType):
2585        IDLType.__init__(self, location, name)
2586        self.builtin = False
2587        self.inner = innerType
2588
2589    def includesRestrictedFloat(self):
2590        return self.inner.includesRestrictedFloat()
2591
2592    def resolveType(self, parentScope):
2593        assert isinstance(parentScope, IDLScope)
2594        self.inner.resolveType(parentScope)
2595
2596    def isComplete(self):
2597        return self.inner.isComplete()
2598
2599    def unroll(self):
2600        return self.inner.unroll()
2601
2602    def _getDependentObjects(self):
2603        return self.inner._getDependentObjects()
2604
2605
2606class IDLNullableType(IDLParametrizedType):
2607    def __init__(self, location, innerType):
2608        assert not innerType.isVoid()
2609        assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any]
2610
2611        IDLParametrizedType.__init__(self, location, None, innerType)
2612
2613    def __hash__(self):
2614        return hash(self.inner)
2615
2616    def __eq__(self, other):
2617        return isinstance(other, IDLNullableType) and self.inner == other.inner
2618
2619    def __str__(self):
2620        return self.inner.__str__() + "OrNull"
2621
2622    def prettyName(self):
2623        return self.inner.prettyName() + "?"
2624
2625    def nullable(self):
2626        return True
2627
2628    def isCallback(self):
2629        return self.inner.isCallback()
2630
2631    def isPrimitive(self):
2632        return self.inner.isPrimitive()
2633
2634    def isBoolean(self):
2635        return self.inner.isBoolean()
2636
2637    def isNumeric(self):
2638        return self.inner.isNumeric()
2639
2640    def isString(self):
2641        return self.inner.isString()
2642
2643    def isByteString(self):
2644        return self.inner.isByteString()
2645
2646    def isDOMString(self):
2647        return self.inner.isDOMString()
2648
2649    def isUSVString(self):
2650        return self.inner.isUSVString()
2651
2652    def isUTF8String(self):
2653        return self.inner.isUTF8String()
2654
2655    def isJSString(self):
2656        return self.inner.isJSString()
2657
2658    def isFloat(self):
2659        return self.inner.isFloat()
2660
2661    def isUnrestricted(self):
2662        return self.inner.isUnrestricted()
2663
2664    def isInteger(self):
2665        return self.inner.isInteger()
2666
2667    def isVoid(self):
2668        return False
2669
2670    def isSequence(self):
2671        return self.inner.isSequence()
2672
2673    def isRecord(self):
2674        return self.inner.isRecord()
2675
2676    def isReadableStream(self):
2677        return self.inner.isReadableStream()
2678
2679    def isArrayBuffer(self):
2680        return self.inner.isArrayBuffer()
2681
2682    def isArrayBufferView(self):
2683        return self.inner.isArrayBufferView()
2684
2685    def isTypedArray(self):
2686        return self.inner.isTypedArray()
2687
2688    def isDictionary(self):
2689        return self.inner.isDictionary()
2690
2691    def isInterface(self):
2692        return self.inner.isInterface()
2693
2694    def isPromise(self):
2695        # There is no such thing as a nullable Promise.
2696        assert not self.inner.isPromise()
2697        return False
2698
2699    def isCallbackInterface(self):
2700        return self.inner.isCallbackInterface()
2701
2702    def isNonCallbackInterface(self):
2703        return self.inner.isNonCallbackInterface()
2704
2705    def isEnum(self):
2706        return self.inner.isEnum()
2707
2708    def isUnion(self):
2709        return self.inner.isUnion()
2710
2711    def isJSONType(self):
2712        return self.inner.isJSONType()
2713
2714    def hasClamp(self):
2715        return self.inner.hasClamp()
2716
2717    def hasEnforceRange(self):
2718        return self.inner.hasEnforceRange()
2719
2720    def hasAllowShared(self):
2721        return self.inner.hasAllowShared()
2722
2723    def isComplete(self):
2724        return self.name is not None
2725
2726    def tag(self):
2727        return self.inner.tag()
2728
2729    def complete(self, scope):
2730        if not self.inner.isComplete():
2731            self.inner = self.inner.complete(scope)
2732        assert self.inner.isComplete()
2733
2734        if self.inner.nullable():
2735            raise WebIDLError(
2736                "The inner type of a nullable type must not be " "a nullable type",
2737                [self.location, self.inner.location],
2738            )
2739        if self.inner.isUnion():
2740            if self.inner.hasNullableType:
2741                raise WebIDLError(
2742                    "The inner type of a nullable type must not "
2743                    "be a union type that itself has a nullable "
2744                    "type as a member type",
2745                    [self.location],
2746                )
2747        if self.inner.isDOMString():
2748            if self.inner.legacyNullToEmptyString:
2749                raise WebIDLError(
2750                    "[LegacyNullToEmptyString] not allowed on a nullable DOMString",
2751                    [self.location, self.inner.location],
2752                )
2753
2754        self.name = self.inner.name + "OrNull"
2755        return self
2756
2757    def isDistinguishableFrom(self, other):
2758        if (
2759            other.nullable()
2760            or other.isDictionary()
2761            or (
2762                other.isUnion() and (other.hasNullableType or other.hasDictionaryType())
2763            )
2764        ):
2765            # Can't tell which type null should become
2766            return False
2767        return self.inner.isDistinguishableFrom(other)
2768
2769    def withExtendedAttributes(self, attrs):
2770        # See https://github.com/heycam/webidl/issues/827#issuecomment-565131350
2771        # Allowing extended attributes to apply to a nullable type is an intermediate solution.
2772        # A potential longer term solution is to introduce a null type and get rid of nullables.
2773        # For example, we could do `([Clamp] long or null) foo` in the future.
2774        return IDLNullableType(self.location, self.inner.withExtendedAttributes(attrs))
2775
2776
2777class IDLSequenceType(IDLParametrizedType):
2778    def __init__(self, location, parameterType):
2779        assert not parameterType.isVoid()
2780
2781        IDLParametrizedType.__init__(self, location, parameterType.name, parameterType)
2782        # Need to set self.name up front if our inner type is already complete,
2783        # since in that case our .complete() won't be called.
2784        if self.inner.isComplete():
2785            self.name = self.inner.name + "Sequence"
2786
2787    def __hash__(self):
2788        return hash(self.inner)
2789
2790    def __eq__(self, other):
2791        return isinstance(other, IDLSequenceType) and self.inner == other.inner
2792
2793    def __str__(self):
2794        return self.inner.__str__() + "Sequence"
2795
2796    def prettyName(self):
2797        return "sequence<%s>" % self.inner.prettyName()
2798
2799    def isVoid(self):
2800        return False
2801
2802    def isSequence(self):
2803        return True
2804
2805    def isJSONType(self):
2806        return self.inner.isJSONType()
2807
2808    def tag(self):
2809        return IDLType.Tags.sequence
2810
2811    def complete(self, scope):
2812        self.inner = self.inner.complete(scope)
2813        self.name = self.inner.name + "Sequence"
2814        return self
2815
2816    def isDistinguishableFrom(self, other):
2817        if other.isPromise():
2818            return False
2819        if other.isUnion():
2820            # Just forward to the union; it'll deal
2821            return other.isDistinguishableFrom(self)
2822        return (
2823            other.isPrimitive()
2824            or other.isString()
2825            or other.isEnum()
2826            or other.isInterface()
2827            or other.isDictionary()
2828            or other.isCallback()
2829            or other.isRecord()
2830        )
2831
2832
2833class IDLRecordType(IDLParametrizedType):
2834    def __init__(self, location, keyType, valueType):
2835        assert keyType.isString()
2836        assert keyType.isComplete()
2837        assert not valueType.isVoid()
2838
2839        IDLParametrizedType.__init__(self, location, valueType.name, valueType)
2840        self.keyType = keyType
2841
2842        # Need to set self.name up front if our inner type is already complete,
2843        # since in that case our .complete() won't be called.
2844        if self.inner.isComplete():
2845            self.name = self.keyType.name + self.inner.name + "Record"
2846
2847    def __hash__(self):
2848        return hash(self.inner)
2849
2850    def __eq__(self, other):
2851        return isinstance(other, IDLRecordType) and self.inner == other.inner
2852
2853    def __str__(self):
2854        return self.keyType.__str__() + self.inner.__str__() + "Record"
2855
2856    def prettyName(self):
2857        return "record<%s, %s>" % (self.keyType.prettyName(), self.inner.prettyName())
2858
2859    def isRecord(self):
2860        return True
2861
2862    def isJSONType(self):
2863        return self.inner.isJSONType()
2864
2865    def tag(self):
2866        return IDLType.Tags.record
2867
2868    def complete(self, scope):
2869        self.inner = self.inner.complete(scope)
2870        self.name = self.keyType.name + self.inner.name + "Record"
2871        return self
2872
2873    def unroll(self):
2874        # We do not unroll our inner.  Just stop at ourselves.  That
2875        # lets us add headers for both ourselves and our inner as
2876        # needed.
2877        return self
2878
2879    def isDistinguishableFrom(self, other):
2880        if other.isPromise():
2881            return False
2882        if other.isUnion():
2883            # Just forward to the union; it'll deal
2884            return other.isDistinguishableFrom(self)
2885        return (
2886            other.isPrimitive()
2887            or other.isString()
2888            or other.isEnum()
2889            or other.isNonCallbackInterface()
2890            or other.isSequence()
2891        )
2892
2893    def isExposedInAllOf(self, exposureSet):
2894        return self.inner.unroll().isExposedInAllOf(exposureSet)
2895
2896
2897class IDLUnionType(IDLType):
2898    def __init__(self, location, memberTypes):
2899        IDLType.__init__(self, location, "")
2900        self.memberTypes = memberTypes
2901        self.hasNullableType = False
2902        self._dictionaryType = None
2903        self.flatMemberTypes = None
2904        self.builtin = False
2905
2906    def __eq__(self, other):
2907        return isinstance(other, IDLUnionType) and self.memberTypes == other.memberTypes
2908
2909    def __hash__(self):
2910        assert self.isComplete()
2911        return self.name.__hash__()
2912
2913    def prettyName(self):
2914        return "(" + " or ".join(m.prettyName() for m in self.memberTypes) + ")"
2915
2916    def isVoid(self):
2917        return False
2918
2919    def isUnion(self):
2920        return True
2921
2922    def isJSONType(self):
2923        return all(m.isJSONType() for m in self.memberTypes)
2924
2925    def includesRestrictedFloat(self):
2926        return any(t.includesRestrictedFloat() for t in self.memberTypes)
2927
2928    def tag(self):
2929        return IDLType.Tags.union
2930
2931    def resolveType(self, parentScope):
2932        assert isinstance(parentScope, IDLScope)
2933        for t in self.memberTypes:
2934            t.resolveType(parentScope)
2935
2936    def isComplete(self):
2937        return self.flatMemberTypes is not None
2938
2939    def complete(self, scope):
2940        def typeName(type):
2941            if isinstance(type, IDLNullableType):
2942                return typeName(type.inner) + "OrNull"
2943            if isinstance(type, IDLWrapperType):
2944                return typeName(type._identifier.object())
2945            if isinstance(type, IDLObjectWithIdentifier):
2946                return typeName(type.identifier)
2947            if isinstance(type, IDLBuiltinType) and type.hasAllowShared():
2948                assert type.isBufferSource()
2949                return "MaybeShared" + type.name
2950            return type.name
2951
2952        for (i, type) in enumerate(self.memberTypes):
2953            if not type.isComplete():
2954                self.memberTypes[i] = type.complete(scope)
2955
2956        self.name = "Or".join(typeName(type) for type in self.memberTypes)
2957        self.flatMemberTypes = list(self.memberTypes)
2958        i = 0
2959        while i < len(self.flatMemberTypes):
2960            if self.flatMemberTypes[i].nullable():
2961                if self.hasNullableType:
2962                    raise WebIDLError(
2963                        "Can't have more than one nullable types in a union",
2964                        [nullableType.location, self.flatMemberTypes[i].location],
2965                    )
2966                if self.hasDictionaryType():
2967                    raise WebIDLError(
2968                        "Can't have a nullable type and a "
2969                        "dictionary type in a union",
2970                        [
2971                            self._dictionaryType.location,
2972                            self.flatMemberTypes[i].location,
2973                        ],
2974                    )
2975                self.hasNullableType = True
2976                nullableType = self.flatMemberTypes[i]
2977                self.flatMemberTypes[i] = self.flatMemberTypes[i].inner
2978                continue
2979            if self.flatMemberTypes[i].isDictionary():
2980                if self.hasNullableType:
2981                    raise WebIDLError(
2982                        "Can't have a nullable type and a "
2983                        "dictionary type in a union",
2984                        [nullableType.location, self.flatMemberTypes[i].location],
2985                    )
2986                self._dictionaryType = self.flatMemberTypes[i]
2987            elif self.flatMemberTypes[i].isUnion():
2988                self.flatMemberTypes[i : i + 1] = self.flatMemberTypes[i].memberTypes
2989                continue
2990            i += 1
2991
2992        for (i, t) in enumerate(self.flatMemberTypes[:-1]):
2993            for u in self.flatMemberTypes[i + 1 :]:
2994                if not t.isDistinguishableFrom(u):
2995                    raise WebIDLError(
2996                        "Flat member types of a union should be "
2997                        "distinguishable, " + str(t) + " is not "
2998                        "distinguishable from " + str(u),
2999                        [self.location, t.location, u.location],
3000                    )
3001
3002        return self
3003
3004    def isDistinguishableFrom(self, other):
3005        if self.hasNullableType and other.nullable():
3006            # Can't tell which type null should become
3007            return False
3008        if other.isUnion():
3009            otherTypes = other.unroll().memberTypes
3010        else:
3011            otherTypes = [other]
3012        # For every type in otherTypes, check that it's distinguishable from
3013        # every type in our types
3014        for u in otherTypes:
3015            if any(not t.isDistinguishableFrom(u) for t in self.memberTypes):
3016                return False
3017        return True
3018
3019    def isExposedInAllOf(self, exposureSet):
3020        # We could have different member types in different globals.  Just make sure that each thing in exposureSet has one of our member types exposed in it.
3021        for globalName in exposureSet:
3022            if not any(
3023                t.unroll().isExposedInAllOf(set([globalName]))
3024                for t in self.flatMemberTypes
3025            ):
3026                return False
3027        return True
3028
3029    def hasDictionaryType(self):
3030        return self._dictionaryType is not None
3031
3032    def hasPossiblyEmptyDictionaryType(self):
3033        return (
3034            self._dictionaryType is not None and self._dictionaryType.inner.canBeEmpty()
3035        )
3036
3037    def _getDependentObjects(self):
3038        return set(self.memberTypes)
3039
3040
3041class IDLTypedefType(IDLType):
3042    def __init__(self, location, innerType, name):
3043        IDLType.__init__(self, location, name)
3044        self.inner = innerType
3045        self.builtin = False
3046
3047    def __hash__(self):
3048        return hash(self.inner)
3049
3050    def __eq__(self, other):
3051        return isinstance(other, IDLTypedefType) and self.inner == other.inner
3052
3053    def __str__(self):
3054        return self.name
3055
3056    def nullable(self):
3057        return self.inner.nullable()
3058
3059    def isPrimitive(self):
3060        return self.inner.isPrimitive()
3061
3062    def isBoolean(self):
3063        return self.inner.isBoolean()
3064
3065    def isNumeric(self):
3066        return self.inner.isNumeric()
3067
3068    def isString(self):
3069        return self.inner.isString()
3070
3071    def isByteString(self):
3072        return self.inner.isByteString()
3073
3074    def isDOMString(self):
3075        return self.inner.isDOMString()
3076
3077    def isUSVString(self):
3078        return self.inner.isUSVString()
3079
3080    def isUTF8String(self):
3081        return self.inner.isUTF8String()
3082
3083    def isJSString(self):
3084        return self.inner.isJSString()
3085
3086    def isVoid(self):
3087        return self.inner.isVoid()
3088
3089    def isJSONType(self):
3090        return self.inner.isJSONType()
3091
3092    def isSequence(self):
3093        return self.inner.isSequence()
3094
3095    def isRecord(self):
3096        return self.inner.isRecord()
3097
3098    def isReadableStream(self):
3099        return self.inner.isReadableStream()
3100
3101    def isDictionary(self):
3102        return self.inner.isDictionary()
3103
3104    def isArrayBuffer(self):
3105        return self.inner.isArrayBuffer()
3106
3107    def isArrayBufferView(self):
3108        return self.inner.isArrayBufferView()
3109
3110    def isTypedArray(self):
3111        return self.inner.isTypedArray()
3112
3113    def isInterface(self):
3114        return self.inner.isInterface()
3115
3116    def isCallbackInterface(self):
3117        return self.inner.isCallbackInterface()
3118
3119    def isNonCallbackInterface(self):
3120        return self.inner.isNonCallbackInterface()
3121
3122    def isComplete(self):
3123        return False
3124
3125    def complete(self, parentScope):
3126        if not self.inner.isComplete():
3127            self.inner = self.inner.complete(parentScope)
3128        assert self.inner.isComplete()
3129        return self.inner
3130
3131    # Do we need a resolveType impl?  I don't think it's particularly useful....
3132
3133    def tag(self):
3134        return self.inner.tag()
3135
3136    def unroll(self):
3137        return self.inner.unroll()
3138
3139    def isDistinguishableFrom(self, other):
3140        return self.inner.isDistinguishableFrom(other)
3141
3142    def _getDependentObjects(self):
3143        return self.inner._getDependentObjects()
3144
3145    def withExtendedAttributes(self, attrs):
3146        return IDLTypedefType(
3147            self.location, self.inner.withExtendedAttributes(attrs), self.name
3148        )
3149
3150
3151class IDLTypedef(IDLObjectWithIdentifier):
3152    def __init__(self, location, parentScope, innerType, name):
3153        # Set self.innerType first, because IDLObjectWithIdentifier.__init__
3154        # will call our __str__, which wants to use it.
3155        self.innerType = innerType
3156        identifier = IDLUnresolvedIdentifier(location, name)
3157        IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
3158
3159    def __str__(self):
3160        return "Typedef %s %s" % (self.identifier.name, self.innerType)
3161
3162    def finish(self, parentScope):
3163        if not self.innerType.isComplete():
3164            self.innerType = self.innerType.complete(parentScope)
3165
3166    def validate(self):
3167        pass
3168
3169    def isTypedef(self):
3170        return True
3171
3172    def addExtendedAttributes(self, attrs):
3173        if len(attrs) != 0:
3174            raise WebIDLError(
3175                "There are no extended attributes that are " "allowed on typedefs",
3176                [attrs[0].location, self.location],
3177            )
3178
3179    def _getDependentObjects(self):
3180        return self.innerType._getDependentObjects()
3181
3182
3183class IDLWrapperType(IDLType):
3184    def __init__(self, location, inner):
3185        IDLType.__init__(self, location, inner.identifier.name)
3186        self.inner = inner
3187        self._identifier = inner.identifier
3188        self.builtin = False
3189
3190    def __hash__(self):
3191        return hash(self._identifier) + hash(self.builtin)
3192
3193    def __eq__(self, other):
3194        return (
3195            isinstance(other, IDLWrapperType)
3196            and self._identifier == other._identifier
3197            and self.builtin == other.builtin
3198        )
3199
3200    def __str__(self):
3201        return str(self.name) + " (Wrapper)"
3202
3203    def isVoid(self):
3204        return False
3205
3206    def isDictionary(self):
3207        return isinstance(self.inner, IDLDictionary)
3208
3209    def isInterface(self):
3210        return isinstance(self.inner, IDLInterface) or isinstance(
3211            self.inner, IDLExternalInterface
3212        )
3213
3214    def isCallbackInterface(self):
3215        return self.isInterface() and self.inner.isCallback()
3216
3217    def isNonCallbackInterface(self):
3218        return self.isInterface() and not self.inner.isCallback()
3219
3220    def isEnum(self):
3221        return isinstance(self.inner, IDLEnum)
3222
3223    def isJSONType(self):
3224        if self.isInterface():
3225            if self.inner.isExternal():
3226                return False
3227            iface = self.inner
3228            while iface:
3229                if any(m.isMethod() and m.isToJSON() for m in iface.members):
3230                    return True
3231                iface = iface.parent
3232            return False
3233        elif self.isEnum():
3234            return True
3235        elif self.isDictionary():
3236            dictionary = self.inner
3237            while dictionary:
3238                if not all(m.type.isJSONType() for m in dictionary.members):
3239                    return False
3240                dictionary = dictionary.parent
3241            return True
3242        else:
3243            raise WebIDLError(
3244                "IDLWrapperType wraps type %s that we don't know if "
3245                "is serializable" % type(self.inner),
3246                [self.location],
3247            )
3248
3249    def resolveType(self, parentScope):
3250        assert isinstance(parentScope, IDLScope)
3251        self.inner.resolve(parentScope)
3252
3253    def isComplete(self):
3254        return True
3255
3256    def tag(self):
3257        if self.isInterface():
3258            return IDLType.Tags.interface
3259        elif self.isEnum():
3260            return IDLType.Tags.enum
3261        elif self.isDictionary():
3262            return IDLType.Tags.dictionary
3263        else:
3264            assert False
3265
3266    def isDistinguishableFrom(self, other):
3267        if other.isPromise():
3268            return False
3269        if other.isUnion():
3270            # Just forward to the union; it'll deal
3271            return other.isDistinguishableFrom(self)
3272        assert self.isInterface() or self.isEnum() or self.isDictionary()
3273        if self.isEnum():
3274            return (
3275                other.isPrimitive()
3276                or other.isInterface()
3277                or other.isObject()
3278                or other.isCallback()
3279                or other.isDictionary()
3280                or other.isSequence()
3281                or other.isRecord()
3282            )
3283        if self.isDictionary() and other.nullable():
3284            return False
3285        if (
3286            other.isPrimitive()
3287            or other.isString()
3288            or other.isEnum()
3289            or other.isSequence()
3290        ):
3291            return True
3292        if self.isDictionary():
3293            return other.isNonCallbackInterface()
3294
3295        assert self.isInterface()
3296        if other.isInterface():
3297            if other.isSpiderMonkeyInterface():
3298                # Just let |other| handle things
3299                return other.isDistinguishableFrom(self)
3300            assert self.isGeckoInterface() and other.isGeckoInterface()
3301            if self.inner.isExternal() or other.unroll().inner.isExternal():
3302                return self != other
3303            return len(
3304                self.inner.interfacesBasedOnSelf
3305                & other.unroll().inner.interfacesBasedOnSelf
3306            ) == 0 and (self.isNonCallbackInterface() or other.isNonCallbackInterface())
3307        if other.isDictionary() or other.isCallback() or other.isRecord():
3308            return self.isNonCallbackInterface()
3309
3310        # Not much else |other| can be
3311        assert other.isObject()
3312        return False
3313
3314    def isExposedInAllOf(self, exposureSet):
3315        if not self.isInterface():
3316            return True
3317        iface = self.inner
3318        if iface.isExternal():
3319            # Let's say true, so we don't have to implement exposure mixins on
3320            # external interfaces and sprinkle [Exposed=Window] on every single
3321            # external interface declaration.
3322            return True
3323        return iface.exposureSet.issuperset(exposureSet)
3324
3325    def _getDependentObjects(self):
3326        # NB: The codegen for an interface type depends on
3327        #  a) That the identifier is in fact an interface (as opposed to
3328        #     a dictionary or something else).
3329        #  b) The native type of the interface.
3330        #  If we depend on the interface object we will also depend on
3331        #  anything the interface depends on which is undesirable.  We
3332        #  considered implementing a dependency just on the interface type
3333        #  file, but then every modification to an interface would cause this
3334        #  to be regenerated which is still undesirable.  We decided not to
3335        #  depend on anything, reasoning that:
3336        #  1) Changing the concrete type of the interface requires modifying
3337        #     Bindings.conf, which is still a global dependency.
3338        #  2) Changing an interface to a dictionary (or vice versa) with the
3339        #     same identifier should be incredibly rare.
3340        #
3341        # On the other hand, if our type is a dictionary, we should
3342        # depend on it, because the member types of a dictionary
3343        # affect whether a method taking the dictionary as an argument
3344        # takes a JSContext* argument or not.
3345        if self.isDictionary():
3346            return set([self.inner])
3347        return set()
3348
3349
3350class IDLPromiseType(IDLParametrizedType):
3351    def __init__(self, location, innerType):
3352        IDLParametrizedType.__init__(self, location, "Promise", innerType)
3353
3354    def __hash__(self):
3355        return hash(self.promiseInnerType())
3356
3357    def __eq__(self, other):
3358        return (
3359            isinstance(other, IDLPromiseType)
3360            and self.promiseInnerType() == other.promiseInnerType()
3361        )
3362
3363    def __str__(self):
3364        return self.inner.__str__() + "Promise"
3365
3366    def prettyName(self):
3367        return "Promise<%s>" % self.inner.prettyName()
3368
3369    def isPromise(self):
3370        return True
3371
3372    def promiseInnerType(self):
3373        return self.inner
3374
3375    def tag(self):
3376        return IDLType.Tags.promise
3377
3378    def complete(self, scope):
3379        self.inner = self.promiseInnerType().complete(scope)
3380        return self
3381
3382    def unroll(self):
3383        # We do not unroll our inner.  Just stop at ourselves.  That
3384        # lets us add headers for both ourselves and our inner as
3385        # needed.
3386        return self
3387
3388    def isDistinguishableFrom(self, other):
3389        # Promises are not distinguishable from anything.
3390        return False
3391
3392    def isExposedInAllOf(self, exposureSet):
3393        # Check the internal type
3394        return self.promiseInnerType().unroll().isExposedInAllOf(exposureSet)
3395
3396
3397class IDLBuiltinType(IDLType):
3398
3399    Types = enum(
3400        # The integer types
3401        "byte",
3402        "octet",
3403        "short",
3404        "unsigned_short",
3405        "long",
3406        "unsigned_long",
3407        "long_long",
3408        "unsigned_long_long",
3409        # Additional primitive types
3410        "boolean",
3411        "unrestricted_float",
3412        "float",
3413        "unrestricted_double",
3414        # IMPORTANT: "double" must be the last primitive type listed
3415        "double",
3416        # Other types
3417        "any",
3418        "domstring",
3419        "bytestring",
3420        "usvstring",
3421        "utf8string",
3422        "jsstring",
3423        "object",
3424        "void",
3425        # Funny stuff
3426        "ArrayBuffer",
3427        "ArrayBufferView",
3428        "Int8Array",
3429        "Uint8Array",
3430        "Uint8ClampedArray",
3431        "Int16Array",
3432        "Uint16Array",
3433        "Int32Array",
3434        "Uint32Array",
3435        "Float32Array",
3436        "Float64Array",
3437        "ReadableStream",
3438    )
3439
3440    TagLookup = {
3441        Types.byte: IDLType.Tags.int8,
3442        Types.octet: IDLType.Tags.uint8,
3443        Types.short: IDLType.Tags.int16,
3444        Types.unsigned_short: IDLType.Tags.uint16,
3445        Types.long: IDLType.Tags.int32,
3446        Types.unsigned_long: IDLType.Tags.uint32,
3447        Types.long_long: IDLType.Tags.int64,
3448        Types.unsigned_long_long: IDLType.Tags.uint64,
3449        Types.boolean: IDLType.Tags.bool,
3450        Types.unrestricted_float: IDLType.Tags.unrestricted_float,
3451        Types.float: IDLType.Tags.float,
3452        Types.unrestricted_double: IDLType.Tags.unrestricted_double,
3453        Types.double: IDLType.Tags.double,
3454        Types.any: IDLType.Tags.any,
3455        Types.domstring: IDLType.Tags.domstring,
3456        Types.bytestring: IDLType.Tags.bytestring,
3457        Types.usvstring: IDLType.Tags.usvstring,
3458        Types.utf8string: IDLType.Tags.utf8string,
3459        Types.jsstring: IDLType.Tags.jsstring,
3460        Types.object: IDLType.Tags.object,
3461        Types.void: IDLType.Tags.void,
3462        Types.ArrayBuffer: IDLType.Tags.interface,
3463        Types.ArrayBufferView: IDLType.Tags.interface,
3464        Types.Int8Array: IDLType.Tags.interface,
3465        Types.Uint8Array: IDLType.Tags.interface,
3466        Types.Uint8ClampedArray: IDLType.Tags.interface,
3467        Types.Int16Array: IDLType.Tags.interface,
3468        Types.Uint16Array: IDLType.Tags.interface,
3469        Types.Int32Array: IDLType.Tags.interface,
3470        Types.Uint32Array: IDLType.Tags.interface,
3471        Types.Float32Array: IDLType.Tags.interface,
3472        Types.Float64Array: IDLType.Tags.interface,
3473        Types.ReadableStream: IDLType.Tags.interface,
3474    }
3475
3476    PrettyNames = {
3477        Types.byte: "byte",
3478        Types.octet: "octet",
3479        Types.short: "short",
3480        Types.unsigned_short: "unsigned short",
3481        Types.long: "long",
3482        Types.unsigned_long: "unsigned long",
3483        Types.long_long: "long long",
3484        Types.unsigned_long_long: "unsigned long long",
3485        Types.boolean: "boolean",
3486        Types.unrestricted_float: "unrestricted float",
3487        Types.float: "float",
3488        Types.unrestricted_double: "unrestricted double",
3489        Types.double: "double",
3490        Types.any: "any",
3491        Types.domstring: "DOMString",
3492        Types.bytestring: "ByteString",
3493        Types.usvstring: "USVString",
3494        Types.utf8string: "USVString",  # That's what it is in spec terms
3495        Types.jsstring: "USVString",  # Again, that's what it is in spec terms
3496        Types.object: "object",
3497        Types.void: "void",
3498        Types.ArrayBuffer: "ArrayBuffer",
3499        Types.ArrayBufferView: "ArrayBufferView",
3500        Types.Int8Array: "Int8Array",
3501        Types.Uint8Array: "Uint8Array",
3502        Types.Uint8ClampedArray: "Uint8ClampedArray",
3503        Types.Int16Array: "Int16Array",
3504        Types.Uint16Array: "Uint16Array",
3505        Types.Int32Array: "Int32Array",
3506        Types.Uint32Array: "Uint32Array",
3507        Types.Float32Array: "Float32Array",
3508        Types.Float64Array: "Float64Array",
3509        Types.ReadableStream: "ReadableStream",
3510    }
3511
3512    def __init__(
3513        self,
3514        location,
3515        name,
3516        type,
3517        clamp=False,
3518        enforceRange=False,
3519        legacyNullToEmptyString=False,
3520        allowShared=False,
3521        attrLocation=[],
3522    ):
3523        """
3524        The mutually exclusive clamp/enforceRange/legacyNullToEmptyString/allowShared arguments are used
3525        to create instances of this type with the appropriate attributes attached. Use .clamped(),
3526        .rangeEnforced(), .withLegacyNullToEmptyString() and .withAllowShared().
3527
3528        attrLocation is an array of source locations of these attributes for error reporting.
3529        """
3530        IDLType.__init__(self, location, name)
3531        self.builtin = True
3532        self._typeTag = type
3533        self._clamped = None
3534        self._rangeEnforced = None
3535        self._withLegacyNullToEmptyString = None
3536        self._withAllowShared = None
3537        if self.isInteger():
3538            if clamp:
3539                self._clamp = True
3540                self.name = "Clamped" + self.name
3541                self._extendedAttrDict["Clamp"] = True
3542            elif enforceRange:
3543                self._enforceRange = True
3544                self.name = "RangeEnforced" + self.name
3545                self._extendedAttrDict["EnforceRange"] = True
3546        elif clamp or enforceRange:
3547            raise WebIDLError(
3548                "Non-integer types cannot be [Clamp] or [EnforceRange]", attrLocation
3549            )
3550        if self.isDOMString() or self.isUTF8String():
3551            if legacyNullToEmptyString:
3552                self.legacyNullToEmptyString = True
3553                self.name = "NullIsEmpty" + self.name
3554                self._extendedAttrDict["LegacyNullToEmptyString"] = True
3555        elif legacyNullToEmptyString:
3556            raise WebIDLError(
3557                "Non-string types cannot be [LegacyNullToEmptyString]", attrLocation
3558            )
3559        if self.isBufferSource():
3560            if allowShared:
3561                self._allowShared = True
3562                self._extendedAttrDict["AllowShared"] = True
3563        elif allowShared:
3564            raise WebIDLError(
3565                "Types that are not buffer source types cannot be [AllowShared]",
3566                attrLocation,
3567            )
3568
3569    def __str__(self):
3570        if self._allowShared:
3571            assert self.isBufferSource()
3572            return "MaybeShared" + str(self.name)
3573        return str(self.name)
3574
3575    def prettyName(self):
3576        return IDLBuiltinType.PrettyNames[self._typeTag]
3577
3578    def clamped(self, attrLocation):
3579        if not self._clamped:
3580            self._clamped = IDLBuiltinType(
3581                self.location,
3582                self.name,
3583                self._typeTag,
3584                clamp=True,
3585                attrLocation=attrLocation,
3586            )
3587        return self._clamped
3588
3589    def rangeEnforced(self, attrLocation):
3590        if not self._rangeEnforced:
3591            self._rangeEnforced = IDLBuiltinType(
3592                self.location,
3593                self.name,
3594                self._typeTag,
3595                enforceRange=True,
3596                attrLocation=attrLocation,
3597            )
3598        return self._rangeEnforced
3599
3600    def withLegacyNullToEmptyString(self, attrLocation):
3601        if not self._withLegacyNullToEmptyString:
3602            self._withLegacyNullToEmptyString = IDLBuiltinType(
3603                self.location,
3604                self.name,
3605                self._typeTag,
3606                legacyNullToEmptyString=True,
3607                attrLocation=attrLocation,
3608            )
3609        return self._withLegacyNullToEmptyString
3610
3611    def withAllowShared(self, attrLocation):
3612        if not self._withAllowShared:
3613            self._withAllowShared = IDLBuiltinType(
3614                self.location,
3615                self.name,
3616                self._typeTag,
3617                allowShared=True,
3618                attrLocation=attrLocation,
3619            )
3620        return self._withAllowShared
3621
3622    def isPrimitive(self):
3623        return self._typeTag <= IDLBuiltinType.Types.double
3624
3625    def isBoolean(self):
3626        return self._typeTag == IDLBuiltinType.Types.boolean
3627
3628    def isNumeric(self):
3629        return self.isPrimitive() and not self.isBoolean()
3630
3631    def isString(self):
3632        return (
3633            self._typeTag == IDLBuiltinType.Types.domstring
3634            or self._typeTag == IDLBuiltinType.Types.bytestring
3635            or self._typeTag == IDLBuiltinType.Types.usvstring
3636            or self._typeTag == IDLBuiltinType.Types.utf8string
3637            or self._typeTag == IDLBuiltinType.Types.jsstring
3638        )
3639
3640    def isByteString(self):
3641        return self._typeTag == IDLBuiltinType.Types.bytestring
3642
3643    def isDOMString(self):
3644        return self._typeTag == IDLBuiltinType.Types.domstring
3645
3646    def isUSVString(self):
3647        return self._typeTag == IDLBuiltinType.Types.usvstring
3648
3649    def isUTF8String(self):
3650        return self._typeTag == IDLBuiltinType.Types.utf8string
3651
3652    def isJSString(self):
3653        return self._typeTag == IDLBuiltinType.Types.jsstring
3654
3655    def isInteger(self):
3656        return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long
3657
3658    def isArrayBuffer(self):
3659        return self._typeTag == IDLBuiltinType.Types.ArrayBuffer
3660
3661    def isArrayBufferView(self):
3662        return self._typeTag == IDLBuiltinType.Types.ArrayBufferView
3663
3664    def isTypedArray(self):
3665        return (
3666            self._typeTag >= IDLBuiltinType.Types.Int8Array
3667            and self._typeTag <= IDLBuiltinType.Types.Float64Array
3668        )
3669
3670    def isReadableStream(self):
3671        return self._typeTag == IDLBuiltinType.Types.ReadableStream
3672
3673    def isInterface(self):
3674        # TypedArray things are interface types per the TypedArray spec,
3675        # but we handle them as builtins because SpiderMonkey implements
3676        # all of it internally.
3677        return (
3678            self.isArrayBuffer()
3679            or self.isArrayBufferView()
3680            or self.isTypedArray()
3681            or self.isReadableStream()
3682        )
3683
3684    def isNonCallbackInterface(self):
3685        # All the interfaces we can be are non-callback
3686        return self.isInterface()
3687
3688    def isFloat(self):
3689        return (
3690            self._typeTag == IDLBuiltinType.Types.float
3691            or self._typeTag == IDLBuiltinType.Types.double
3692            or self._typeTag == IDLBuiltinType.Types.unrestricted_float
3693            or self._typeTag == IDLBuiltinType.Types.unrestricted_double
3694        )
3695
3696    def isUnrestricted(self):
3697        assert self.isFloat()
3698        return (
3699            self._typeTag == IDLBuiltinType.Types.unrestricted_float
3700            or self._typeTag == IDLBuiltinType.Types.unrestricted_double
3701        )
3702
3703    def isJSONType(self):
3704        return self.isPrimitive() or self.isString() or self.isObject()
3705
3706    def includesRestrictedFloat(self):
3707        return self.isFloat() and not self.isUnrestricted()
3708
3709    def tag(self):
3710        return IDLBuiltinType.TagLookup[self._typeTag]
3711
3712    def isDistinguishableFrom(self, other):
3713        if other.isPromise():
3714            return False
3715        if other.isUnion():
3716            # Just forward to the union; it'll deal
3717            return other.isDistinguishableFrom(self)
3718        if self.isBoolean():
3719            return (
3720                other.isNumeric()
3721                or other.isString()
3722                or other.isEnum()
3723                or other.isInterface()
3724                or other.isObject()
3725                or other.isCallback()
3726                or other.isDictionary()
3727                or other.isSequence()
3728                or other.isRecord()
3729            )
3730        if self.isNumeric():
3731            return (
3732                other.isBoolean()
3733                or other.isString()
3734                or other.isEnum()
3735                or other.isInterface()
3736                or other.isObject()
3737                or other.isCallback()
3738                or other.isDictionary()
3739                or other.isSequence()
3740                or other.isRecord()
3741            )
3742        if self.isString():
3743            return (
3744                other.isPrimitive()
3745                or other.isInterface()
3746                or other.isObject()
3747                or other.isCallback()
3748                or other.isDictionary()
3749                or other.isSequence()
3750                or other.isRecord()
3751            )
3752        if self.isAny():
3753            # Can't tell "any" apart from anything
3754            return False
3755        if self.isObject():
3756            return other.isPrimitive() or other.isString() or other.isEnum()
3757        if self.isVoid():
3758            return not other.isVoid()
3759        # Not much else we could be!
3760        assert self.isSpiderMonkeyInterface()
3761        # Like interfaces, but we know we're not a callback
3762        return (
3763            other.isPrimitive()
3764            or other.isString()
3765            or other.isEnum()
3766            or other.isCallback()
3767            or other.isDictionary()
3768            or other.isSequence()
3769            or other.isRecord()
3770            or (
3771                other.isInterface()
3772                and (
3773                    # ArrayBuffer is distinguishable from everything
3774                    # that's not an ArrayBuffer or a callback interface
3775                    (self.isArrayBuffer() and not other.isArrayBuffer())
3776                    or (self.isReadableStream() and not other.isReadableStream())
3777                    or
3778                    # ArrayBufferView is distinguishable from everything
3779                    # that's not an ArrayBufferView or typed array.
3780                    (
3781                        self.isArrayBufferView()
3782                        and not other.isArrayBufferView()
3783                        and not other.isTypedArray()
3784                    )
3785                    or
3786                    # Typed arrays are distinguishable from everything
3787                    # except ArrayBufferView and the same type of typed
3788                    # array
3789                    (
3790                        self.isTypedArray()
3791                        and not other.isArrayBufferView()
3792                        and not (other.isTypedArray() and other.name == self.name)
3793                    )
3794                )
3795            )
3796        )
3797
3798    def _getDependentObjects(self):
3799        return set()
3800
3801    def withExtendedAttributes(self, attrs):
3802        ret = self
3803        for attribute in attrs:
3804            identifier = attribute.identifier()
3805            if identifier == "Clamp":
3806                if not attribute.noArguments():
3807                    raise WebIDLError(
3808                        "[Clamp] must take no arguments", [attribute.location]
3809                    )
3810                if ret.hasEnforceRange() or self._enforceRange:
3811                    raise WebIDLError(
3812                        "[EnforceRange] and [Clamp] are mutually exclusive",
3813                        [self.location, attribute.location],
3814                    )
3815                ret = self.clamped([self.location, attribute.location])
3816            elif identifier == "EnforceRange":
3817                if not attribute.noArguments():
3818                    raise WebIDLError(
3819                        "[EnforceRange] must take no arguments", [attribute.location]
3820                    )
3821                if ret.hasClamp() or self._clamp:
3822                    raise WebIDLError(
3823                        "[EnforceRange] and [Clamp] are mutually exclusive",
3824                        [self.location, attribute.location],
3825                    )
3826                ret = self.rangeEnforced([self.location, attribute.location])
3827            elif identifier == "LegacyNullToEmptyString":
3828                if not (self.isDOMString() or self.isUTF8String()):
3829                    raise WebIDLError(
3830                        "[LegacyNullToEmptyString] only allowed on DOMStrings and UTF8Strings",
3831                        [self.location, attribute.location],
3832                    )
3833                assert not self.nullable()
3834                if attribute.hasValue():
3835                    raise WebIDLError(
3836                        "[LegacyNullToEmptyString] must take no identifier argument",
3837                        [attribute.location],
3838                    )
3839                ret = self.withLegacyNullToEmptyString(
3840                    [self.location, attribute.location]
3841                )
3842            elif identifier == "AllowShared":
3843                if not attribute.noArguments():
3844                    raise WebIDLError(
3845                        "[AllowShared] must take no arguments", [attribute.location]
3846                    )
3847                if not self.isBufferSource():
3848                    raise WebIDLError(
3849                        "[AllowShared] only allowed on buffer source types",
3850                        [self.location, attribute.location],
3851                    )
3852                ret = self.withAllowShared([self.location, attribute.location])
3853
3854            else:
3855                raise WebIDLError(
3856                    "Unhandled extended attribute on type",
3857                    [self.location, attribute.location],
3858                )
3859        return ret
3860
3861
3862BuiltinTypes = {
3863    IDLBuiltinType.Types.byte: IDLBuiltinType(
3864        BuiltinLocation("<builtin type>"), "Byte", IDLBuiltinType.Types.byte
3865    ),
3866    IDLBuiltinType.Types.octet: IDLBuiltinType(
3867        BuiltinLocation("<builtin type>"), "Octet", IDLBuiltinType.Types.octet
3868    ),
3869    IDLBuiltinType.Types.short: IDLBuiltinType(
3870        BuiltinLocation("<builtin type>"), "Short", IDLBuiltinType.Types.short
3871    ),
3872    IDLBuiltinType.Types.unsigned_short: IDLBuiltinType(
3873        BuiltinLocation("<builtin type>"),
3874        "UnsignedShort",
3875        IDLBuiltinType.Types.unsigned_short,
3876    ),
3877    IDLBuiltinType.Types.long: IDLBuiltinType(
3878        BuiltinLocation("<builtin type>"), "Long", IDLBuiltinType.Types.long
3879    ),
3880    IDLBuiltinType.Types.unsigned_long: IDLBuiltinType(
3881        BuiltinLocation("<builtin type>"),
3882        "UnsignedLong",
3883        IDLBuiltinType.Types.unsigned_long,
3884    ),
3885    IDLBuiltinType.Types.long_long: IDLBuiltinType(
3886        BuiltinLocation("<builtin type>"), "LongLong", IDLBuiltinType.Types.long_long
3887    ),
3888    IDLBuiltinType.Types.unsigned_long_long: IDLBuiltinType(
3889        BuiltinLocation("<builtin type>"),
3890        "UnsignedLongLong",
3891        IDLBuiltinType.Types.unsigned_long_long,
3892    ),
3893    IDLBuiltinType.Types.boolean: IDLBuiltinType(
3894        BuiltinLocation("<builtin type>"), "Boolean", IDLBuiltinType.Types.boolean
3895    ),
3896    IDLBuiltinType.Types.float: IDLBuiltinType(
3897        BuiltinLocation("<builtin type>"), "Float", IDLBuiltinType.Types.float
3898    ),
3899    IDLBuiltinType.Types.unrestricted_float: IDLBuiltinType(
3900        BuiltinLocation("<builtin type>"),
3901        "UnrestrictedFloat",
3902        IDLBuiltinType.Types.unrestricted_float,
3903    ),
3904    IDLBuiltinType.Types.double: IDLBuiltinType(
3905        BuiltinLocation("<builtin type>"), "Double", IDLBuiltinType.Types.double
3906    ),
3907    IDLBuiltinType.Types.unrestricted_double: IDLBuiltinType(
3908        BuiltinLocation("<builtin type>"),
3909        "UnrestrictedDouble",
3910        IDLBuiltinType.Types.unrestricted_double,
3911    ),
3912    IDLBuiltinType.Types.any: IDLBuiltinType(
3913        BuiltinLocation("<builtin type>"), "Any", IDLBuiltinType.Types.any
3914    ),
3915    IDLBuiltinType.Types.domstring: IDLBuiltinType(
3916        BuiltinLocation("<builtin type>"), "String", IDLBuiltinType.Types.domstring
3917    ),
3918    IDLBuiltinType.Types.bytestring: IDLBuiltinType(
3919        BuiltinLocation("<builtin type>"), "ByteString", IDLBuiltinType.Types.bytestring
3920    ),
3921    IDLBuiltinType.Types.usvstring: IDLBuiltinType(
3922        BuiltinLocation("<builtin type>"), "USVString", IDLBuiltinType.Types.usvstring
3923    ),
3924    IDLBuiltinType.Types.utf8string: IDLBuiltinType(
3925        BuiltinLocation("<builtin type>"), "UTF8String", IDLBuiltinType.Types.utf8string
3926    ),
3927    IDLBuiltinType.Types.jsstring: IDLBuiltinType(
3928        BuiltinLocation("<builtin type>"), "JSString", IDLBuiltinType.Types.jsstring
3929    ),
3930    IDLBuiltinType.Types.object: IDLBuiltinType(
3931        BuiltinLocation("<builtin type>"), "Object", IDLBuiltinType.Types.object
3932    ),
3933    IDLBuiltinType.Types.void: IDLBuiltinType(
3934        BuiltinLocation("<builtin type>"), "Void", IDLBuiltinType.Types.void
3935    ),
3936    IDLBuiltinType.Types.ArrayBuffer: IDLBuiltinType(
3937        BuiltinLocation("<builtin type>"),
3938        "ArrayBuffer",
3939        IDLBuiltinType.Types.ArrayBuffer,
3940    ),
3941    IDLBuiltinType.Types.ArrayBufferView: IDLBuiltinType(
3942        BuiltinLocation("<builtin type>"),
3943        "ArrayBufferView",
3944        IDLBuiltinType.Types.ArrayBufferView,
3945    ),
3946    IDLBuiltinType.Types.Int8Array: IDLBuiltinType(
3947        BuiltinLocation("<builtin type>"), "Int8Array", IDLBuiltinType.Types.Int8Array
3948    ),
3949    IDLBuiltinType.Types.Uint8Array: IDLBuiltinType(
3950        BuiltinLocation("<builtin type>"), "Uint8Array", IDLBuiltinType.Types.Uint8Array
3951    ),
3952    IDLBuiltinType.Types.Uint8ClampedArray: IDLBuiltinType(
3953        BuiltinLocation("<builtin type>"),
3954        "Uint8ClampedArray",
3955        IDLBuiltinType.Types.Uint8ClampedArray,
3956    ),
3957    IDLBuiltinType.Types.Int16Array: IDLBuiltinType(
3958        BuiltinLocation("<builtin type>"), "Int16Array", IDLBuiltinType.Types.Int16Array
3959    ),
3960    IDLBuiltinType.Types.Uint16Array: IDLBuiltinType(
3961        BuiltinLocation("<builtin type>"),
3962        "Uint16Array",
3963        IDLBuiltinType.Types.Uint16Array,
3964    ),
3965    IDLBuiltinType.Types.Int32Array: IDLBuiltinType(
3966        BuiltinLocation("<builtin type>"), "Int32Array", IDLBuiltinType.Types.Int32Array
3967    ),
3968    IDLBuiltinType.Types.Uint32Array: IDLBuiltinType(
3969        BuiltinLocation("<builtin type>"),
3970        "Uint32Array",
3971        IDLBuiltinType.Types.Uint32Array,
3972    ),
3973    IDLBuiltinType.Types.Float32Array: IDLBuiltinType(
3974        BuiltinLocation("<builtin type>"),
3975        "Float32Array",
3976        IDLBuiltinType.Types.Float32Array,
3977    ),
3978    IDLBuiltinType.Types.Float64Array: IDLBuiltinType(
3979        BuiltinLocation("<builtin type>"),
3980        "Float64Array",
3981        IDLBuiltinType.Types.Float64Array,
3982    ),
3983    IDLBuiltinType.Types.ReadableStream: IDLBuiltinType(
3984        BuiltinLocation("<builtin type>"),
3985        "ReadableStream",
3986        IDLBuiltinType.Types.ReadableStream,
3987    ),
3988}
3989
3990
3991integerTypeSizes = {
3992    IDLBuiltinType.Types.byte: (-128, 127),
3993    IDLBuiltinType.Types.octet: (0, 255),
3994    IDLBuiltinType.Types.short: (-32768, 32767),
3995    IDLBuiltinType.Types.unsigned_short: (0, 65535),
3996    IDLBuiltinType.Types.long: (-2147483648, 2147483647),
3997    IDLBuiltinType.Types.unsigned_long: (0, 4294967295),
3998    IDLBuiltinType.Types.long_long: (-9223372036854775808, 9223372036854775807),
3999    IDLBuiltinType.Types.unsigned_long_long: (0, 18446744073709551615),
4000}
4001
4002
4003def matchIntegerValueToType(value):
4004    for type, extremes in integerTypeSizes.items():
4005        (min, max) = extremes
4006        if value <= max and value >= min:
4007            return BuiltinTypes[type]
4008
4009    return None
4010
4011
4012class NoCoercionFoundError(WebIDLError):
4013    """
4014    A class we use to indicate generic coercion failures because none of the
4015    types worked out in IDLValue.coerceToType.
4016    """
4017
4018
4019class IDLValue(IDLObject):
4020    def __init__(self, location, type, value):
4021        IDLObject.__init__(self, location)
4022        self.type = type
4023        assert isinstance(type, IDLType)
4024
4025        self.value = value
4026
4027    def coerceToType(self, type, location):
4028        if type == self.type:
4029            return self  # Nothing to do
4030
4031        # We first check for unions to ensure that even if the union is nullable
4032        # we end up with the right flat member type, not the union's type.
4033        if type.isUnion():
4034            # We use the flat member types here, because if we have a nullable
4035            # member type, or a nested union, we want the type the value
4036            # actually coerces to, not the nullable or nested union type.
4037            for subtype in type.unroll().flatMemberTypes:
4038                try:
4039                    coercedValue = self.coerceToType(subtype, location)
4040                    # Create a new IDLValue to make sure that we have the
4041                    # correct float/double type.  This is necessary because we
4042                    # use the value's type when it is a default value of a
4043                    # union, and the union cares about the exact float type.
4044                    return IDLValue(self.location, subtype, coercedValue.value)
4045                except Exception as e:
4046                    # Make sure to propagate out WebIDLErrors that are not the
4047                    # generic "hey, we could not coerce to this type at all"
4048                    # exception, because those are specific "coercion failed for
4049                    # reason X" exceptions.  Note that we want to swallow
4050                    # non-WebIDLErrors here, because those can just happen if
4051                    # "type" is not something that can have a default value at
4052                    # all.
4053                    if isinstance(e, WebIDLError) and not isinstance(
4054                        e, NoCoercionFoundError
4055                    ):
4056                        raise e
4057
4058        # If the type allows null, rerun this matching on the inner type, except
4059        # nullable enums.  We handle those specially, because we want our
4060        # default string values to stay strings even when assigned to a nullable
4061        # enum.
4062        elif type.nullable() and not type.isEnum():
4063            innerValue = self.coerceToType(type.inner, location)
4064            return IDLValue(self.location, type, innerValue.value)
4065
4066        elif self.type.isInteger() and type.isInteger():
4067            # We're both integer types.  See if we fit.
4068
4069            (min, max) = integerTypeSizes[type._typeTag]
4070            if self.value <= max and self.value >= min:
4071                # Promote
4072                return IDLValue(self.location, type, self.value)
4073            else:
4074                raise WebIDLError(
4075                    "Value %s is out of range for type %s." % (self.value, type),
4076                    [location],
4077                )
4078        elif self.type.isInteger() and type.isFloat():
4079            # Convert an integer literal into float
4080            if -(2 ** 24) <= self.value <= 2 ** 24:
4081                return IDLValue(self.location, type, float(self.value))
4082            else:
4083                raise WebIDLError(
4084                    "Converting value %s to %s will lose precision."
4085                    % (self.value, type),
4086                    [location],
4087                )
4088        elif self.type.isString() and type.isEnum():
4089            # Just keep our string, but make sure it's a valid value for this enum
4090            enum = type.unroll().inner
4091            if self.value not in enum.values():
4092                raise WebIDLError(
4093                    "'%s' is not a valid default value for enum %s"
4094                    % (self.value, enum.identifier.name),
4095                    [location, enum.location],
4096                )
4097            return self
4098        elif self.type.isFloat() and type.isFloat():
4099            if not type.isUnrestricted() and (
4100                self.value == float("inf")
4101                or self.value == float("-inf")
4102                or math.isnan(self.value)
4103            ):
4104                raise WebIDLError(
4105                    "Trying to convert unrestricted value %s to non-unrestricted"
4106                    % self.value,
4107                    [location],
4108                )
4109            return IDLValue(self.location, type, self.value)
4110        elif self.type.isString() and type.isUSVString():
4111            # Allow USVStrings to use default value just like
4112            # DOMString.  No coercion is required in this case as Codegen.py
4113            # treats USVString just like DOMString, but with an
4114            # extra normalization step.
4115            assert self.type.isDOMString()
4116            return self
4117        elif self.type.isString() and (
4118            type.isByteString() or type.isJSString() or type.isUTF8String()
4119        ):
4120            # Allow ByteStrings, UTF8String, and JSStrings to use a default
4121            # value like DOMString.
4122            # No coercion is required as Codegen.py will handle the
4123            # extra steps. We want to make sure that our string contains
4124            # only valid characters, so we check that here.
4125            valid_ascii_lit = (
4126                " " + string.ascii_letters + string.digits + string.punctuation
4127            )
4128            for idx, c in enumerate(self.value):
4129                if c not in valid_ascii_lit:
4130                    raise WebIDLError(
4131                        "Coercing this string literal %s to a ByteString is not supported yet. "
4132                        "Coercion failed due to an unsupported byte %d at index %d."
4133                        % (self.value.__repr__(), ord(c), idx),
4134                        [location],
4135                    )
4136
4137            return IDLValue(self.location, type, self.value)
4138        elif self.type.isDOMString() and type.legacyNullToEmptyString:
4139            # LegacyNullToEmptyString is a different type for resolution reasons,
4140            # however once you have a value it doesn't matter
4141            return self
4142
4143        raise NoCoercionFoundError(
4144            "Cannot coerce type %s to type %s." % (self.type, type), [location]
4145        )
4146
4147    def _getDependentObjects(self):
4148        return set()
4149
4150
4151class IDLNullValue(IDLObject):
4152    def __init__(self, location):
4153        IDLObject.__init__(self, location)
4154        self.type = None
4155        self.value = None
4156
4157    def coerceToType(self, type, location):
4158        if (
4159            not isinstance(type, IDLNullableType)
4160            and not (type.isUnion() and type.hasNullableType)
4161            and not type.isAny()
4162        ):
4163            raise WebIDLError("Cannot coerce null value to type %s." % type, [location])
4164
4165        nullValue = IDLNullValue(self.location)
4166        if type.isUnion() and not type.nullable() and type.hasDictionaryType():
4167            # We're actually a default value for the union's dictionary member.
4168            # Use its type.
4169            for t in type.flatMemberTypes:
4170                if t.isDictionary():
4171                    nullValue.type = t
4172                    return nullValue
4173        nullValue.type = type
4174        return nullValue
4175
4176    def _getDependentObjects(self):
4177        return set()
4178
4179
4180class IDLEmptySequenceValue(IDLObject):
4181    def __init__(self, location):
4182        IDLObject.__init__(self, location)
4183        self.type = None
4184        self.value = None
4185
4186    def coerceToType(self, type, location):
4187        if type.isUnion():
4188            # We use the flat member types here, because if we have a nullable
4189            # member type, or a nested union, we want the type the value
4190            # actually coerces to, not the nullable or nested union type.
4191            for subtype in type.unroll().flatMemberTypes:
4192                try:
4193                    return self.coerceToType(subtype, location)
4194                except:
4195                    pass
4196
4197        if not type.isSequence():
4198            raise WebIDLError(
4199                "Cannot coerce empty sequence value to type %s." % type, [location]
4200            )
4201
4202        emptySequenceValue = IDLEmptySequenceValue(self.location)
4203        emptySequenceValue.type = type
4204        return emptySequenceValue
4205
4206    def _getDependentObjects(self):
4207        return set()
4208
4209
4210class IDLDefaultDictionaryValue(IDLObject):
4211    def __init__(self, location):
4212        IDLObject.__init__(self, location)
4213        self.type = None
4214        self.value = None
4215
4216    def coerceToType(self, type, location):
4217        if type.isUnion():
4218            # We use the flat member types here, because if we have a nullable
4219            # member type, or a nested union, we want the type the value
4220            # actually coerces to, not the nullable or nested union type.
4221            for subtype in type.unroll().flatMemberTypes:
4222                try:
4223                    return self.coerceToType(subtype, location)
4224                except:
4225                    pass
4226
4227        if not type.isDictionary():
4228            raise WebIDLError(
4229                "Cannot coerce default dictionary value to type %s." % type, [location]
4230            )
4231
4232        defaultDictionaryValue = IDLDefaultDictionaryValue(self.location)
4233        defaultDictionaryValue.type = type
4234        return defaultDictionaryValue
4235
4236    def _getDependentObjects(self):
4237        return set()
4238
4239
4240class IDLUndefinedValue(IDLObject):
4241    def __init__(self, location):
4242        IDLObject.__init__(self, location)
4243        self.type = None
4244        self.value = None
4245
4246    def coerceToType(self, type, location):
4247        if not type.isAny():
4248            raise WebIDLError(
4249                "Cannot coerce undefined value to type %s." % type, [location]
4250            )
4251
4252        undefinedValue = IDLUndefinedValue(self.location)
4253        undefinedValue.type = type
4254        return undefinedValue
4255
4256    def _getDependentObjects(self):
4257        return set()
4258
4259
4260class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
4261
4262    Tags = enum("Const", "Attr", "Method", "MaplikeOrSetlike", "Iterable")
4263
4264    Special = enum("Static", "Stringifier")
4265
4266    AffectsValues = ("Nothing", "Everything")
4267    DependsOnValues = ("Nothing", "DOMState", "DeviceState", "Everything")
4268
4269    def __init__(self, location, identifier, tag, extendedAttrDict=None):
4270        IDLObjectWithIdentifier.__init__(self, location, None, identifier)
4271        IDLExposureMixins.__init__(self, location)
4272        self.tag = tag
4273        if extendedAttrDict is None:
4274            self._extendedAttrDict = {}
4275        else:
4276            self._extendedAttrDict = extendedAttrDict
4277
4278    def isMethod(self):
4279        return self.tag == IDLInterfaceMember.Tags.Method
4280
4281    def isAttr(self):
4282        return self.tag == IDLInterfaceMember.Tags.Attr
4283
4284    def isConst(self):
4285        return self.tag == IDLInterfaceMember.Tags.Const
4286
4287    def isMaplikeOrSetlikeOrIterable(self):
4288        return (
4289            self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike
4290            or self.tag == IDLInterfaceMember.Tags.Iterable
4291        )
4292
4293    def isMaplikeOrSetlike(self):
4294        return self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike
4295
4296    def addExtendedAttributes(self, attrs):
4297        for attr in attrs:
4298            self.handleExtendedAttribute(attr)
4299            attrlist = attr.listValue()
4300            self._extendedAttrDict[attr.identifier()] = (
4301                attrlist if len(attrlist) else True
4302            )
4303
4304    def handleExtendedAttribute(self, attr):
4305        pass
4306
4307    def getExtendedAttribute(self, name):
4308        return self._extendedAttrDict.get(name, None)
4309
4310    def finish(self, scope):
4311        IDLExposureMixins.finish(self, scope)
4312
4313    def validate(self):
4314        if self.isAttr() or self.isMethod():
4315            if self.affects == "Everything" and self.dependsOn != "Everything":
4316                raise WebIDLError(
4317                    "Interface member is flagged as affecting "
4318                    "everything but not depending on everything. "
4319                    "That seems rather unlikely.",
4320                    [self.location],
4321                )
4322
4323        if self.getExtendedAttribute("NewObject"):
4324            if self.dependsOn == "Nothing" or self.dependsOn == "DOMState":
4325                raise WebIDLError(
4326                    "A [NewObject] method is not idempotent, "
4327                    "so it has to depend on something other than DOM state.",
4328                    [self.location],
4329                )
4330            if self.getExtendedAttribute("Cached") or self.getExtendedAttribute(
4331                "StoreInSlot"
4332            ):
4333                raise WebIDLError(
4334                    "A [NewObject] attribute shouldnt be "
4335                    "[Cached] or [StoreInSlot], since the point "
4336                    "of those is to keep returning the same "
4337                    "thing across multiple calls, which is not "
4338                    "what [NewObject] does.",
4339                    [self.location],
4340                )
4341
4342    def _setDependsOn(self, dependsOn):
4343        if self.dependsOn != "Everything":
4344            raise WebIDLError(
4345                "Trying to specify multiple different DependsOn, "
4346                "Pure, or Constant extended attributes for "
4347                "attribute",
4348                [self.location],
4349            )
4350        if dependsOn not in IDLInterfaceMember.DependsOnValues:
4351            raise WebIDLError(
4352                "Invalid [DependsOn=%s] on attribute" % dependsOn, [self.location]
4353            )
4354        self.dependsOn = dependsOn
4355
4356    def _setAffects(self, affects):
4357        if self.affects != "Everything":
4358            raise WebIDLError(
4359                "Trying to specify multiple different Affects, "
4360                "Pure, or Constant extended attributes for "
4361                "attribute",
4362                [self.location],
4363            )
4364        if affects not in IDLInterfaceMember.AffectsValues:
4365            raise WebIDLError(
4366                "Invalid [Affects=%s] on attribute" % dependsOn, [self.location]
4367            )
4368        self.affects = affects
4369
4370    def _addAlias(self, alias):
4371        if alias in self.aliases:
4372            raise WebIDLError(
4373                "Duplicate [Alias=%s] on attribute" % alias, [self.location]
4374            )
4375        self.aliases.append(alias)
4376
4377    def _addBindingAlias(self, bindingAlias):
4378        if bindingAlias in self.bindingAliases:
4379            raise WebIDLError(
4380                "Duplicate [BindingAlias=%s] on attribute" % bindingAlias,
4381                [self.location],
4382            )
4383        self.bindingAliases.append(bindingAlias)
4384
4385
4386class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
4387    def __init__(self, location, identifier, ifaceType, keyType, valueType, ifaceKind):
4388        IDLInterfaceMember.__init__(self, location, identifier, ifaceKind)
4389        if keyType is not None:
4390            assert isinstance(keyType, IDLType)
4391        else:
4392            assert valueType is not None
4393        assert ifaceType in ["maplike", "setlike", "iterable"]
4394        if valueType is not None:
4395            assert isinstance(valueType, IDLType)
4396        self.keyType = keyType
4397        self.valueType = valueType
4398        self.maplikeOrSetlikeOrIterableType = ifaceType
4399        self.disallowedMemberNames = []
4400        self.disallowedNonMethodNames = []
4401
4402    def isMaplike(self):
4403        return self.maplikeOrSetlikeOrIterableType == "maplike"
4404
4405    def isSetlike(self):
4406        return self.maplikeOrSetlikeOrIterableType == "setlike"
4407
4408    def isIterable(self):
4409        return self.maplikeOrSetlikeOrIterableType == "iterable"
4410
4411    def hasKeyType(self):
4412        return self.keyType is not None
4413
4414    def hasValueType(self):
4415        return self.valueType is not None
4416
4417    def checkCollisions(self, members, isAncestor):
4418        for member in members:
4419            # Check that there are no disallowed members
4420            if member.identifier.name in self.disallowedMemberNames and not (
4421                (member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod())
4422                or (member.isAttr() and member.isMaplikeOrSetlikeAttr())
4423            ):
4424                raise WebIDLError(
4425                    "Member '%s' conflicts "
4426                    "with reserved %s name."
4427                    % (member.identifier.name, self.maplikeOrSetlikeOrIterableType),
4428                    [self.location, member.location],
4429                )
4430            # Check that there are no disallowed non-method members.
4431            # Ancestor members are always disallowed here; own members
4432            # are disallowed only if they're non-methods.
4433            if (
4434                isAncestor or member.isAttr() or member.isConst()
4435            ) and member.identifier.name in self.disallowedNonMethodNames:
4436                raise WebIDLError(
4437                    "Member '%s' conflicts "
4438                    "with reserved %s method."
4439                    % (member.identifier.name, self.maplikeOrSetlikeOrIterableType),
4440                    [self.location, member.location],
4441                )
4442
4443    def addMethod(
4444        self,
4445        name,
4446        members,
4447        allowExistingOperations,
4448        returnType,
4449        args=[],
4450        chromeOnly=False,
4451        isPure=False,
4452        affectsNothing=False,
4453        newObject=False,
4454        isIteratorAlias=False,
4455    ):
4456        """
4457        Create an IDLMethod based on the parameters passed in.
4458
4459        - members is the member list to add this function to, since this is
4460          called during the member expansion portion of interface object
4461          building.
4462
4463        - chromeOnly is only True for read-only js implemented classes, to
4464        implement underscore prefixed convenience functions which would
4465        otherwise not be available, unlike the case of C++ bindings.
4466
4467        - isPure is only True for idempotent functions, so it is not valid for
4468        things like keys, values, etc. that return a new object every time.
4469
4470        - affectsNothing means that nothing changes due to this method, which
4471          affects JIT optimization behavior
4472
4473        - newObject means the method creates and returns a new object.
4474
4475        """
4476        # Only add name to lists for collision checks if it's not chrome
4477        # only.
4478        if chromeOnly:
4479            name = "__" + name
4480        else:
4481            if not allowExistingOperations:
4482                self.disallowedMemberNames.append(name)
4483            else:
4484                self.disallowedNonMethodNames.append(name)
4485        # If allowExistingOperations is True, and another operation exists
4486        # with the same name as the one we're trying to add, don't add the
4487        # maplike/setlike operation. However, if the operation is static,
4488        # then fail by way of creating the function, which will cause a
4489        # naming conflict, per the spec.
4490        if allowExistingOperations:
4491            for m in members:
4492                if m.identifier.name == name and m.isMethod() and not m.isStatic():
4493                    return
4494        method = IDLMethod(
4495            self.location,
4496            IDLUnresolvedIdentifier(
4497                self.location, name, allowDoubleUnderscore=chromeOnly
4498            ),
4499            returnType,
4500            args,
4501            maplikeOrSetlikeOrIterable=self,
4502        )
4503        # We need to be able to throw from declaration methods
4504        method.addExtendedAttributes([IDLExtendedAttribute(self.location, ("Throws",))])
4505        if chromeOnly:
4506            method.addExtendedAttributes(
4507                [IDLExtendedAttribute(self.location, ("ChromeOnly",))]
4508            )
4509        if isPure:
4510            method.addExtendedAttributes(
4511                [IDLExtendedAttribute(self.location, ("Pure",))]
4512            )
4513        # Following attributes are used for keys/values/entries. Can't mark
4514        # them pure, since they return a new object each time they are run.
4515        if affectsNothing:
4516            method.addExtendedAttributes(
4517                [
4518                    IDLExtendedAttribute(self.location, ("DependsOn", "Everything")),
4519                    IDLExtendedAttribute(self.location, ("Affects", "Nothing")),
4520                ]
4521            )
4522        if newObject:
4523            method.addExtendedAttributes(
4524                [IDLExtendedAttribute(self.location, ("NewObject",))]
4525            )
4526        if isIteratorAlias:
4527            method.addExtendedAttributes(
4528                [IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))]
4529            )
4530        # Methods generated for iterables should be enumerable, but the ones for
4531        # maplike/setlike should not be.
4532        if not self.isIterable():
4533            method.addExtendedAttributes(
4534                [IDLExtendedAttribute(self.location, ("NonEnumerable",))]
4535            )
4536        members.append(method)
4537
4538    def resolve(self, parentScope):
4539        if self.keyType:
4540            self.keyType.resolveType(parentScope)
4541        if self.valueType:
4542            self.valueType.resolveType(parentScope)
4543
4544    def finish(self, scope):
4545        IDLInterfaceMember.finish(self, scope)
4546        if self.keyType and not self.keyType.isComplete():
4547            t = self.keyType.complete(scope)
4548
4549            assert not isinstance(t, IDLUnresolvedType)
4550            assert not isinstance(t, IDLTypedefType)
4551            assert not isinstance(t.name, IDLUnresolvedIdentifier)
4552            self.keyType = t
4553        if self.valueType and not self.valueType.isComplete():
4554            t = self.valueType.complete(scope)
4555
4556            assert not isinstance(t, IDLUnresolvedType)
4557            assert not isinstance(t, IDLTypedefType)
4558            assert not isinstance(t.name, IDLUnresolvedIdentifier)
4559            self.valueType = t
4560
4561    def validate(self):
4562        IDLInterfaceMember.validate(self)
4563
4564    def handleExtendedAttribute(self, attr):
4565        IDLInterfaceMember.handleExtendedAttribute(self, attr)
4566
4567    def _getDependentObjects(self):
4568        deps = set()
4569        if self.keyType:
4570            deps.add(self.keyType)
4571        if self.valueType:
4572            deps.add(self.valueType)
4573        return deps
4574
4575    def getForEachArguments(self):
4576        return [
4577            IDLArgument(
4578                self.location,
4579                IDLUnresolvedIdentifier(
4580                    BuiltinLocation("<auto-generated-identifier>"), "callback"
4581                ),
4582                BuiltinTypes[IDLBuiltinType.Types.object],
4583            ),
4584            IDLArgument(
4585                self.location,
4586                IDLUnresolvedIdentifier(
4587                    BuiltinLocation("<auto-generated-identifier>"), "thisArg"
4588                ),
4589                BuiltinTypes[IDLBuiltinType.Types.any],
4590                optional=True,
4591            ),
4592        ]
4593
4594
4595# Iterable adds ES6 iterator style functions and traits
4596# (keys/values/entries/@@iterator) to an interface.
4597class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase):
4598    def __init__(self, location, identifier, keyType, valueType=None, scope=None):
4599        IDLMaplikeOrSetlikeOrIterableBase.__init__(
4600            self,
4601            location,
4602            identifier,
4603            "iterable",
4604            keyType,
4605            valueType,
4606            IDLInterfaceMember.Tags.Iterable,
4607        )
4608        self.iteratorType = None
4609
4610    def __str__(self):
4611        return "declared iterable with key '%s' and value '%s'" % (
4612            self.keyType,
4613            self.valueType,
4614        )
4615
4616    def expand(self, members):
4617        """
4618        In order to take advantage of all of the method machinery in Codegen,
4619        we generate our functions as if they were part of the interface
4620        specification during parsing.
4621        """
4622        # We only need to add entries/keys/values here if we're a pair iterator.
4623        # Value iterators just copy these from %ArrayPrototype% instead.
4624        if not self.isPairIterator():
4625            return
4626
4627        # object entries()
4628        self.addMethod(
4629            "entries",
4630            members,
4631            False,
4632            self.iteratorType,
4633            affectsNothing=True,
4634            newObject=True,
4635            isIteratorAlias=True,
4636        )
4637        # object keys()
4638        self.addMethod(
4639            "keys",
4640            members,
4641            False,
4642            self.iteratorType,
4643            affectsNothing=True,
4644            newObject=True,
4645        )
4646        # object values()
4647        self.addMethod(
4648            "values",
4649            members,
4650            False,
4651            self.iteratorType,
4652            affectsNothing=True,
4653            newObject=True,
4654        )
4655
4656        # void forEach(callback(valueType, keyType), optional any thisArg)
4657        self.addMethod(
4658            "forEach",
4659            members,
4660            False,
4661            BuiltinTypes[IDLBuiltinType.Types.void],
4662            self.getForEachArguments(),
4663        )
4664
4665    def isValueIterator(self):
4666        return not self.isPairIterator()
4667
4668    def isPairIterator(self):
4669        return self.hasKeyType()
4670
4671
4672# MaplikeOrSetlike adds ES6 map-or-set-like traits to an interface.
4673class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
4674    def __init__(
4675        self, location, identifier, maplikeOrSetlikeType, readonly, keyType, valueType
4676    ):
4677        IDLMaplikeOrSetlikeOrIterableBase.__init__(
4678            self,
4679            location,
4680            identifier,
4681            maplikeOrSetlikeType,
4682            keyType,
4683            valueType,
4684            IDLInterfaceMember.Tags.MaplikeOrSetlike,
4685        )
4686        self.readonly = readonly
4687        self.slotIndices = None
4688
4689        # When generating JSAPI access code, we need to know the backing object
4690        # type prefix to create the correct function. Generate here for reuse.
4691        if self.isMaplike():
4692            self.prefix = "Map"
4693        elif self.isSetlike():
4694            self.prefix = "Set"
4695
4696    def __str__(self):
4697        return "declared '%s' with key '%s'" % (
4698            self.maplikeOrSetlikeOrIterableType,
4699            self.keyType,
4700        )
4701
4702    def expand(self, members):
4703        """
4704        In order to take advantage of all of the method machinery in Codegen,
4705        we generate our functions as if they were part of the interface
4706        specification during parsing.
4707        """
4708        # Both maplike and setlike have a size attribute
4709        sizeAttr = IDLAttribute(
4710            self.location,
4711            IDLUnresolvedIdentifier(
4712                BuiltinLocation("<auto-generated-identifier>"), "size"
4713            ),
4714            BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
4715            True,
4716            maplikeOrSetlike=self,
4717        )
4718        # This should be non-enumerable.
4719        sizeAttr.addExtendedAttributes(
4720            [IDLExtendedAttribute(self.location, ("NonEnumerable",))]
4721        )
4722        members.append(sizeAttr)
4723        self.reserved_ro_names = ["size"]
4724        self.disallowedMemberNames.append("size")
4725
4726        # object entries()
4727        self.addMethod(
4728            "entries",
4729            members,
4730            False,
4731            BuiltinTypes[IDLBuiltinType.Types.object],
4732            affectsNothing=True,
4733            isIteratorAlias=self.isMaplike(),
4734        )
4735        # object keys()
4736        self.addMethod(
4737            "keys",
4738            members,
4739            False,
4740            BuiltinTypes[IDLBuiltinType.Types.object],
4741            affectsNothing=True,
4742        )
4743        # object values()
4744        self.addMethod(
4745            "values",
4746            members,
4747            False,
4748            BuiltinTypes[IDLBuiltinType.Types.object],
4749            affectsNothing=True,
4750            isIteratorAlias=self.isSetlike(),
4751        )
4752
4753        # void forEach(callback(valueType, keyType), thisVal)
4754        self.addMethod(
4755            "forEach",
4756            members,
4757            False,
4758            BuiltinTypes[IDLBuiltinType.Types.void],
4759            self.getForEachArguments(),
4760        )
4761
4762        def getKeyArg():
4763            return IDLArgument(
4764                self.location,
4765                IDLUnresolvedIdentifier(self.location, "key"),
4766                self.keyType,
4767            )
4768
4769        # boolean has(keyType key)
4770        self.addMethod(
4771            "has",
4772            members,
4773            False,
4774            BuiltinTypes[IDLBuiltinType.Types.boolean],
4775            [getKeyArg()],
4776            isPure=True,
4777        )
4778
4779        if not self.readonly:
4780            # void clear()
4781            self.addMethod(
4782                "clear", members, True, BuiltinTypes[IDLBuiltinType.Types.void], []
4783            )
4784            # boolean delete(keyType key)
4785            self.addMethod(
4786                "delete",
4787                members,
4788                True,
4789                BuiltinTypes[IDLBuiltinType.Types.boolean],
4790                [getKeyArg()],
4791            )
4792
4793        if self.isSetlike():
4794            if not self.readonly:
4795                # Add returns the set object it just added to.
4796                # object add(keyType key)
4797
4798                self.addMethod(
4799                    "add",
4800                    members,
4801                    True,
4802                    BuiltinTypes[IDLBuiltinType.Types.object],
4803                    [getKeyArg()],
4804                )
4805            return
4806
4807        # If we get this far, we're a maplike declaration.
4808
4809        # valueType get(keyType key)
4810        #
4811        # Note that instead of the value type, we're using any here. The
4812        # validity checks should happen as things are inserted into the map,
4813        # and using any as the return type makes code generation much simpler.
4814        #
4815        # TODO: Bug 1155340 may change this to use specific type to provide
4816        # more info to JIT.
4817        self.addMethod(
4818            "get",
4819            members,
4820            False,
4821            BuiltinTypes[IDLBuiltinType.Types.any],
4822            [getKeyArg()],
4823            isPure=True,
4824        )
4825
4826        def getValueArg():
4827            return IDLArgument(
4828                self.location,
4829                IDLUnresolvedIdentifier(self.location, "value"),
4830                self.valueType,
4831            )
4832
4833        if not self.readonly:
4834            self.addMethod(
4835                "set",
4836                members,
4837                True,
4838                BuiltinTypes[IDLBuiltinType.Types.object],
4839                [getKeyArg(), getValueArg()],
4840            )
4841
4842
4843class IDLConst(IDLInterfaceMember):
4844    def __init__(self, location, identifier, type, value):
4845        IDLInterfaceMember.__init__(
4846            self, location, identifier, IDLInterfaceMember.Tags.Const
4847        )
4848
4849        assert isinstance(type, IDLType)
4850        if type.isDictionary():
4851            raise WebIDLError(
4852                "A constant cannot be of a dictionary type", [self.location]
4853            )
4854        if type.isRecord():
4855            raise WebIDLError("A constant cannot be of a record type", [self.location])
4856        self.type = type
4857        self.value = value
4858
4859        if identifier.name == "prototype":
4860            raise WebIDLError(
4861                "The identifier of a constant must not be 'prototype'", [location]
4862            )
4863
4864    def __str__(self):
4865        return "'%s' const '%s'" % (self.type, self.identifier)
4866
4867    def finish(self, scope):
4868        IDLInterfaceMember.finish(self, scope)
4869
4870        if not self.type.isComplete():
4871            type = self.type.complete(scope)
4872            if not type.isPrimitive() and not type.isString():
4873                locations = [self.type.location, type.location]
4874                try:
4875                    locations.append(type.inner.location)
4876                except:
4877                    pass
4878                raise WebIDLError("Incorrect type for constant", locations)
4879            self.type = type
4880
4881        # The value might not match the type
4882        coercedValue = self.value.coerceToType(self.type, self.location)
4883        assert coercedValue
4884
4885        self.value = coercedValue
4886
4887    def validate(self):
4888        IDLInterfaceMember.validate(self)
4889
4890    def handleExtendedAttribute(self, attr):
4891        identifier = attr.identifier()
4892        if identifier == "Exposed":
4893            convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
4894        elif (
4895            identifier == "Pref"
4896            or identifier == "ChromeOnly"
4897            or identifier == "Func"
4898            or identifier == "SecureContext"
4899            or identifier == "NonEnumerable"
4900        ):
4901            # Known attributes that we don't need to do anything with here
4902            pass
4903        else:
4904            raise WebIDLError(
4905                "Unknown extended attribute %s on constant" % identifier,
4906                [attr.location],
4907            )
4908        IDLInterfaceMember.handleExtendedAttribute(self, attr)
4909
4910    def _getDependentObjects(self):
4911        return set([self.type, self.value])
4912
4913
4914class IDLAttribute(IDLInterfaceMember):
4915    def __init__(
4916        self,
4917        location,
4918        identifier,
4919        type,
4920        readonly,
4921        inherit=False,
4922        static=False,
4923        stringifier=False,
4924        maplikeOrSetlike=None,
4925        extendedAttrDict=None,
4926    ):
4927        IDLInterfaceMember.__init__(
4928            self,
4929            location,
4930            identifier,
4931            IDLInterfaceMember.Tags.Attr,
4932            extendedAttrDict=extendedAttrDict,
4933        )
4934
4935        assert isinstance(type, IDLType)
4936        self.type = type
4937        self.readonly = readonly
4938        self.inherit = inherit
4939        self._static = static
4940        self.legacyLenientThis = False
4941        self._legacyUnforgeable = False
4942        self.stringifier = stringifier
4943        self.slotIndices = None
4944        assert maplikeOrSetlike is None or isinstance(
4945            maplikeOrSetlike, IDLMaplikeOrSetlike
4946        )
4947        self.maplikeOrSetlike = maplikeOrSetlike
4948        self.dependsOn = "Everything"
4949        self.affects = "Everything"
4950        self.bindingAliases = []
4951
4952        if static and identifier.name == "prototype":
4953            raise WebIDLError(
4954                "The identifier of a static attribute must not be 'prototype'",
4955                [location],
4956            )
4957
4958        if readonly and inherit:
4959            raise WebIDLError(
4960                "An attribute cannot be both 'readonly' and 'inherit'", [self.location]
4961            )
4962
4963    def isStatic(self):
4964        return self._static
4965
4966    def forceStatic(self):
4967        self._static = True
4968
4969    def __str__(self):
4970        return "'%s' attribute '%s'" % (self.type, self.identifier)
4971
4972    def finish(self, scope):
4973        IDLInterfaceMember.finish(self, scope)
4974
4975        if not self.type.isComplete():
4976            t = self.type.complete(scope)
4977
4978            assert not isinstance(t, IDLUnresolvedType)
4979            assert not isinstance(t, IDLTypedefType)
4980            assert not isinstance(t.name, IDLUnresolvedIdentifier)
4981            self.type = t
4982
4983        if self.readonly and (
4984            self.type.hasClamp()
4985            or self.type.hasEnforceRange()
4986            or self.type.hasAllowShared()
4987            or self.type.legacyNullToEmptyString
4988        ):
4989            raise WebIDLError(
4990                "A readonly attribute cannot be [Clamp] or [EnforceRange] or [AllowShared]",
4991                [self.location],
4992            )
4993        if self.type.isDictionary() and not self.getExtendedAttribute("Cached"):
4994            raise WebIDLError(
4995                "An attribute cannot be of a dictionary type", [self.location]
4996            )
4997        if self.type.isSequence() and not self.getExtendedAttribute("Cached"):
4998            raise WebIDLError(
4999                "A non-cached attribute cannot be of a sequence " "type",
5000                [self.location],
5001            )
5002        if self.type.isRecord() and not self.getExtendedAttribute("Cached"):
5003            raise WebIDLError(
5004                "A non-cached attribute cannot be of a record " "type", [self.location]
5005            )
5006        if self.type.isUnion():
5007            for f in self.type.unroll().flatMemberTypes:
5008                if f.isDictionary():
5009                    raise WebIDLError(
5010                        "An attribute cannot be of a union "
5011                        "type if one of its member types (or "
5012                        "one of its member types's member "
5013                        "types, and so on) is a dictionary "
5014                        "type",
5015                        [self.location, f.location],
5016                    )
5017                if f.isSequence():
5018                    raise WebIDLError(
5019                        "An attribute cannot be of a union "
5020                        "type if one of its member types (or "
5021                        "one of its member types's member "
5022                        "types, and so on) is a sequence "
5023                        "type",
5024                        [self.location, f.location],
5025                    )
5026                if f.isRecord():
5027                    raise WebIDLError(
5028                        "An attribute cannot be of a union "
5029                        "type if one of its member types (or "
5030                        "one of its member types's member "
5031                        "types, and so on) is a record "
5032                        "type",
5033                        [self.location, f.location],
5034                    )
5035        if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"):
5036            raise WebIDLError(
5037                "An attribute with [PutForwards] must have an "
5038                "interface type as its type",
5039                [self.location],
5040            )
5041
5042        if not self.type.isInterface() and self.getExtendedAttribute("SameObject"):
5043            raise WebIDLError(
5044                "An attribute with [SameObject] must have an "
5045                "interface type as its type",
5046                [self.location],
5047            )
5048
5049        if self.type.isPromise() and not self.readonly:
5050            raise WebIDLError(
5051                "Promise-returning attributes must be readonly", [self.location]
5052            )
5053
5054    def validate(self):
5055        def typeContainsChromeOnlyDictionaryMember(type):
5056            if type.nullable() or type.isSequence() or type.isRecord():
5057                return typeContainsChromeOnlyDictionaryMember(type.inner)
5058
5059            if type.isUnion():
5060                for memberType in type.flatMemberTypes:
5061                    (contains, location) = typeContainsChromeOnlyDictionaryMember(
5062                        memberType
5063                    )
5064                    if contains:
5065                        return (True, location)
5066
5067            if type.isDictionary():
5068                dictionary = type.inner
5069                while dictionary:
5070                    (contains, location) = dictionaryContainsChromeOnlyMember(
5071                        dictionary
5072                    )
5073                    if contains:
5074                        return (True, location)
5075                    dictionary = dictionary.parent
5076
5077            return (False, None)
5078
5079        def dictionaryContainsChromeOnlyMember(dictionary):
5080            for member in dictionary.members:
5081                if member.getExtendedAttribute("ChromeOnly"):
5082                    return (True, member.location)
5083                (contains, location) = typeContainsChromeOnlyDictionaryMember(
5084                    member.type
5085                )
5086                if contains:
5087                    return (True, location)
5088            return (False, None)
5089
5090        IDLInterfaceMember.validate(self)
5091
5092        if self.getExtendedAttribute("Cached") or self.getExtendedAttribute(
5093            "StoreInSlot"
5094        ):
5095            if not self.affects == "Nothing":
5096                raise WebIDLError(
5097                    "Cached attributes and attributes stored in "
5098                    "slots must be Constant or Pure or "
5099                    "Affects=Nothing, since the getter won't always "
5100                    "be called.",
5101                    [self.location],
5102                )
5103            (contains, location) = typeContainsChromeOnlyDictionaryMember(self.type)
5104            if contains:
5105                raise WebIDLError(
5106                    "[Cached] and [StoreInSlot] must not be used "
5107                    "on an attribute whose type contains a "
5108                    "[ChromeOnly] dictionary member",
5109                    [self.location, location],
5110                )
5111        if self.getExtendedAttribute("Frozen"):
5112            if (
5113                not self.type.isSequence()
5114                and not self.type.isDictionary()
5115                and not self.type.isRecord()
5116            ):
5117                raise WebIDLError(
5118                    "[Frozen] is only allowed on "
5119                    "sequence-valued, dictionary-valued, and "
5120                    "record-valued attributes",
5121                    [self.location],
5122                )
5123        if not self.type.unroll().isExposedInAllOf(self.exposureSet):
5124            raise WebIDLError(
5125                "Attribute returns a type that is not exposed "
5126                "everywhere where the attribute is exposed",
5127                [self.location],
5128            )
5129        if self.getExtendedAttribute("CEReactions"):
5130            if self.readonly:
5131                raise WebIDLError(
5132                    "[CEReactions] is not allowed on " "readonly attributes",
5133                    [self.location],
5134                )
5135
5136    def handleExtendedAttribute(self, attr):
5137        identifier = attr.identifier()
5138        if (
5139            identifier == "SetterThrows"
5140            or identifier == "SetterCanOOM"
5141            or identifier == "SetterNeedsSubjectPrincipal"
5142        ) and self.readonly:
5143            raise WebIDLError(
5144                "Readonly attributes must not be flagged as " "[%s]" % identifier,
5145                [self.location],
5146            )
5147        elif identifier == "BindingAlias":
5148            if not attr.hasValue():
5149                raise WebIDLError(
5150                    "[BindingAlias] takes an identifier or string", [attr.location]
5151                )
5152            self._addBindingAlias(attr.value())
5153        elif (
5154            (
5155                identifier == "Throws"
5156                or identifier == "GetterThrows"
5157                or identifier == "CanOOM"
5158                or identifier == "GetterCanOOM"
5159            )
5160            and self.getExtendedAttribute("StoreInSlot")
5161        ) or (
5162            identifier == "StoreInSlot"
5163            and (
5164                self.getExtendedAttribute("Throws")
5165                or self.getExtendedAttribute("GetterThrows")
5166                or self.getExtendedAttribute("CanOOM")
5167                or self.getExtendedAttribute("GetterCanOOM")
5168            )
5169        ):
5170            raise WebIDLError("Throwing things can't be [StoreInSlot]", [attr.location])
5171        elif identifier == "LegacyLenientThis":
5172            if not attr.noArguments():
5173                raise WebIDLError(
5174                    "[LegacyLenientThis] must take no arguments", [attr.location]
5175                )
5176            if self.isStatic():
5177                raise WebIDLError(
5178                    "[LegacyLenientThis] is only allowed on non-static " "attributes",
5179                    [attr.location, self.location],
5180                )
5181            if self.getExtendedAttribute("CrossOriginReadable"):
5182                raise WebIDLError(
5183                    "[LegacyLenientThis] is not allowed in combination "
5184                    "with [CrossOriginReadable]",
5185                    [attr.location, self.location],
5186                )
5187            if self.getExtendedAttribute("CrossOriginWritable"):
5188                raise WebIDLError(
5189                    "[LegacyLenientThis] is not allowed in combination "
5190                    "with [CrossOriginWritable]",
5191                    [attr.location, self.location],
5192                )
5193            self.legacyLenientThis = True
5194        elif identifier == "LegacyUnforgeable":
5195            if self.isStatic():
5196                raise WebIDLError(
5197                    "[LegacyUnforgeable] is only allowed on non-static " "attributes",
5198                    [attr.location, self.location],
5199                )
5200            self._legacyUnforgeable = True
5201        elif identifier == "SameObject" and not self.readonly:
5202            raise WebIDLError(
5203                "[SameObject] only allowed on readonly attributes",
5204                [attr.location, self.location],
5205            )
5206        elif identifier == "Constant" and not self.readonly:
5207            raise WebIDLError(
5208                "[Constant] only allowed on readonly attributes",
5209                [attr.location, self.location],
5210            )
5211        elif identifier == "PutForwards":
5212            if not self.readonly:
5213                raise WebIDLError(
5214                    "[PutForwards] is only allowed on readonly " "attributes",
5215                    [attr.location, self.location],
5216                )
5217            if self.type.isPromise():
5218                raise WebIDLError(
5219                    "[PutForwards] is not allowed on " "Promise-typed attributes",
5220                    [attr.location, self.location],
5221                )
5222            if self.isStatic():
5223                raise WebIDLError(
5224                    "[PutForwards] is only allowed on non-static " "attributes",
5225                    [attr.location, self.location],
5226                )
5227            if self.getExtendedAttribute("Replaceable") is not None:
5228                raise WebIDLError(
5229                    "[PutForwards] and [Replaceable] can't both "
5230                    "appear on the same attribute",
5231                    [attr.location, self.location],
5232                )
5233            if not attr.hasValue():
5234                raise WebIDLError(
5235                    "[PutForwards] takes an identifier", [attr.location, self.location]
5236                )
5237        elif identifier == "Replaceable":
5238            if not attr.noArguments():
5239                raise WebIDLError(
5240                    "[Replaceable] must take no arguments", [attr.location]
5241                )
5242            if not self.readonly:
5243                raise WebIDLError(
5244                    "[Replaceable] is only allowed on readonly " "attributes",
5245                    [attr.location, self.location],
5246                )
5247            if self.type.isPromise():
5248                raise WebIDLError(
5249                    "[Replaceable] is not allowed on " "Promise-typed attributes",
5250                    [attr.location, self.location],
5251                )
5252            if self.isStatic():
5253                raise WebIDLError(
5254                    "[Replaceable] is only allowed on non-static " "attributes",
5255                    [attr.location, self.location],
5256                )
5257            if self.getExtendedAttribute("PutForwards") is not None:
5258                raise WebIDLError(
5259                    "[PutForwards] and [Replaceable] can't both "
5260                    "appear on the same attribute",
5261                    [attr.location, self.location],
5262                )
5263        elif identifier == "LegacyLenientSetter":
5264            if not attr.noArguments():
5265                raise WebIDLError(
5266                    "[LegacyLenientSetter] must take no arguments", [attr.location]
5267                )
5268            if not self.readonly:
5269                raise WebIDLError(
5270                    "[LegacyLenientSetter] is only allowed on readonly " "attributes",
5271                    [attr.location, self.location],
5272                )
5273            if self.type.isPromise():
5274                raise WebIDLError(
5275                    "[LegacyLenientSetter] is not allowed on "
5276                    "Promise-typed attributes",
5277                    [attr.location, self.location],
5278                )
5279            if self.isStatic():
5280                raise WebIDLError(
5281                    "[LegacyLenientSetter] is only allowed on non-static " "attributes",
5282                    [attr.location, self.location],
5283                )
5284            if self.getExtendedAttribute("PutForwards") is not None:
5285                raise WebIDLError(
5286                    "[LegacyLenientSetter] and [PutForwards] can't both "
5287                    "appear on the same attribute",
5288                    [attr.location, self.location],
5289                )
5290            if self.getExtendedAttribute("Replaceable") is not None:
5291                raise WebIDLError(
5292                    "[LegacyLenientSetter] and [Replaceable] can't both "
5293                    "appear on the same attribute",
5294                    [attr.location, self.location],
5295                )
5296        elif identifier == "LenientFloat":
5297            if self.readonly:
5298                raise WebIDLError(
5299                    "[LenientFloat] used on a readonly attribute",
5300                    [attr.location, self.location],
5301                )
5302            if not self.type.includesRestrictedFloat():
5303                raise WebIDLError(
5304                    "[LenientFloat] used on an attribute with a "
5305                    "non-restricted-float type",
5306                    [attr.location, self.location],
5307                )
5308        elif identifier == "StoreInSlot":
5309            if self.getExtendedAttribute("Cached"):
5310                raise WebIDLError(
5311                    "[StoreInSlot] and [Cached] must not be "
5312                    "specified on the same attribute",
5313                    [attr.location, self.location],
5314                )
5315        elif identifier == "Cached":
5316            if self.getExtendedAttribute("StoreInSlot"):
5317                raise WebIDLError(
5318                    "[Cached] and [StoreInSlot] must not be "
5319                    "specified on the same attribute",
5320                    [attr.location, self.location],
5321                )
5322        elif identifier == "CrossOriginReadable" or identifier == "CrossOriginWritable":
5323            if not attr.noArguments():
5324                raise WebIDLError(
5325                    "[%s] must take no arguments" % identifier, [attr.location]
5326                )
5327            if self.isStatic():
5328                raise WebIDLError(
5329                    "[%s] is only allowed on non-static " "attributes" % identifier,
5330                    [attr.location, self.location],
5331                )
5332            if self.getExtendedAttribute("LegacyLenientThis"):
5333                raise WebIDLError(
5334                    "[LegacyLenientThis] is not allowed in combination "
5335                    "with [%s]" % identifier,
5336                    [attr.location, self.location],
5337                )
5338        elif identifier == "Exposed":
5339            convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
5340        elif identifier == "Pure":
5341            if not attr.noArguments():
5342                raise WebIDLError("[Pure] must take no arguments", [attr.location])
5343            self._setDependsOn("DOMState")
5344            self._setAffects("Nothing")
5345        elif identifier == "Constant" or identifier == "SameObject":
5346            if not attr.noArguments():
5347                raise WebIDLError(
5348                    "[%s] must take no arguments" % identifier, [attr.location]
5349                )
5350            self._setDependsOn("Nothing")
5351            self._setAffects("Nothing")
5352        elif identifier == "Affects":
5353            if not attr.hasValue():
5354                raise WebIDLError("[Affects] takes an identifier", [attr.location])
5355            self._setAffects(attr.value())
5356        elif identifier == "DependsOn":
5357            if not attr.hasValue():
5358                raise WebIDLError("[DependsOn] takes an identifier", [attr.location])
5359            if (
5360                attr.value() != "Everything"
5361                and attr.value() != "DOMState"
5362                and not self.readonly
5363            ):
5364                raise WebIDLError(
5365                    "[DependsOn=%s] only allowed on "
5366                    "readonly attributes" % attr.value(),
5367                    [attr.location, self.location],
5368                )
5369            self._setDependsOn(attr.value())
5370        elif identifier == "UseCounter":
5371            if self.stringifier:
5372                raise WebIDLError(
5373                    "[UseCounter] must not be used on a " "stringifier attribute",
5374                    [attr.location, self.location],
5375                )
5376        elif identifier == "Unscopable":
5377            if not attr.noArguments():
5378                raise WebIDLError(
5379                    "[Unscopable] must take no arguments", [attr.location]
5380                )
5381            if self.isStatic():
5382                raise WebIDLError(
5383                    "[Unscopable] is only allowed on non-static "
5384                    "attributes and operations",
5385                    [attr.location, self.location],
5386                )
5387        elif identifier == "CEReactions":
5388            if not attr.noArguments():
5389                raise WebIDLError(
5390                    "[CEReactions] must take no arguments", [attr.location]
5391                )
5392        elif (
5393            identifier == "Pref"
5394            or identifier == "Deprecated"
5395            or identifier == "SetterThrows"
5396            or identifier == "Throws"
5397            or identifier == "GetterThrows"
5398            or identifier == "SetterCanOOM"
5399            or identifier == "CanOOM"
5400            or identifier == "GetterCanOOM"
5401            or identifier == "ChromeOnly"
5402            or identifier == "Func"
5403            or identifier == "SecureContext"
5404            or identifier == "Frozen"
5405            or identifier == "NewObject"
5406            or identifier == "NeedsSubjectPrincipal"
5407            or identifier == "SetterNeedsSubjectPrincipal"
5408            or identifier == "GetterNeedsSubjectPrincipal"
5409            or identifier == "NeedsCallerType"
5410            or identifier == "ReturnValueNeedsContainsHack"
5411            or identifier == "BinaryName"
5412            or identifier == "NonEnumerable"
5413        ):
5414            # Known attributes that we don't need to do anything with here
5415            pass
5416        else:
5417            raise WebIDLError(
5418                "Unknown extended attribute %s on attribute" % identifier,
5419                [attr.location],
5420            )
5421        IDLInterfaceMember.handleExtendedAttribute(self, attr)
5422
5423    def resolve(self, parentScope):
5424        assert isinstance(parentScope, IDLScope)
5425        self.type.resolveType(parentScope)
5426        IDLObjectWithIdentifier.resolve(self, parentScope)
5427
5428    def hasLegacyLenientThis(self):
5429        return self.legacyLenientThis
5430
5431    def isMaplikeOrSetlikeAttr(self):
5432        """
5433        True if this attribute was generated from an interface with
5434        maplike/setlike (e.g. this is the size attribute for
5435        maplike/setlike)
5436        """
5437        return self.maplikeOrSetlike is not None
5438
5439    def isLegacyUnforgeable(self):
5440        return self._legacyUnforgeable
5441
5442    def _getDependentObjects(self):
5443        return set([self.type])
5444
5445    def expand(self, members):
5446        assert self.stringifier
5447        if (
5448            not self.type.isDOMString()
5449            and not self.type.isUSVString()
5450            and not self.type.isUTF8String()
5451        ):
5452            raise WebIDLError(
5453                "The type of a stringifer attribute must be "
5454                "either DOMString, USVString or UTF8String",
5455                [self.location],
5456            )
5457        identifier = IDLUnresolvedIdentifier(
5458            self.location, "__stringifier", allowDoubleUnderscore=True
5459        )
5460        method = IDLMethod(
5461            self.location,
5462            identifier,
5463            returnType=self.type,
5464            arguments=[],
5465            stringifier=True,
5466            underlyingAttr=self,
5467        )
5468        allowedExtAttrs = ["Throws", "NeedsSubjectPrincipal", "Pure"]
5469        # Safe to ignore these as they are only meaningful for attributes
5470        attributeOnlyExtAttrs = [
5471            "CEReactions",
5472            "CrossOriginWritable",
5473            "SetterThrows",
5474        ]
5475        for (key, value) in self._extendedAttrDict.items():
5476            if key in allowedExtAttrs:
5477                if value is not True:
5478                    raise WebIDLError(
5479                        "[%s] with a value is currently "
5480                        "unsupported in stringifier attributes, "
5481                        "please file a bug to add support" % key,
5482                        [self.location],
5483                    )
5484                method.addExtendedAttributes(
5485                    [IDLExtendedAttribute(self.location, (key,))]
5486                )
5487            elif not key in attributeOnlyExtAttrs:
5488                raise WebIDLError(
5489                    "[%s] is currently unsupported in "
5490                    "stringifier attributes, please file a bug "
5491                    "to add support" % key,
5492                    [self.location],
5493                )
5494        members.append(method)
5495
5496
5497class IDLArgument(IDLObjectWithIdentifier):
5498    def __init__(
5499        self,
5500        location,
5501        identifier,
5502        type,
5503        optional=False,
5504        defaultValue=None,
5505        variadic=False,
5506        dictionaryMember=False,
5507        allowTypeAttributes=False,
5508    ):
5509        IDLObjectWithIdentifier.__init__(self, location, None, identifier)
5510
5511        assert isinstance(type, IDLType)
5512        self.type = type
5513
5514        self.optional = optional
5515        self.defaultValue = defaultValue
5516        self.variadic = variadic
5517        self.dictionaryMember = dictionaryMember
5518        self._isComplete = False
5519        self._allowTreatNonCallableAsNull = False
5520        self._extendedAttrDict = {}
5521        self.allowTypeAttributes = allowTypeAttributes
5522
5523        assert not variadic or optional
5524        assert not variadic or not defaultValue
5525
5526    def addExtendedAttributes(self, attrs):
5527        for attribute in attrs:
5528            identifier = attribute.identifier()
5529            if self.allowTypeAttributes and (
5530                identifier == "EnforceRange"
5531                or identifier == "Clamp"
5532                or identifier == "LegacyNullToEmptyString"
5533                or identifier == "AllowShared"
5534            ):
5535                self.type = self.type.withExtendedAttributes([attribute])
5536            elif identifier == "TreatNonCallableAsNull":
5537                self._allowTreatNonCallableAsNull = True
5538            elif self.dictionaryMember and (
5539                identifier == "ChromeOnly"
5540                or identifier == "Func"
5541                or identifier == "Pref"
5542            ):
5543                if not self.optional:
5544                    raise WebIDLError(
5545                        "[%s] must not be used on a required "
5546                        "dictionary member" % identifier,
5547                        [attribute.location],
5548                    )
5549            else:
5550                raise WebIDLError(
5551                    "Unhandled extended attribute on %s"
5552                    % (
5553                        "a dictionary member"
5554                        if self.dictionaryMember
5555                        else "an argument"
5556                    ),
5557                    [attribute.location],
5558                )
5559            attrlist = attribute.listValue()
5560            self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
5561
5562    def getExtendedAttribute(self, name):
5563        return self._extendedAttrDict.get(name, None)
5564
5565    def isComplete(self):
5566        return self._isComplete
5567
5568    def complete(self, scope):
5569        if self._isComplete:
5570            return
5571
5572        self._isComplete = True
5573
5574        if not self.type.isComplete():
5575            type = self.type.complete(scope)
5576            assert not isinstance(type, IDLUnresolvedType)
5577            assert not isinstance(type, IDLTypedefType)
5578            assert not isinstance(type.name, IDLUnresolvedIdentifier)
5579            self.type = type
5580
5581        if self.type.isAny():
5582            assert self.defaultValue is None or isinstance(
5583                self.defaultValue, IDLNullValue
5584            )
5585            # optional 'any' values always have a default value
5586            if self.optional and not self.defaultValue and not self.variadic:
5587                # Set the default value to undefined, for simplicity, so the
5588                # codegen doesn't have to special-case this.
5589                self.defaultValue = IDLUndefinedValue(self.location)
5590
5591        if self.dictionaryMember and self.type.legacyNullToEmptyString:
5592            raise WebIDLError(
5593                "Dictionary members cannot be [LegacyNullToEmptyString]",
5594                [self.location],
5595            )
5596        # Now do the coercing thing; this needs to happen after the
5597        # above creation of a default value.
5598        if self.defaultValue:
5599            self.defaultValue = self.defaultValue.coerceToType(self.type, self.location)
5600            assert self.defaultValue
5601
5602    def allowTreatNonCallableAsNull(self):
5603        return self._allowTreatNonCallableAsNull
5604
5605    def _getDependentObjects(self):
5606        deps = set([self.type])
5607        if self.defaultValue:
5608            deps.add(self.defaultValue)
5609        return deps
5610
5611    def canHaveMissingValue(self):
5612        return self.optional and not self.defaultValue
5613
5614
5615class IDLCallback(IDLObjectWithScope):
5616    def __init__(
5617        self, location, parentScope, identifier, returnType, arguments, isConstructor
5618    ):
5619        assert isinstance(returnType, IDLType)
5620
5621        self._returnType = returnType
5622        # Clone the list
5623        self._arguments = list(arguments)
5624
5625        IDLObjectWithScope.__init__(self, location, parentScope, identifier)
5626
5627        for (returnType, arguments) in self.signatures():
5628            for argument in arguments:
5629                argument.resolve(self)
5630
5631        self._treatNonCallableAsNull = False
5632        self._treatNonObjectAsNull = False
5633        self._isRunScriptBoundary = False
5634        self._isConstructor = isConstructor
5635
5636    def isCallback(self):
5637        return True
5638
5639    def isConstructor(self):
5640        return self._isConstructor
5641
5642    def signatures(self):
5643        return [(self._returnType, self._arguments)]
5644
5645    def finish(self, scope):
5646        if not self._returnType.isComplete():
5647            type = self._returnType.complete(scope)
5648
5649            assert not isinstance(type, IDLUnresolvedType)
5650            assert not isinstance(type, IDLTypedefType)
5651            assert not isinstance(type.name, IDLUnresolvedIdentifier)
5652            self._returnType = type
5653
5654        for argument in self._arguments:
5655            if argument.type.isComplete():
5656                continue
5657
5658            type = argument.type.complete(scope)
5659
5660            assert not isinstance(type, IDLUnresolvedType)
5661            assert not isinstance(type, IDLTypedefType)
5662            assert not isinstance(type.name, IDLUnresolvedIdentifier)
5663            argument.type = type
5664
5665    def validate(self):
5666        pass
5667
5668    def addExtendedAttributes(self, attrs):
5669        unhandledAttrs = []
5670        for attr in attrs:
5671            if attr.identifier() == "TreatNonCallableAsNull":
5672                self._treatNonCallableAsNull = True
5673            elif attr.identifier() == "LegacyTreatNonObjectAsNull":
5674                if self._isConstructor:
5675                    raise WebIDLError(
5676                        "[LegacyTreatNonObjectAsNull] is not supported "
5677                        "on constructors",
5678                        [self.location],
5679                    )
5680                self._treatNonObjectAsNull = True
5681            elif attr.identifier() == "MOZ_CAN_RUN_SCRIPT_BOUNDARY":
5682                if self._isConstructor:
5683                    raise WebIDLError(
5684                        "[MOZ_CAN_RUN_SCRIPT_BOUNDARY] is not "
5685                        "permitted on constructors",
5686                        [self.location],
5687                    )
5688                self._isRunScriptBoundary = True
5689            else:
5690                unhandledAttrs.append(attr)
5691        if self._treatNonCallableAsNull and self._treatNonObjectAsNull:
5692            raise WebIDLError(
5693                "Cannot specify both [TreatNonCallableAsNull] "
5694                "and [LegacyTreatNonObjectAsNull]",
5695                [self.location],
5696            )
5697        if len(unhandledAttrs) != 0:
5698            IDLType.addExtendedAttributes(self, unhandledAttrs)
5699
5700    def _getDependentObjects(self):
5701        return set([self._returnType] + self._arguments)
5702
5703    def isRunScriptBoundary(self):
5704        return self._isRunScriptBoundary
5705
5706
5707class IDLCallbackType(IDLType):
5708    def __init__(self, location, callback):
5709        IDLType.__init__(self, location, callback.identifier.name)
5710        self.callback = callback
5711
5712    def isCallback(self):
5713        return True
5714
5715    def tag(self):
5716        return IDLType.Tags.callback
5717
5718    def isDistinguishableFrom(self, other):
5719        if other.isPromise():
5720            return False
5721        if other.isUnion():
5722            # Just forward to the union; it'll deal
5723            return other.isDistinguishableFrom(self)
5724        return (
5725            other.isPrimitive()
5726            or other.isString()
5727            or other.isEnum()
5728            or other.isNonCallbackInterface()
5729            or other.isSequence()
5730        )
5731
5732    def _getDependentObjects(self):
5733        return self.callback._getDependentObjects()
5734
5735
5736class IDLMethodOverload:
5737    """
5738    A class that represents a single overload of a WebIDL method.  This is not
5739    quite the same as an element of the "effective overload set" in the spec,
5740    because separate IDLMethodOverloads are not created based on arguments being
5741    optional.  Rather, when multiple methods have the same name, there is an
5742    IDLMethodOverload for each one, all hanging off an IDLMethod representing
5743    the full set of overloads.
5744    """
5745
5746    def __init__(self, returnType, arguments, location):
5747        self.returnType = returnType
5748        # Clone the list of arguments, just in case
5749        self.arguments = list(arguments)
5750        self.location = location
5751
5752    def _getDependentObjects(self):
5753        deps = set(self.arguments)
5754        deps.add(self.returnType)
5755        return deps
5756
5757    def includesRestrictedFloatArgument(self):
5758        return any(arg.type.includesRestrictedFloat() for arg in self.arguments)
5759
5760
5761class IDLMethod(IDLInterfaceMember, IDLScope):
5762
5763    Special = enum(
5764        "Getter", "Setter", "Deleter", "LegacyCaller", base=IDLInterfaceMember.Special
5765    )
5766
5767    NamedOrIndexed = enum("Neither", "Named", "Indexed")
5768
5769    def __init__(
5770        self,
5771        location,
5772        identifier,
5773        returnType,
5774        arguments,
5775        static=False,
5776        getter=False,
5777        setter=False,
5778        deleter=False,
5779        specialType=NamedOrIndexed.Neither,
5780        legacycaller=False,
5781        stringifier=False,
5782        maplikeOrSetlikeOrIterable=None,
5783        underlyingAttr=None,
5784    ):
5785        # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
5786        IDLInterfaceMember.__init__(
5787            self, location, identifier, IDLInterfaceMember.Tags.Method
5788        )
5789
5790        self._hasOverloads = False
5791
5792        assert isinstance(returnType, IDLType)
5793
5794        # self._overloads is a list of IDLMethodOverloads
5795        self._overloads = [IDLMethodOverload(returnType, arguments, location)]
5796
5797        assert isinstance(static, bool)
5798        self._static = static
5799        assert isinstance(getter, bool)
5800        self._getter = getter
5801        assert isinstance(setter, bool)
5802        self._setter = setter
5803        assert isinstance(deleter, bool)
5804        self._deleter = deleter
5805        assert isinstance(legacycaller, bool)
5806        self._legacycaller = legacycaller
5807        assert isinstance(stringifier, bool)
5808        self._stringifier = stringifier
5809        assert maplikeOrSetlikeOrIterable is None or isinstance(
5810            maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase
5811        )
5812        self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable
5813        self._htmlConstructor = False
5814        self.underlyingAttr = underlyingAttr
5815        self._specialType = specialType
5816        self._legacyUnforgeable = False
5817        self.dependsOn = "Everything"
5818        self.affects = "Everything"
5819        self.aliases = []
5820
5821        if static and identifier.name == "prototype":
5822            raise WebIDLError(
5823                "The identifier of a static operation must not be 'prototype'",
5824                [location],
5825            )
5826
5827        self.assertSignatureConstraints()
5828
5829    def __str__(self):
5830        return "Method '%s'" % self.identifier
5831
5832    def assertSignatureConstraints(self):
5833        if self._getter or self._deleter:
5834            assert len(self._overloads) == 1
5835            overload = self._overloads[0]
5836            arguments = overload.arguments
5837            assert len(arguments) == 1
5838            assert (
5839                arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring]
5840                or arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]
5841            )
5842            assert not arguments[0].optional and not arguments[0].variadic
5843            assert not self._getter or not overload.returnType.isVoid()
5844
5845        if self._setter:
5846            assert len(self._overloads) == 1
5847            arguments = self._overloads[0].arguments
5848            assert len(arguments) == 2
5849            assert (
5850                arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring]
5851                or arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]
5852            )
5853            assert not arguments[0].optional and not arguments[0].variadic
5854            assert not arguments[1].optional and not arguments[1].variadic
5855
5856        if self._stringifier:
5857            assert len(self._overloads) == 1
5858            overload = self._overloads[0]
5859            assert len(overload.arguments) == 0
5860            if not self.underlyingAttr:
5861                assert (
5862                    overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring]
5863                )
5864
5865    def isStatic(self):
5866        return self._static
5867
5868    def forceStatic(self):
5869        self._static = True
5870
5871    def isGetter(self):
5872        return self._getter
5873
5874    def isSetter(self):
5875        return self._setter
5876
5877    def isDeleter(self):
5878        return self._deleter
5879
5880    def isNamed(self):
5881        assert (
5882            self._specialType == IDLMethod.NamedOrIndexed.Named
5883            or self._specialType == IDLMethod.NamedOrIndexed.Indexed
5884        )
5885        return self._specialType == IDLMethod.NamedOrIndexed.Named
5886
5887    def isIndexed(self):
5888        assert (
5889            self._specialType == IDLMethod.NamedOrIndexed.Named
5890            or self._specialType == IDLMethod.NamedOrIndexed.Indexed
5891        )
5892        return self._specialType == IDLMethod.NamedOrIndexed.Indexed
5893
5894    def isLegacycaller(self):
5895        return self._legacycaller
5896
5897    def isStringifier(self):
5898        return self._stringifier
5899
5900    def isToJSON(self):
5901        return self.identifier.name == "toJSON"
5902
5903    def isDefaultToJSON(self):
5904        return self.isToJSON() and self.getExtendedAttribute("Default")
5905
5906    def isMaplikeOrSetlikeOrIterableMethod(self):
5907        """
5908        True if this method was generated as part of a
5909        maplike/setlike/etc interface (e.g. has/get methods)
5910        """
5911        return self.maplikeOrSetlikeOrIterable is not None
5912
5913    def isSpecial(self):
5914        return (
5915            self.isGetter()
5916            or self.isSetter()
5917            or self.isDeleter()
5918            or self.isLegacycaller()
5919            or self.isStringifier()
5920        )
5921
5922    def isHTMLConstructor(self):
5923        return self._htmlConstructor
5924
5925    def hasOverloads(self):
5926        return self._hasOverloads
5927
5928    def isIdentifierLess(self):
5929        """
5930        True if the method name started with __, and if the method is not a
5931        maplike/setlike method. Interfaces with maplike/setlike will generate
5932        methods starting with __ for chrome only backing object access in JS
5933        implemented interfaces, so while these functions use what is considered
5934        an non-identifier name, they actually DO have an identifier.
5935        """
5936        return (
5937            self.identifier.name[:2] == "__"
5938            and not self.isMaplikeOrSetlikeOrIterableMethod()
5939        )
5940
5941    def resolve(self, parentScope):
5942        assert isinstance(parentScope, IDLScope)
5943        IDLObjectWithIdentifier.resolve(self, parentScope)
5944        IDLScope.__init__(self, self.location, parentScope, self.identifier)
5945        for (returnType, arguments) in self.signatures():
5946            for argument in arguments:
5947                argument.resolve(self)
5948
5949    def addOverload(self, method):
5950        assert len(method._overloads) == 1
5951
5952        if self._extendedAttrDict != method._extendedAttrDict:
5953            extendedAttrDiff = set(self._extendedAttrDict.keys()) ^ set(
5954                method._extendedAttrDict.keys()
5955            )
5956
5957            if extendedAttrDiff == {"LenientFloat"}:
5958                if "LenientFloat" not in self._extendedAttrDict:
5959                    for overload in self._overloads:
5960                        if overload.includesRestrictedFloatArgument():
5961                            raise WebIDLError(
5962                                "Restricted float behavior differs on different "
5963                                "overloads of %s" % method.identifier,
5964                                [overload.location, method.location],
5965                            )
5966                    self._extendedAttrDict["LenientFloat"] = method._extendedAttrDict[
5967                        "LenientFloat"
5968                    ]
5969                elif method._overloads[0].includesRestrictedFloatArgument():
5970                    raise WebIDLError(
5971                        "Restricted float behavior differs on different "
5972                        "overloads of %s" % method.identifier,
5973                        [self.location, method.location],
5974                    )
5975            else:
5976                raise WebIDLError(
5977                    "Extended attributes differ on different "
5978                    "overloads of %s" % method.identifier,
5979                    [self.location, method.location],
5980                )
5981
5982        self._overloads.extend(method._overloads)
5983
5984        self._hasOverloads = True
5985
5986        if self.isStatic() != method.isStatic():
5987            raise WebIDLError(
5988                "Overloaded identifier %s appears with different values of the 'static' attribute"
5989                % method.identifier,
5990                [method.location],
5991            )
5992
5993        if self.isLegacycaller() != method.isLegacycaller():
5994            raise WebIDLError(
5995                "Overloaded identifier %s appears with different values of the 'legacycaller' attribute"
5996                % method.identifier,
5997                [method.location],
5998            )
5999
6000        # Can't overload special things!
6001        assert not self.isGetter()
6002        assert not method.isGetter()
6003        assert not self.isSetter()
6004        assert not method.isSetter()
6005        assert not self.isDeleter()
6006        assert not method.isDeleter()
6007        assert not self.isStringifier()
6008        assert not method.isStringifier()
6009        assert not self.isHTMLConstructor()
6010        assert not method.isHTMLConstructor()
6011
6012        return self
6013
6014    def signatures(self):
6015        return [
6016            (overload.returnType, overload.arguments) for overload in self._overloads
6017        ]
6018
6019    def finish(self, scope):
6020        IDLInterfaceMember.finish(self, scope)
6021
6022        for overload in self._overloads:
6023            returnType = overload.returnType
6024            if not returnType.isComplete():
6025                returnType = returnType.complete(scope)
6026                assert not isinstance(returnType, IDLUnresolvedType)
6027                assert not isinstance(returnType, IDLTypedefType)
6028                assert not isinstance(returnType.name, IDLUnresolvedIdentifier)
6029                overload.returnType = returnType
6030
6031            for argument in overload.arguments:
6032                if not argument.isComplete():
6033                    argument.complete(scope)
6034                assert argument.type.isComplete()
6035
6036        # Now compute various information that will be used by the
6037        # WebIDL overload resolution algorithm.
6038        self.maxArgCount = max(len(s[1]) for s in self.signatures())
6039        self.allowedArgCounts = [
6040            i
6041            for i in range(self.maxArgCount + 1)
6042            if len(self.signaturesForArgCount(i)) != 0
6043        ]
6044
6045    def validate(self):
6046        IDLInterfaceMember.validate(self)
6047
6048        # Make sure our overloads are properly distinguishable and don't have
6049        # different argument types before the distinguishing args.
6050        for argCount in self.allowedArgCounts:
6051            possibleOverloads = self.overloadsForArgCount(argCount)
6052            if len(possibleOverloads) == 1:
6053                continue
6054            distinguishingIndex = self.distinguishingIndexForArgCount(argCount)
6055            for idx in range(distinguishingIndex):
6056                firstSigType = possibleOverloads[0].arguments[idx].type
6057                for overload in possibleOverloads[1:]:
6058                    if overload.arguments[idx].type != firstSigType:
6059                        raise WebIDLError(
6060                            "Signatures for method '%s' with %d arguments have "
6061                            "different types of arguments at index %d, which "
6062                            "is before distinguishing index %d"
6063                            % (
6064                                self.identifier.name,
6065                                argCount,
6066                                idx,
6067                                distinguishingIndex,
6068                            ),
6069                            [self.location, overload.location],
6070                        )
6071
6072        overloadWithPromiseReturnType = None
6073        overloadWithoutPromiseReturnType = None
6074        for overload in self._overloads:
6075            returnType = overload.returnType
6076            if not returnType.unroll().isExposedInAllOf(self.exposureSet):
6077                raise WebIDLError(
6078                    "Overload returns a type that is not exposed "
6079                    "everywhere where the method is exposed",
6080                    [overload.location],
6081                )
6082
6083            variadicArgument = None
6084
6085            arguments = overload.arguments
6086            for (idx, argument) in enumerate(arguments):
6087                assert argument.type.isComplete()
6088
6089                if (
6090                    argument.type.isDictionary()
6091                    and argument.type.unroll().inner.canBeEmpty()
6092                ) or (
6093                    argument.type.isUnion()
6094                    and argument.type.unroll().hasPossiblyEmptyDictionaryType()
6095                ):
6096                    # Optional dictionaries and unions containing optional
6097                    # dictionaries at the end of the list or followed by
6098                    # optional arguments must be optional.
6099                    if not argument.optional and all(
6100                        arg.optional for arg in arguments[idx + 1 :]
6101                    ):
6102                        raise WebIDLError(
6103                            "Dictionary argument without any "
6104                            "required fields or union argument "
6105                            "containing such dictionary not "
6106                            "followed by a required argument "
6107                            "must be optional",
6108                            [argument.location],
6109                        )
6110
6111                    if not argument.defaultValue and all(
6112                        arg.optional for arg in arguments[idx + 1 :]
6113                    ):
6114                        raise WebIDLError(
6115                            "Dictionary argument without any "
6116                            "required fields or union argument "
6117                            "containing such dictionary not "
6118                            "followed by a required argument "
6119                            "must have a default value",
6120                            [argument.location],
6121                        )
6122
6123                # An argument cannot be a nullable dictionary or a
6124                # nullable union containing a dictionary.
6125                if argument.type.nullable() and (
6126                    argument.type.isDictionary()
6127                    or (
6128                        argument.type.isUnion()
6129                        and argument.type.unroll().hasDictionaryType()
6130                    )
6131                ):
6132                    raise WebIDLError(
6133                        "An argument cannot be a nullable "
6134                        "dictionary or nullable union "
6135                        "containing a dictionary",
6136                        [argument.location],
6137                    )
6138
6139                # Only the last argument can be variadic
6140                if variadicArgument:
6141                    raise WebIDLError(
6142                        "Variadic argument is not last argument",
6143                        [variadicArgument.location],
6144                    )
6145                if argument.variadic:
6146                    variadicArgument = argument
6147
6148            if returnType.isPromise():
6149                overloadWithPromiseReturnType = overload
6150            else:
6151                overloadWithoutPromiseReturnType = overload
6152
6153        # Make sure either all our overloads return Promises or none do
6154        if overloadWithPromiseReturnType and overloadWithoutPromiseReturnType:
6155            raise WebIDLError(
6156                "We have overloads with both Promise and " "non-Promise return types",
6157                [
6158                    overloadWithPromiseReturnType.location,
6159                    overloadWithoutPromiseReturnType.location,
6160                ],
6161            )
6162
6163        if overloadWithPromiseReturnType and self._legacycaller:
6164            raise WebIDLError(
6165                "May not have a Promise return type for a " "legacycaller.",
6166                [overloadWithPromiseReturnType.location],
6167            )
6168
6169        if self.getExtendedAttribute("StaticClassOverride") and not (
6170            self.identifier.scope.isJSImplemented() and self.isStatic()
6171        ):
6172            raise WebIDLError(
6173                "StaticClassOverride can be applied to static"
6174                " methods on JS-implemented classes only.",
6175                [self.location],
6176            )
6177
6178        # Ensure that toJSON methods satisfy the spec constraints on them.
6179        if self.identifier.name == "toJSON":
6180            if len(self.signatures()) != 1:
6181                raise WebIDLError(
6182                    "toJSON method has multiple overloads",
6183                    [self._overloads[0].location, self._overloads[1].location],
6184                )
6185            if len(self.signatures()[0][1]) != 0:
6186                raise WebIDLError("toJSON method has arguments", [self.location])
6187            if not self.signatures()[0][0].isJSONType():
6188                raise WebIDLError(
6189                    "toJSON method has non-JSON return type", [self.location]
6190                )
6191
6192    def overloadsForArgCount(self, argc):
6193        return [
6194            overload
6195            for overload in self._overloads
6196            if len(overload.arguments) == argc
6197            or (
6198                len(overload.arguments) > argc
6199                and all(arg.optional for arg in overload.arguments[argc:])
6200            )
6201            or (
6202                len(overload.arguments) < argc
6203                and len(overload.arguments) > 0
6204                and overload.arguments[-1].variadic
6205            )
6206        ]
6207
6208    def signaturesForArgCount(self, argc):
6209        return [
6210            (overload.returnType, overload.arguments)
6211            for overload in self.overloadsForArgCount(argc)
6212        ]
6213
6214    def locationsForArgCount(self, argc):
6215        return [overload.location for overload in self.overloadsForArgCount(argc)]
6216
6217    def distinguishingIndexForArgCount(self, argc):
6218        def isValidDistinguishingIndex(idx, signatures):
6219            for (firstSigIndex, (firstRetval, firstArgs)) in enumerate(signatures[:-1]):
6220                for (secondRetval, secondArgs) in signatures[firstSigIndex + 1 :]:
6221                    if idx < len(firstArgs):
6222                        firstType = firstArgs[idx].type
6223                    else:
6224                        assert firstArgs[-1].variadic
6225                        firstType = firstArgs[-1].type
6226                    if idx < len(secondArgs):
6227                        secondType = secondArgs[idx].type
6228                    else:
6229                        assert secondArgs[-1].variadic
6230                        secondType = secondArgs[-1].type
6231                    if not firstType.isDistinguishableFrom(secondType):
6232                        return False
6233            return True
6234
6235        signatures = self.signaturesForArgCount(argc)
6236        for idx in range(argc):
6237            if isValidDistinguishingIndex(idx, signatures):
6238                return idx
6239        # No valid distinguishing index.  Time to throw
6240        locations = self.locationsForArgCount(argc)
6241        raise WebIDLError(
6242            "Signatures with %d arguments for method '%s' are not "
6243            "distinguishable" % (argc, self.identifier.name),
6244            locations,
6245        )
6246
6247    def handleExtendedAttribute(self, attr):
6248        identifier = attr.identifier()
6249        if (
6250            identifier == "GetterThrows"
6251            or identifier == "SetterThrows"
6252            or identifier == "GetterCanOOM"
6253            or identifier == "SetterCanOOM"
6254            or identifier == "SetterNeedsSubjectPrincipal"
6255            or identifier == "GetterNeedsSubjectPrincipal"
6256        ):
6257            raise WebIDLError(
6258                "Methods must not be flagged as " "[%s]" % identifier,
6259                [attr.location, self.location],
6260            )
6261        elif identifier == "LegacyUnforgeable":
6262            if self.isStatic():
6263                raise WebIDLError(
6264                    "[LegacyUnforgeable] is only allowed on non-static " "methods",
6265                    [attr.location, self.location],
6266                )
6267            self._legacyUnforgeable = True
6268        elif identifier == "SameObject":
6269            raise WebIDLError(
6270                "Methods must not be flagged as [SameObject]",
6271                [attr.location, self.location],
6272            )
6273        elif identifier == "Constant":
6274            raise WebIDLError(
6275                "Methods must not be flagged as [Constant]",
6276                [attr.location, self.location],
6277            )
6278        elif identifier == "PutForwards":
6279            raise WebIDLError(
6280                "Only attributes support [PutForwards]", [attr.location, self.location]
6281            )
6282        elif identifier == "LegacyLenientSetter":
6283            raise WebIDLError(
6284                "Only attributes support [LegacyLenientSetter]",
6285                [attr.location, self.location],
6286            )
6287        elif identifier == "LenientFloat":
6288            # This is called before we've done overload resolution
6289            overloads = self._overloads
6290            assert len(overloads) == 1
6291            if not overloads[0].returnType.isVoid():
6292                raise WebIDLError(
6293                    "[LenientFloat] used on a non-void method",
6294                    [attr.location, self.location],
6295                )
6296            if not overloads[0].includesRestrictedFloatArgument():
6297                raise WebIDLError(
6298                    "[LenientFloat] used on an operation with no "
6299                    "restricted float type arguments",
6300                    [attr.location, self.location],
6301                )
6302        elif identifier == "Exposed":
6303            convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
6304        elif (
6305            identifier == "CrossOriginCallable"
6306            or identifier == "WebGLHandlesContextLoss"
6307        ):
6308            # Known no-argument attributes.
6309            if not attr.noArguments():
6310                raise WebIDLError(
6311                    "[%s] must take no arguments" % identifier, [attr.location]
6312                )
6313            if identifier == "CrossOriginCallable" and self.isStatic():
6314                raise WebIDLError(
6315                    "[CrossOriginCallable] is only allowed on non-static " "attributes",
6316                    [attr.location, self.location],
6317                )
6318        elif identifier == "Pure":
6319            if not attr.noArguments():
6320                raise WebIDLError("[Pure] must take no arguments", [attr.location])
6321            self._setDependsOn("DOMState")
6322            self._setAffects("Nothing")
6323        elif identifier == "Affects":
6324            if not attr.hasValue():
6325                raise WebIDLError("[Affects] takes an identifier", [attr.location])
6326            self._setAffects(attr.value())
6327        elif identifier == "DependsOn":
6328            if not attr.hasValue():
6329                raise WebIDLError("[DependsOn] takes an identifier", [attr.location])
6330            self._setDependsOn(attr.value())
6331        elif identifier == "Alias":
6332            if not attr.hasValue():
6333                raise WebIDLError(
6334                    "[Alias] takes an identifier or string", [attr.location]
6335                )
6336            self._addAlias(attr.value())
6337        elif identifier == "UseCounter":
6338            if self.isSpecial():
6339                raise WebIDLError(
6340                    "[UseCounter] must not be used on a special " "operation",
6341                    [attr.location, self.location],
6342                )
6343        elif identifier == "Unscopable":
6344            if not attr.noArguments():
6345                raise WebIDLError(
6346                    "[Unscopable] must take no arguments", [attr.location]
6347                )
6348            if self.isStatic():
6349                raise WebIDLError(
6350                    "[Unscopable] is only allowed on non-static "
6351                    "attributes and operations",
6352                    [attr.location, self.location],
6353                )
6354        elif identifier == "CEReactions":
6355            if not attr.noArguments():
6356                raise WebIDLError(
6357                    "[CEReactions] must take no arguments", [attr.location]
6358                )
6359
6360            if self.isSpecial() and not self.isSetter() and not self.isDeleter():
6361                raise WebIDLError(
6362                    "[CEReactions] is only allowed on operation, "
6363                    "attribute, setter, and deleter",
6364                    [attr.location, self.location],
6365                )
6366        elif identifier == "Default":
6367            if not attr.noArguments():
6368                raise WebIDLError("[Default] must take no arguments", [attr.location])
6369
6370            if not self.isToJSON():
6371                raise WebIDLError(
6372                    "[Default] is only allowed on toJSON operations",
6373                    [attr.location, self.location],
6374                )
6375
6376            if self.signatures()[0][0] != BuiltinTypes[IDLBuiltinType.Types.object]:
6377                raise WebIDLError(
6378                    "The return type of the default toJSON "
6379                    "operation must be 'object'",
6380                    [attr.location, self.location],
6381                )
6382        elif (
6383            identifier == "Throws"
6384            or identifier == "CanOOM"
6385            or identifier == "NewObject"
6386            or identifier == "ChromeOnly"
6387            or identifier == "Pref"
6388            or identifier == "Deprecated"
6389            or identifier == "Func"
6390            or identifier == "SecureContext"
6391            or identifier == "BinaryName"
6392            or identifier == "NeedsSubjectPrincipal"
6393            or identifier == "NeedsCallerType"
6394            or identifier == "StaticClassOverride"
6395            or identifier == "NonEnumerable"
6396            or identifier == "Unexposed"
6397            or identifier == "WebExtensionStub"
6398        ):
6399            # Known attributes that we don't need to do anything with here
6400            pass
6401        else:
6402            raise WebIDLError(
6403                "Unknown extended attribute %s on method" % identifier, [attr.location]
6404            )
6405        IDLInterfaceMember.handleExtendedAttribute(self, attr)
6406
6407    def returnsPromise(self):
6408        return self._overloads[0].returnType.isPromise()
6409
6410    def isLegacyUnforgeable(self):
6411        return self._legacyUnforgeable
6412
6413    def _getDependentObjects(self):
6414        deps = set()
6415        for overload in self._overloads:
6416            deps.update(overload._getDependentObjects())
6417        return deps
6418
6419
6420class IDLConstructor(IDLMethod):
6421    def __init__(self, location, args, name):
6422        # We can't actually init our IDLMethod yet, because we do not know the
6423        # return type yet.  Just save the info we have for now and we will init
6424        # it later.
6425        self._initLocation = location
6426        self._initArgs = args
6427        self._initName = name
6428        self._inited = False
6429        self._initExtendedAttrs = []
6430
6431    def addExtendedAttributes(self, attrs):
6432        if self._inited:
6433            return IDLMethod.addExtendedAttributes(self, attrs)
6434        self._initExtendedAttrs.extend(attrs)
6435
6436    def handleExtendedAttribute(self, attr):
6437        identifier = attr.identifier()
6438        if (
6439            identifier == "BinaryName"
6440            or identifier == "ChromeOnly"
6441            or identifier == "NewObject"
6442            or identifier == "SecureContext"
6443            or identifier == "Throws"
6444            or identifier == "Func"
6445            or identifier == "Pref"
6446        ):
6447            IDLMethod.handleExtendedAttribute(self, attr)
6448        elif identifier == "HTMLConstructor":
6449            if not attr.noArguments():
6450                raise WebIDLError(
6451                    "[HTMLConstructor] must take no arguments", [attr.location]
6452                )
6453            # We shouldn't end up here for legacy factory functions.
6454            assert self.identifier.name == "constructor"
6455
6456            if any(len(sig[1]) != 0 for sig in self.signatures()):
6457                raise WebIDLError(
6458                    "[HTMLConstructor] must not be applied to a "
6459                    "constructor operation that has arguments.",
6460                    [attr.location],
6461                )
6462            self._htmlConstructor = True
6463        else:
6464            raise WebIDLError(
6465                "Unknown extended attribute %s on method" % identifier, [attr.location]
6466            )
6467
6468    def reallyInit(self, parentInterface):
6469        name = self._initName
6470        location = self._initLocation
6471        identifier = IDLUnresolvedIdentifier(location, name, allowForbidden=True)
6472        retType = IDLWrapperType(parentInterface.location, parentInterface)
6473        IDLMethod.__init__(
6474            self, location, identifier, retType, self._initArgs, static=True
6475        )
6476        self._inited = True
6477        # Propagate through whatever extended attributes we already had
6478        self.addExtendedAttributes(self._initExtendedAttrs)
6479        self._initExtendedAttrs = []
6480        # Constructors are always NewObject.  Whether they throw or not is
6481        # indicated by [Throws] annotations in the usual way.
6482        self.addExtendedAttributes(
6483            [IDLExtendedAttribute(self.location, ("NewObject",))]
6484        )
6485
6486
6487class IDLIncludesStatement(IDLObject):
6488    def __init__(self, location, interface, mixin):
6489        IDLObject.__init__(self, location)
6490        self.interface = interface
6491        self.mixin = mixin
6492        self._finished = False
6493
6494    def finish(self, scope):
6495        if self._finished:
6496            return
6497        self._finished = True
6498        assert isinstance(self.interface, IDLIdentifierPlaceholder)
6499        assert isinstance(self.mixin, IDLIdentifierPlaceholder)
6500        interface = self.interface.finish(scope)
6501        mixin = self.mixin.finish(scope)
6502        # NOTE: we depend on not setting self.interface and
6503        # self.mixin here to keep track of the original
6504        # locations.
6505        if not isinstance(interface, IDLInterface):
6506            raise WebIDLError(
6507                "Left-hand side of 'includes' is not an " "interface",
6508                [self.interface.location, interface.location],
6509            )
6510        if interface.isCallback():
6511            raise WebIDLError(
6512                "Left-hand side of 'includes' is a callback " "interface",
6513                [self.interface.location, interface.location],
6514            )
6515        if not isinstance(mixin, IDLInterfaceMixin):
6516            raise WebIDLError(
6517                "Right-hand side of 'includes' is not an " "interface mixin",
6518                [self.mixin.location, mixin.location],
6519            )
6520
6521        mixin.actualExposureGlobalNames.update(interface._exposureGlobalNames)
6522
6523        interface.addIncludedMixin(mixin)
6524        self.interface = interface
6525        self.mixin = mixin
6526
6527    def validate(self):
6528        pass
6529
6530    def addExtendedAttributes(self, attrs):
6531        if len(attrs) != 0:
6532            raise WebIDLError(
6533                "There are no extended attributes that are "
6534                "allowed on includes statements",
6535                [attrs[0].location, self.location],
6536            )
6537
6538
6539class IDLExtendedAttribute(IDLObject):
6540    """
6541    A class to represent IDL extended attributes so we can give them locations
6542    """
6543
6544    def __init__(self, location, tuple):
6545        IDLObject.__init__(self, location)
6546        self._tuple = tuple
6547
6548    def identifier(self):
6549        return self._tuple[0]
6550
6551    def noArguments(self):
6552        return len(self._tuple) == 1
6553
6554    def hasValue(self):
6555        return len(self._tuple) >= 2 and isinstance(self._tuple[1], str)
6556
6557    def value(self):
6558        assert self.hasValue()
6559        return self._tuple[1]
6560
6561    def hasArgs(self):
6562        return (
6563            len(self._tuple) == 2
6564            and isinstance(self._tuple[1], list)
6565            or len(self._tuple) == 3
6566        )
6567
6568    def args(self):
6569        assert self.hasArgs()
6570        # Our args are our last element
6571        return self._tuple[-1]
6572
6573    def listValue(self):
6574        """
6575        Backdoor for storing random data in _extendedAttrDict
6576        """
6577        return list(self._tuple)[1:]
6578
6579
6580# Parser
6581
6582
6583class Tokenizer(object):
6584    tokens = ["INTEGER", "FLOATLITERAL", "IDENTIFIER", "STRING", "WHITESPACE", "OTHER"]
6585
6586    def t_FLOATLITERAL(self, t):
6587        r"(-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+|Infinity))|NaN"
6588        t.value = float(t.value)
6589        return t
6590
6591    def t_INTEGER(self, t):
6592        r"-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)"
6593        try:
6594            # Can't use int(), because that doesn't handle octal properly.
6595            t.value = parseInt(t.value)
6596        except:
6597            raise WebIDLError(
6598                "Invalid integer literal",
6599                [
6600                    Location(
6601                        lexer=self.lexer,
6602                        lineno=self.lexer.lineno,
6603                        lexpos=self.lexer.lexpos,
6604                        filename=self._filename,
6605                    )
6606                ],
6607            )
6608        return t
6609
6610    def t_IDENTIFIER(self, t):
6611        r"[_-]?[A-Za-z][0-9A-Z_a-z-]*"
6612        t.type = self.keywords.get(t.value, "IDENTIFIER")
6613        return t
6614
6615    def t_STRING(self, t):
6616        r'"[^"]*"'
6617        t.value = t.value[1:-1]
6618        return t
6619
6620    def t_WHITESPACE(self, t):
6621        r"[\t\n\r ]+|[\t\n\r ]*((//[^\n]*|/\*.*?\*/)[\t\n\r ]*)+"
6622        pass
6623
6624    def t_ELLIPSIS(self, t):
6625        r"\.\.\."
6626        t.type = self.keywords.get(t.value)
6627        return t
6628
6629    def t_OTHER(self, t):
6630        r"[^\t\n\r 0-9A-Z_a-z]"
6631        t.type = self.keywords.get(t.value, "OTHER")
6632        return t
6633
6634    keywords = {
6635        "interface": "INTERFACE",
6636        "partial": "PARTIAL",
6637        "mixin": "MIXIN",
6638        "dictionary": "DICTIONARY",
6639        "exception": "EXCEPTION",
6640        "enum": "ENUM",
6641        "callback": "CALLBACK",
6642        "typedef": "TYPEDEF",
6643        "includes": "INCLUDES",
6644        "const": "CONST",
6645        "null": "NULL",
6646        "true": "TRUE",
6647        "false": "FALSE",
6648        "serializer": "SERIALIZER",
6649        "stringifier": "STRINGIFIER",
6650        "unrestricted": "UNRESTRICTED",
6651        "attribute": "ATTRIBUTE",
6652        "readonly": "READONLY",
6653        "inherit": "INHERIT",
6654        "static": "STATIC",
6655        "getter": "GETTER",
6656        "setter": "SETTER",
6657        "deleter": "DELETER",
6658        "legacycaller": "LEGACYCALLER",
6659        "optional": "OPTIONAL",
6660        "...": "ELLIPSIS",
6661        "::": "SCOPE",
6662        "DOMString": "DOMSTRING",
6663        "ByteString": "BYTESTRING",
6664        "USVString": "USVSTRING",
6665        "JSString": "JSSTRING",
6666        "UTF8String": "UTF8STRING",
6667        "any": "ANY",
6668        "boolean": "BOOLEAN",
6669        "byte": "BYTE",
6670        "double": "DOUBLE",
6671        "float": "FLOAT",
6672        "long": "LONG",
6673        "object": "OBJECT",
6674        "octet": "OCTET",
6675        "Promise": "PROMISE",
6676        "required": "REQUIRED",
6677        "sequence": "SEQUENCE",
6678        "record": "RECORD",
6679        "short": "SHORT",
6680        "unsigned": "UNSIGNED",
6681        "void": "VOID",
6682        ":": "COLON",
6683        ";": "SEMICOLON",
6684        "{": "LBRACE",
6685        "}": "RBRACE",
6686        "(": "LPAREN",
6687        ")": "RPAREN",
6688        "[": "LBRACKET",
6689        "]": "RBRACKET",
6690        "?": "QUESTIONMARK",
6691        ",": "COMMA",
6692        "=": "EQUALS",
6693        "<": "LT",
6694        ">": "GT",
6695        "ArrayBuffer": "ARRAYBUFFER",
6696        "or": "OR",
6697        "maplike": "MAPLIKE",
6698        "setlike": "SETLIKE",
6699        "iterable": "ITERABLE",
6700        "namespace": "NAMESPACE",
6701        "ReadableStream": "READABLESTREAM",
6702        "constructor": "CONSTRUCTOR",
6703        "symbol": "SYMBOL",
6704        "async": "ASYNC",
6705    }
6706
6707    tokens.extend(keywords.values())
6708
6709    def t_error(self, t):
6710        raise WebIDLError(
6711            "Unrecognized Input",
6712            [
6713                Location(
6714                    lexer=self.lexer,
6715                    lineno=self.lexer.lineno,
6716                    lexpos=self.lexer.lexpos,
6717                    filename=self.filename,
6718                )
6719            ],
6720        )
6721
6722    def __init__(self, outputdir, lexer=None):
6723        if lexer:
6724            self.lexer = lexer
6725        else:
6726            self.lexer = lex.lex(object=self, reflags=re.DOTALL)
6727
6728
6729class SqueakyCleanLogger(object):
6730    errorWhitelist = [
6731        # Web IDL defines the WHITESPACE token, but doesn't actually
6732        # use it ... so far.
6733        "Token 'WHITESPACE' defined, but not used",
6734        # And that means we have an unused token
6735        "There is 1 unused token",
6736        # Web IDL defines a OtherOrComma rule that's only used in
6737        # ExtendedAttributeInner, which we don't use yet.
6738        "Rule 'OtherOrComma' defined, but not used",
6739        # And an unused rule
6740        "There is 1 unused rule",
6741        # And the OtherOrComma grammar symbol is unreachable.
6742        "Symbol 'OtherOrComma' is unreachable",
6743        # Which means the Other symbol is unreachable.
6744        "Symbol 'Other' is unreachable",
6745    ]
6746
6747    def __init__(self):
6748        self.errors = []
6749
6750    def debug(self, msg, *args, **kwargs):
6751        pass
6752
6753    info = debug
6754
6755    def warning(self, msg, *args, **kwargs):
6756        if (
6757            msg == "%s:%d: Rule %r defined, but not used"
6758            or msg == "%s:%d: Rule '%s' defined, but not used"
6759        ):
6760            # Munge things so we don't have to hardcode filenames and
6761            # line numbers in our whitelist.
6762            whitelistmsg = "Rule %r defined, but not used"
6763            whitelistargs = args[2:]
6764        else:
6765            whitelistmsg = msg
6766            whitelistargs = args
6767        if (whitelistmsg % whitelistargs) not in SqueakyCleanLogger.errorWhitelist:
6768            self.errors.append(msg % args)
6769
6770    error = warning
6771
6772    def reportGrammarErrors(self):
6773        if self.errors:
6774            raise WebIDLError("\n".join(self.errors), [])
6775
6776
6777class Parser(Tokenizer):
6778    def getLocation(self, p, i):
6779        return Location(self.lexer, p.lineno(i), p.lexpos(i), self._filename)
6780
6781    def globalScope(self):
6782        return self._globalScope
6783
6784    # The p_Foo functions here must match the WebIDL spec's grammar.
6785    # It's acceptable to split things at '|' boundaries.
6786    def p_Definitions(self, p):
6787        """
6788        Definitions : ExtendedAttributeList Definition Definitions
6789        """
6790        if p[2]:
6791            p[0] = [p[2]]
6792            p[2].addExtendedAttributes(p[1])
6793        else:
6794            assert not p[1]
6795            p[0] = []
6796
6797        p[0].extend(p[3])
6798
6799    def p_DefinitionsEmpty(self, p):
6800        """
6801        Definitions :
6802        """
6803        p[0] = []
6804
6805    def p_Definition(self, p):
6806        """
6807        Definition : CallbackOrInterfaceOrMixin
6808                   | Namespace
6809                   | Partial
6810                   | Dictionary
6811                   | Exception
6812                   | Enum
6813                   | Typedef
6814                   | IncludesStatement
6815        """
6816        p[0] = p[1]
6817        assert p[1]  # We might not have implemented something ...
6818
6819    def p_CallbackOrInterfaceOrMixinCallback(self, p):
6820        """
6821        CallbackOrInterfaceOrMixin : CALLBACK CallbackRestOrInterface
6822        """
6823        if p[2].isInterface():
6824            assert isinstance(p[2], IDLInterface)
6825            p[2].setCallback(True)
6826
6827        p[0] = p[2]
6828
6829    def p_CallbackOrInterfaceOrMixinInterfaceOrMixin(self, p):
6830        """
6831        CallbackOrInterfaceOrMixin : INTERFACE InterfaceOrMixin
6832        """
6833        p[0] = p[2]
6834
6835    def p_CallbackRestOrInterface(self, p):
6836        """
6837        CallbackRestOrInterface : CallbackRest
6838                                | CallbackConstructorRest
6839                                | CallbackInterface
6840        """
6841        assert p[1]
6842        p[0] = p[1]
6843
6844    def handleNonPartialObject(
6845        self, location, identifier, constructor, constructorArgs, nonPartialArgs
6846    ):
6847        """
6848        This handles non-partial objects (interfaces, namespaces and
6849        dictionaries) by checking for an existing partial object, and promoting
6850        it to non-partial as needed.  The return value is the non-partial
6851        object.
6852
6853        constructorArgs are all the args for the constructor except the last
6854        one: isKnownNonPartial.
6855
6856        nonPartialArgs are the args for the setNonPartial call.
6857        """
6858        # The name of the class starts with "IDL", so strip that off.
6859        # Also, starts with a capital letter after that, so nix that
6860        # as well.
6861        prettyname = constructor.__name__[3:].lower()
6862
6863        try:
6864            existingObj = self.globalScope()._lookupIdentifier(identifier)
6865            if existingObj:
6866                if not isinstance(existingObj, constructor):
6867                    raise WebIDLError(
6868                        "%s has the same name as "
6869                        "non-%s object" % (prettyname.capitalize(), prettyname),
6870                        [location, existingObj.location],
6871                    )
6872                existingObj.setNonPartial(*nonPartialArgs)
6873                return existingObj
6874        except Exception as ex:
6875            if isinstance(ex, WebIDLError):
6876                raise ex
6877            pass
6878
6879        # True for isKnownNonPartial
6880        return constructor(*(constructorArgs + [True]))
6881
6882    def p_InterfaceOrMixin(self, p):
6883        """
6884        InterfaceOrMixin : InterfaceRest
6885                         | MixinRest
6886        """
6887        p[0] = p[1]
6888
6889    def p_CallbackInterface(self, p):
6890        """
6891        CallbackInterface : INTERFACE InterfaceRest
6892        """
6893        p[0] = p[2]
6894
6895    def p_InterfaceRest(self, p):
6896        """
6897        InterfaceRest : IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON
6898        """
6899        location = self.getLocation(p, 1)
6900        identifier = IDLUnresolvedIdentifier(location, p[1])
6901        members = p[4]
6902        parent = p[2]
6903
6904        p[0] = self.handleNonPartialObject(
6905            location,
6906            identifier,
6907            IDLInterface,
6908            [location, self.globalScope(), identifier, parent, members],
6909            [location, parent, members],
6910        )
6911
6912    def p_InterfaceForwardDecl(self, p):
6913        """
6914        InterfaceRest : IDENTIFIER SEMICOLON
6915        """
6916        location = self.getLocation(p, 1)
6917        identifier = IDLUnresolvedIdentifier(location, p[1])
6918
6919        try:
6920            if self.globalScope()._lookupIdentifier(identifier):
6921                p[0] = self.globalScope()._lookupIdentifier(identifier)
6922                if not isinstance(p[0], IDLExternalInterface):
6923                    raise WebIDLError(
6924                        "Name collision between external "
6925                        "interface declaration for identifier "
6926                        "%s and %s" % (identifier.name, p[0]),
6927                        [location, p[0].location],
6928                    )
6929                return
6930        except Exception as ex:
6931            if isinstance(ex, WebIDLError):
6932                raise ex
6933            pass
6934
6935        p[0] = IDLExternalInterface(location, self.globalScope(), identifier)
6936
6937    def p_MixinRest(self, p):
6938        """
6939        MixinRest : MIXIN IDENTIFIER LBRACE MixinMembers RBRACE SEMICOLON
6940        """
6941        location = self.getLocation(p, 1)
6942        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
6943        members = p[4]
6944
6945        p[0] = self.handleNonPartialObject(
6946            location,
6947            identifier,
6948            IDLInterfaceMixin,
6949            [location, self.globalScope(), identifier, members],
6950            [location, members],
6951        )
6952
6953    def p_Namespace(self, p):
6954        """
6955        Namespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
6956        """
6957        location = self.getLocation(p, 1)
6958        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
6959        members = p[4]
6960
6961        p[0] = self.handleNonPartialObject(
6962            location,
6963            identifier,
6964            IDLNamespace,
6965            [location, self.globalScope(), identifier, members],
6966            [location, None, members],
6967        )
6968
6969    def p_Partial(self, p):
6970        """
6971        Partial : PARTIAL PartialDefinition
6972        """
6973        p[0] = p[2]
6974
6975    def p_PartialDefinitionInterface(self, p):
6976        """
6977        PartialDefinition : INTERFACE PartialInterfaceOrPartialMixin
6978        """
6979        p[0] = p[2]
6980
6981    def p_PartialDefinition(self, p):
6982        """
6983        PartialDefinition : PartialNamespace
6984                          | PartialDictionary
6985        """
6986        p[0] = p[1]
6987
6988    def handlePartialObject(
6989        self,
6990        location,
6991        identifier,
6992        nonPartialConstructor,
6993        nonPartialConstructorArgs,
6994        partialConstructorArgs,
6995    ):
6996        """
6997        This handles partial objects (interfaces, namespaces and dictionaries)
6998        by checking for an existing non-partial object, and adding ourselves to
6999        it as needed.  The return value is our partial object.  We use
7000        IDLPartialInterfaceOrNamespace for partial interfaces or namespaces,
7001        and IDLPartialDictionary for partial dictionaries.
7002
7003        nonPartialConstructorArgs are all the args for the non-partial
7004        constructor except the last two: members and isKnownNonPartial.
7005
7006        partialConstructorArgs are the arguments for the partial object
7007        constructor, except the last one (the non-partial object).
7008        """
7009        # The name of the class starts with "IDL", so strip that off.
7010        # Also, starts with a capital letter after that, so nix that
7011        # as well.
7012        prettyname = nonPartialConstructor.__name__[3:].lower()
7013
7014        nonPartialObject = None
7015        try:
7016            nonPartialObject = self.globalScope()._lookupIdentifier(identifier)
7017            if nonPartialObject:
7018                if not isinstance(nonPartialObject, nonPartialConstructor):
7019                    raise WebIDLError(
7020                        "Partial %s has the same name as "
7021                        "non-%s object" % (prettyname, prettyname),
7022                        [location, nonPartialObject.location],
7023                    )
7024        except Exception as ex:
7025            if isinstance(ex, WebIDLError):
7026                raise ex
7027            pass
7028
7029        if not nonPartialObject:
7030            nonPartialObject = nonPartialConstructor(
7031                # No members, False for isKnownNonPartial
7032                *(nonPartialConstructorArgs),
7033                members=[],
7034                isKnownNonPartial=False
7035            )
7036
7037        partialObject = None
7038        if isinstance(nonPartialObject, IDLDictionary):
7039            partialObject = IDLPartialDictionary(
7040                *(partialConstructorArgs + [nonPartialObject])
7041            )
7042        elif isinstance(
7043            nonPartialObject, (IDLInterface, IDLInterfaceMixin, IDLNamespace)
7044        ):
7045            partialObject = IDLPartialInterfaceOrNamespace(
7046                *(partialConstructorArgs + [nonPartialObject])
7047            )
7048        else:
7049            raise WebIDLError(
7050                "Unknown partial object type %s" % type(partialObject), [location]
7051            )
7052
7053        return partialObject
7054
7055    def p_PartialInterfaceOrPartialMixin(self, p):
7056        """
7057        PartialInterfaceOrPartialMixin : PartialInterfaceRest
7058                                       | PartialMixinRest
7059        """
7060        p[0] = p[1]
7061
7062    def p_PartialInterfaceRest(self, p):
7063        """
7064        PartialInterfaceRest : IDENTIFIER LBRACE PartialInterfaceMembers RBRACE SEMICOLON
7065        """
7066        location = self.getLocation(p, 1)
7067        identifier = IDLUnresolvedIdentifier(location, p[1])
7068        members = p[3]
7069
7070        p[0] = self.handlePartialObject(
7071            location,
7072            identifier,
7073            IDLInterface,
7074            [location, self.globalScope(), identifier, None],
7075            [location, identifier, members],
7076        )
7077
7078    def p_PartialMixinRest(self, p):
7079        """
7080        PartialMixinRest : MIXIN IDENTIFIER LBRACE MixinMembers RBRACE SEMICOLON
7081        """
7082        location = self.getLocation(p, 1)
7083        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
7084        members = p[4]
7085
7086        p[0] = self.handlePartialObject(
7087            location,
7088            identifier,
7089            IDLInterfaceMixin,
7090            [location, self.globalScope(), identifier],
7091            [location, identifier, members],
7092        )
7093
7094    def p_PartialNamespace(self, p):
7095        """
7096        PartialNamespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
7097        """
7098        location = self.getLocation(p, 1)
7099        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
7100        members = p[4]
7101
7102        p[0] = self.handlePartialObject(
7103            location,
7104            identifier,
7105            IDLNamespace,
7106            [location, self.globalScope(), identifier],
7107            [location, identifier, members],
7108        )
7109
7110    def p_PartialDictionary(self, p):
7111        """
7112        PartialDictionary : DICTIONARY IDENTIFIER LBRACE DictionaryMembers RBRACE SEMICOLON
7113        """
7114        location = self.getLocation(p, 1)
7115        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
7116        members = p[4]
7117
7118        p[0] = self.handlePartialObject(
7119            location,
7120            identifier,
7121            IDLDictionary,
7122            [location, self.globalScope(), identifier],
7123            [location, identifier, members],
7124        )
7125
7126    def p_Inheritance(self, p):
7127        """
7128        Inheritance : COLON ScopedName
7129        """
7130        p[0] = IDLIdentifierPlaceholder(self.getLocation(p, 2), p[2])
7131
7132    def p_InheritanceEmpty(self, p):
7133        """
7134        Inheritance :
7135        """
7136        pass
7137
7138    def p_InterfaceMembers(self, p):
7139        """
7140        InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
7141        """
7142        p[0] = [p[2]]
7143
7144        assert not p[1] or p[2]
7145        p[2].addExtendedAttributes(p[1])
7146
7147        p[0].extend(p[3])
7148
7149    def p_InterfaceMembersEmpty(self, p):
7150        """
7151        InterfaceMembers :
7152        """
7153        p[0] = []
7154
7155    def p_InterfaceMember(self, p):
7156        """
7157        InterfaceMember : PartialInterfaceMember
7158                        | Constructor
7159        """
7160        p[0] = p[1]
7161
7162    def p_Constructor(self, p):
7163        """
7164        Constructor : CONSTRUCTOR LPAREN ArgumentList RPAREN SEMICOLON
7165        """
7166        p[0] = IDLConstructor(self.getLocation(p, 1), p[3], "constructor")
7167
7168    def p_PartialInterfaceMembers(self, p):
7169        """
7170        PartialInterfaceMembers : ExtendedAttributeList PartialInterfaceMember PartialInterfaceMembers
7171        """
7172        p[0] = [p[2]]
7173
7174        assert not p[1] or p[2]
7175        p[2].addExtendedAttributes(p[1])
7176
7177        p[0].extend(p[3])
7178
7179    def p_PartialInterfaceMembersEmpty(self, p):
7180        """
7181        PartialInterfaceMembers :
7182        """
7183        p[0] = []
7184
7185    def p_PartialInterfaceMember(self, p):
7186        """
7187        PartialInterfaceMember : Const
7188                               | AttributeOrOperationOrMaplikeOrSetlikeOrIterable
7189        """
7190        p[0] = p[1]
7191
7192    def p_MixinMembersEmpty(self, p):
7193        """
7194        MixinMembers :
7195        """
7196        p[0] = []
7197
7198    def p_MixinMembers(self, p):
7199        """
7200        MixinMembers : ExtendedAttributeList MixinMember MixinMembers
7201        """
7202        p[0] = [p[2]]
7203
7204        assert not p[1] or p[2]
7205        p[2].addExtendedAttributes(p[1])
7206
7207        p[0].extend(p[3])
7208
7209    def p_MixinMember(self, p):
7210        """
7211        MixinMember : Const
7212                    | Attribute
7213                    | Operation
7214        """
7215        p[0] = p[1]
7216
7217    def p_Dictionary(self, p):
7218        """
7219        Dictionary : DICTIONARY IDENTIFIER Inheritance LBRACE DictionaryMembers RBRACE SEMICOLON
7220        """
7221        location = self.getLocation(p, 1)
7222        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
7223        members = p[5]
7224        p[0] = IDLDictionary(location, self.globalScope(), identifier, p[3], members)
7225
7226    def p_DictionaryMembers(self, p):
7227        """
7228        DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers
7229                         |
7230        """
7231        if len(p) == 1:
7232            # We're at the end of the list
7233            p[0] = []
7234            return
7235        p[2].addExtendedAttributes(p[1])
7236        p[0] = [p[2]]
7237        p[0].extend(p[3])
7238
7239    def p_DictionaryMemberRequired(self, p):
7240        """
7241        DictionaryMember : REQUIRED TypeWithExtendedAttributes IDENTIFIER SEMICOLON
7242        """
7243        # These quack a lot like required arguments, so just treat them that way.
7244        t = p[2]
7245        assert isinstance(t, IDLType)
7246        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
7247
7248        p[0] = IDLArgument(
7249            self.getLocation(p, 3),
7250            identifier,
7251            t,
7252            optional=False,
7253            defaultValue=None,
7254            variadic=False,
7255            dictionaryMember=True,
7256        )
7257
7258    def p_DictionaryMember(self, p):
7259        """
7260        DictionaryMember : Type IDENTIFIER Default SEMICOLON
7261        """
7262        # These quack a lot like optional arguments, so just treat them that way.
7263        t = p[1]
7264        assert isinstance(t, IDLType)
7265        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
7266        defaultValue = p[3]
7267
7268        # Any attributes that precede this may apply to the type, so
7269        # we configure the argument to forward type attributes down instead of producing
7270        # a parse error
7271        p[0] = IDLArgument(
7272            self.getLocation(p, 2),
7273            identifier,
7274            t,
7275            optional=True,
7276            defaultValue=defaultValue,
7277            variadic=False,
7278            dictionaryMember=True,
7279            allowTypeAttributes=True,
7280        )
7281
7282    def p_Default(self, p):
7283        """
7284        Default : EQUALS DefaultValue
7285                |
7286        """
7287        if len(p) > 1:
7288            p[0] = p[2]
7289        else:
7290            p[0] = None
7291
7292    def p_DefaultValue(self, p):
7293        """
7294        DefaultValue : ConstValue
7295                     | LBRACKET RBRACKET
7296                     | LBRACE RBRACE
7297        """
7298        if len(p) == 2:
7299            p[0] = p[1]
7300        else:
7301            assert len(p) == 3  # Must be [] or {}
7302            if p[1] == "[":
7303                p[0] = IDLEmptySequenceValue(self.getLocation(p, 1))
7304            else:
7305                assert p[1] == "{"
7306                p[0] = IDLDefaultDictionaryValue(self.getLocation(p, 1))
7307
7308    def p_DefaultValueNull(self, p):
7309        """
7310        DefaultValue : NULL
7311        """
7312        p[0] = IDLNullValue(self.getLocation(p, 1))
7313
7314    def p_Exception(self, p):
7315        """
7316        Exception : EXCEPTION IDENTIFIER Inheritance LBRACE ExceptionMembers RBRACE SEMICOLON
7317        """
7318        pass
7319
7320    def p_Enum(self, p):
7321        """
7322        Enum : ENUM IDENTIFIER LBRACE EnumValueList RBRACE SEMICOLON
7323        """
7324        location = self.getLocation(p, 1)
7325        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
7326
7327        values = p[4]
7328        assert values
7329        p[0] = IDLEnum(location, self.globalScope(), identifier, values)
7330
7331    def p_EnumValueList(self, p):
7332        """
7333        EnumValueList : STRING EnumValueListComma
7334        """
7335        p[0] = [p[1]]
7336        p[0].extend(p[2])
7337
7338    def p_EnumValueListComma(self, p):
7339        """
7340        EnumValueListComma : COMMA EnumValueListString
7341        """
7342        p[0] = p[2]
7343
7344    def p_EnumValueListCommaEmpty(self, p):
7345        """
7346        EnumValueListComma :
7347        """
7348        p[0] = []
7349
7350    def p_EnumValueListString(self, p):
7351        """
7352        EnumValueListString : STRING EnumValueListComma
7353        """
7354        p[0] = [p[1]]
7355        p[0].extend(p[2])
7356
7357    def p_EnumValueListStringEmpty(self, p):
7358        """
7359        EnumValueListString :
7360        """
7361        p[0] = []
7362
7363    def p_CallbackRest(self, p):
7364        """
7365        CallbackRest : IDENTIFIER EQUALS ReturnType LPAREN ArgumentList RPAREN SEMICOLON
7366        """
7367        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
7368        p[0] = IDLCallback(
7369            self.getLocation(p, 1),
7370            self.globalScope(),
7371            identifier,
7372            p[3],
7373            p[5],
7374            isConstructor=False,
7375        )
7376
7377    def p_CallbackConstructorRest(self, p):
7378        """
7379        CallbackConstructorRest : CONSTRUCTOR IDENTIFIER EQUALS ReturnType LPAREN ArgumentList RPAREN SEMICOLON
7380        """
7381        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
7382        p[0] = IDLCallback(
7383            self.getLocation(p, 2),
7384            self.globalScope(),
7385            identifier,
7386            p[4],
7387            p[6],
7388            isConstructor=True,
7389        )
7390
7391    def p_ExceptionMembers(self, p):
7392        """
7393        ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers
7394                         |
7395        """
7396        pass
7397
7398    def p_Typedef(self, p):
7399        """
7400        Typedef : TYPEDEF TypeWithExtendedAttributes IDENTIFIER SEMICOLON
7401        """
7402        typedef = IDLTypedef(self.getLocation(p, 1), self.globalScope(), p[2], p[3])
7403        p[0] = typedef
7404
7405    def p_IncludesStatement(self, p):
7406        """
7407        IncludesStatement : ScopedName INCLUDES ScopedName SEMICOLON
7408        """
7409        assert p[2] == "includes"
7410        interface = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1])
7411        mixin = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3])
7412        p[0] = IDLIncludesStatement(self.getLocation(p, 1), interface, mixin)
7413
7414    def p_Const(self, p):
7415        """
7416        Const : CONST ConstType IDENTIFIER EQUALS ConstValue SEMICOLON
7417        """
7418        location = self.getLocation(p, 1)
7419        type = p[2]
7420        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
7421        value = p[5]
7422        p[0] = IDLConst(location, identifier, type, value)
7423
7424    def p_ConstValueBoolean(self, p):
7425        """
7426        ConstValue : BooleanLiteral
7427        """
7428        location = self.getLocation(p, 1)
7429        booleanType = BuiltinTypes[IDLBuiltinType.Types.boolean]
7430        p[0] = IDLValue(location, booleanType, p[1])
7431
7432    def p_ConstValueInteger(self, p):
7433        """
7434        ConstValue : INTEGER
7435        """
7436        location = self.getLocation(p, 1)
7437
7438        # We don't know ahead of time what type the integer literal is.
7439        # Determine the smallest type it could possibly fit in and use that.
7440        integerType = matchIntegerValueToType(p[1])
7441        if integerType is None:
7442            raise WebIDLError("Integer literal out of range", [location])
7443
7444        p[0] = IDLValue(location, integerType, p[1])
7445
7446    def p_ConstValueFloat(self, p):
7447        """
7448        ConstValue : FLOATLITERAL
7449        """
7450        location = self.getLocation(p, 1)
7451        p[0] = IDLValue(
7452            location, BuiltinTypes[IDLBuiltinType.Types.unrestricted_float], p[1]
7453        )
7454
7455    def p_ConstValueString(self, p):
7456        """
7457        ConstValue : STRING
7458        """
7459        location = self.getLocation(p, 1)
7460        stringType = BuiltinTypes[IDLBuiltinType.Types.domstring]
7461        p[0] = IDLValue(location, stringType, p[1])
7462
7463    def p_BooleanLiteralTrue(self, p):
7464        """
7465        BooleanLiteral : TRUE
7466        """
7467        p[0] = True
7468
7469    def p_BooleanLiteralFalse(self, p):
7470        """
7471        BooleanLiteral : FALSE
7472        """
7473        p[0] = False
7474
7475    def p_AttributeOrOperationOrMaplikeOrSetlikeOrIterable(self, p):
7476        """
7477        AttributeOrOperationOrMaplikeOrSetlikeOrIterable : Attribute
7478                                                         | Maplike
7479                                                         | Setlike
7480                                                         | Iterable
7481                                                         | Operation
7482        """
7483        p[0] = p[1]
7484
7485    def p_Iterable(self, p):
7486        """
7487        Iterable : ITERABLE LT TypeWithExtendedAttributes GT SEMICOLON
7488                 | ITERABLE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON
7489        """
7490        location = self.getLocation(p, 2)
7491        identifier = IDLUnresolvedIdentifier(
7492            location, "__iterable", allowDoubleUnderscore=True
7493        )
7494        if len(p) > 6:
7495            keyType = p[3]
7496            valueType = p[5]
7497        else:
7498            keyType = None
7499            valueType = p[3]
7500
7501        p[0] = IDLIterable(location, identifier, keyType, valueType, self.globalScope())
7502
7503    def p_Setlike(self, p):
7504        """
7505        Setlike : ReadOnly SETLIKE LT TypeWithExtendedAttributes GT SEMICOLON
7506        """
7507        readonly = p[1]
7508        maplikeOrSetlikeType = p[2]
7509        location = self.getLocation(p, 2)
7510        identifier = IDLUnresolvedIdentifier(
7511            location, "__setlike", allowDoubleUnderscore=True
7512        )
7513        keyType = p[4]
7514        valueType = keyType
7515        p[0] = IDLMaplikeOrSetlike(
7516            location, identifier, maplikeOrSetlikeType, readonly, keyType, valueType
7517        )
7518
7519    def p_Maplike(self, p):
7520        """
7521        Maplike : ReadOnly MAPLIKE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON
7522        """
7523        readonly = p[1]
7524        maplikeOrSetlikeType = p[2]
7525        location = self.getLocation(p, 2)
7526        identifier = IDLUnresolvedIdentifier(
7527            location, "__maplike", allowDoubleUnderscore=True
7528        )
7529        keyType = p[4]
7530        valueType = p[6]
7531        p[0] = IDLMaplikeOrSetlike(
7532            location, identifier, maplikeOrSetlikeType, readonly, keyType, valueType
7533        )
7534
7535    def p_AttributeWithQualifier(self, p):
7536        """
7537        Attribute : Qualifier AttributeRest
7538        """
7539        static = IDLInterfaceMember.Special.Static in p[1]
7540        stringifier = IDLInterfaceMember.Special.Stringifier in p[1]
7541        (location, identifier, type, readonly) = p[2]
7542        p[0] = IDLAttribute(
7543            location, identifier, type, readonly, static=static, stringifier=stringifier
7544        )
7545
7546    def p_AttributeInherited(self, p):
7547        """
7548        Attribute : INHERIT AttributeRest
7549        """
7550        (location, identifier, type, readonly) = p[2]
7551        p[0] = IDLAttribute(location, identifier, type, readonly, inherit=True)
7552
7553    def p_Attribute(self, p):
7554        """
7555        Attribute : AttributeRest
7556        """
7557        (location, identifier, type, readonly) = p[1]
7558        p[0] = IDLAttribute(location, identifier, type, readonly, inherit=False)
7559
7560    def p_AttributeRest(self, p):
7561        """
7562        AttributeRest : ReadOnly ATTRIBUTE TypeWithExtendedAttributes AttributeName SEMICOLON
7563        """
7564        location = self.getLocation(p, 2)
7565        readonly = p[1]
7566        t = p[3]
7567        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 4), p[4])
7568        p[0] = (location, identifier, t, readonly)
7569
7570    def p_ReadOnly(self, p):
7571        """
7572        ReadOnly : READONLY
7573        """
7574        p[0] = True
7575
7576    def p_ReadOnlyEmpty(self, p):
7577        """
7578        ReadOnly :
7579        """
7580        p[0] = False
7581
7582    def p_Operation(self, p):
7583        """
7584        Operation : Qualifiers OperationRest
7585        """
7586        qualifiers = p[1]
7587
7588        # Disallow duplicates in the qualifier set
7589        if not len(set(qualifiers)) == len(qualifiers):
7590            raise WebIDLError(
7591                "Duplicate qualifiers are not allowed", [self.getLocation(p, 1)]
7592            )
7593
7594        static = IDLInterfaceMember.Special.Static in p[1]
7595        # If static is there that's all that's allowed.  This is disallowed
7596        # by the parser, so we can assert here.
7597        assert not static or len(qualifiers) == 1
7598
7599        stringifier = IDLInterfaceMember.Special.Stringifier in p[1]
7600        # If stringifier is there that's all that's allowed.  This is disallowed
7601        # by the parser, so we can assert here.
7602        assert not stringifier or len(qualifiers) == 1
7603
7604        getter = True if IDLMethod.Special.Getter in p[1] else False
7605        setter = True if IDLMethod.Special.Setter in p[1] else False
7606        deleter = True if IDLMethod.Special.Deleter in p[1] else False
7607        legacycaller = True if IDLMethod.Special.LegacyCaller in p[1] else False
7608
7609        if getter or deleter:
7610            if setter:
7611                raise WebIDLError(
7612                    "getter and deleter are incompatible with setter",
7613                    [self.getLocation(p, 1)],
7614                )
7615
7616        (returnType, identifier, arguments) = p[2]
7617
7618        assert isinstance(returnType, IDLType)
7619
7620        specialType = IDLMethod.NamedOrIndexed.Neither
7621
7622        if getter or deleter:
7623            if len(arguments) != 1:
7624                raise WebIDLError(
7625                    "%s has wrong number of arguments"
7626                    % ("getter" if getter else "deleter"),
7627                    [self.getLocation(p, 2)],
7628                )
7629            argType = arguments[0].type
7630            if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
7631                specialType = IDLMethod.NamedOrIndexed.Named
7632            elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
7633                specialType = IDLMethod.NamedOrIndexed.Indexed
7634                if deleter:
7635                    raise WebIDLError(
7636                        "There is no such thing as an indexed deleter.",
7637                        [self.getLocation(p, 1)],
7638                    )
7639            else:
7640                raise WebIDLError(
7641                    "%s has wrong argument type (must be DOMString or UnsignedLong)"
7642                    % ("getter" if getter else "deleter"),
7643                    [arguments[0].location],
7644                )
7645            if arguments[0].optional or arguments[0].variadic:
7646                raise WebIDLError(
7647                    "%s cannot have %s argument"
7648                    % (
7649                        "getter" if getter else "deleter",
7650                        "optional" if arguments[0].optional else "variadic",
7651                    ),
7652                    [arguments[0].location],
7653                )
7654        if getter:
7655            if returnType.isVoid():
7656                raise WebIDLError(
7657                    "getter cannot have void return type", [self.getLocation(p, 2)]
7658                )
7659        if setter:
7660            if len(arguments) != 2:
7661                raise WebIDLError(
7662                    "setter has wrong number of arguments", [self.getLocation(p, 2)]
7663                )
7664            argType = arguments[0].type
7665            if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
7666                specialType = IDLMethod.NamedOrIndexed.Named
7667            elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
7668                specialType = IDLMethod.NamedOrIndexed.Indexed
7669            else:
7670                raise WebIDLError(
7671                    "settter has wrong argument type (must be DOMString or UnsignedLong)",
7672                    [arguments[0].location],
7673                )
7674            if arguments[0].optional or arguments[0].variadic:
7675                raise WebIDLError(
7676                    "setter cannot have %s argument"
7677                    % ("optional" if arguments[0].optional else "variadic"),
7678                    [arguments[0].location],
7679                )
7680            if arguments[1].optional or arguments[1].variadic:
7681                raise WebIDLError(
7682                    "setter cannot have %s argument"
7683                    % ("optional" if arguments[1].optional else "variadic"),
7684                    [arguments[1].location],
7685                )
7686
7687        if stringifier:
7688            if len(arguments) != 0:
7689                raise WebIDLError(
7690                    "stringifier has wrong number of arguments",
7691                    [self.getLocation(p, 2)],
7692                )
7693            if not returnType.isDOMString():
7694                raise WebIDLError(
7695                    "stringifier must have DOMString return type",
7696                    [self.getLocation(p, 2)],
7697                )
7698
7699        # identifier might be None.  This is only permitted for special methods.
7700        if not identifier:
7701            if (
7702                not getter
7703                and not setter
7704                and not deleter
7705                and not legacycaller
7706                and not stringifier
7707            ):
7708                raise WebIDLError(
7709                    "Identifier required for non-special methods",
7710                    [self.getLocation(p, 2)],
7711                )
7712
7713            location = BuiltinLocation("<auto-generated-identifier>")
7714            identifier = IDLUnresolvedIdentifier(
7715                location,
7716                "__%s%s%s%s%s%s"
7717                % (
7718                    "named"
7719                    if specialType == IDLMethod.NamedOrIndexed.Named
7720                    else "indexed"
7721                    if specialType == IDLMethod.NamedOrIndexed.Indexed
7722                    else "",
7723                    "getter" if getter else "",
7724                    "setter" if setter else "",
7725                    "deleter" if deleter else "",
7726                    "legacycaller" if legacycaller else "",
7727                    "stringifier" if stringifier else "",
7728                ),
7729                allowDoubleUnderscore=True,
7730            )
7731
7732        method = IDLMethod(
7733            self.getLocation(p, 2),
7734            identifier,
7735            returnType,
7736            arguments,
7737            static=static,
7738            getter=getter,
7739            setter=setter,
7740            deleter=deleter,
7741            specialType=specialType,
7742            legacycaller=legacycaller,
7743            stringifier=stringifier,
7744        )
7745        p[0] = method
7746
7747    def p_Stringifier(self, p):
7748        """
7749        Operation : STRINGIFIER SEMICOLON
7750        """
7751        identifier = IDLUnresolvedIdentifier(
7752            BuiltinLocation("<auto-generated-identifier>"),
7753            "__stringifier",
7754            allowDoubleUnderscore=True,
7755        )
7756        method = IDLMethod(
7757            self.getLocation(p, 1),
7758            identifier,
7759            returnType=BuiltinTypes[IDLBuiltinType.Types.domstring],
7760            arguments=[],
7761            stringifier=True,
7762        )
7763        p[0] = method
7764
7765    def p_QualifierStatic(self, p):
7766        """
7767        Qualifier : STATIC
7768        """
7769        p[0] = [IDLInterfaceMember.Special.Static]
7770
7771    def p_QualifierStringifier(self, p):
7772        """
7773        Qualifier : STRINGIFIER
7774        """
7775        p[0] = [IDLInterfaceMember.Special.Stringifier]
7776
7777    def p_Qualifiers(self, p):
7778        """
7779        Qualifiers : Qualifier
7780                   | Specials
7781        """
7782        p[0] = p[1]
7783
7784    def p_Specials(self, p):
7785        """
7786        Specials : Special Specials
7787        """
7788        p[0] = [p[1]]
7789        p[0].extend(p[2])
7790
7791    def p_SpecialsEmpty(self, p):
7792        """
7793        Specials :
7794        """
7795        p[0] = []
7796
7797    def p_SpecialGetter(self, p):
7798        """
7799        Special : GETTER
7800        """
7801        p[0] = IDLMethod.Special.Getter
7802
7803    def p_SpecialSetter(self, p):
7804        """
7805        Special : SETTER
7806        """
7807        p[0] = IDLMethod.Special.Setter
7808
7809    def p_SpecialDeleter(self, p):
7810        """
7811        Special : DELETER
7812        """
7813        p[0] = IDLMethod.Special.Deleter
7814
7815    def p_SpecialLegacyCaller(self, p):
7816        """
7817        Special : LEGACYCALLER
7818        """
7819        p[0] = IDLMethod.Special.LegacyCaller
7820
7821    def p_OperationRest(self, p):
7822        """
7823        OperationRest : ReturnType OptionalIdentifier LPAREN ArgumentList RPAREN SEMICOLON
7824        """
7825        p[0] = (p[1], p[2], p[4])
7826
7827    def p_OptionalIdentifier(self, p):
7828        """
7829        OptionalIdentifier : IDENTIFIER
7830        """
7831        p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
7832
7833    def p_OptionalIdentifierEmpty(self, p):
7834        """
7835        OptionalIdentifier :
7836        """
7837        pass
7838
7839    def p_ArgumentList(self, p):
7840        """
7841        ArgumentList : Argument Arguments
7842        """
7843        p[0] = [p[1]] if p[1] else []
7844        p[0].extend(p[2])
7845
7846    def p_ArgumentListEmpty(self, p):
7847        """
7848        ArgumentList :
7849        """
7850        p[0] = []
7851
7852    def p_Arguments(self, p):
7853        """
7854        Arguments : COMMA Argument Arguments
7855        """
7856        p[0] = [p[2]] if p[2] else []
7857        p[0].extend(p[3])
7858
7859    def p_ArgumentsEmpty(self, p):
7860        """
7861        Arguments :
7862        """
7863        p[0] = []
7864
7865    def p_Argument(self, p):
7866        """
7867        Argument : ExtendedAttributeList ArgumentRest
7868        """
7869        p[0] = p[2]
7870        p[0].addExtendedAttributes(p[1])
7871
7872    def p_ArgumentRestOptional(self, p):
7873        """
7874        ArgumentRest : OPTIONAL TypeWithExtendedAttributes ArgumentName Default
7875        """
7876        t = p[2]
7877        assert isinstance(t, IDLType)
7878        # Arg names can be reserved identifiers
7879        identifier = IDLUnresolvedIdentifier(
7880            self.getLocation(p, 3), p[3], allowForbidden=True
7881        )
7882
7883        defaultValue = p[4]
7884
7885        # We can't test t.isAny() here and give it a default value as needed,
7886        # since at this point t is not a fully resolved type yet (e.g. it might
7887        # be a typedef).  We'll handle the 'any' case in IDLArgument.complete.
7888
7889        p[0] = IDLArgument(
7890            self.getLocation(p, 3), identifier, t, True, defaultValue, False
7891        )
7892
7893    def p_ArgumentRest(self, p):
7894        """
7895        ArgumentRest : Type Ellipsis ArgumentName
7896        """
7897        t = p[1]
7898        assert isinstance(t, IDLType)
7899        # Arg names can be reserved identifiers
7900        identifier = IDLUnresolvedIdentifier(
7901            self.getLocation(p, 3), p[3], allowForbidden=True
7902        )
7903
7904        variadic = p[2]
7905
7906        # We can't test t.isAny() here and give it a default value as needed,
7907        # since at this point t is not a fully resolved type yet (e.g. it might
7908        # be a typedef).  We'll handle the 'any' case in IDLArgument.complete.
7909
7910        # variadic implies optional
7911        # Any attributes that precede this may apply to the type, so
7912        # we configure the argument to forward type attributes down instead of producing
7913        # a parse error
7914        p[0] = IDLArgument(
7915            self.getLocation(p, 3),
7916            identifier,
7917            t,
7918            variadic,
7919            None,
7920            variadic,
7921            allowTypeAttributes=True,
7922        )
7923
7924    def p_ArgumentName(self, p):
7925        """
7926        ArgumentName : IDENTIFIER
7927                     | ArgumentNameKeyword
7928        """
7929        p[0] = p[1]
7930
7931    def p_ArgumentNameKeyword(self, p):
7932        """
7933        ArgumentNameKeyword : ASYNC
7934                            | ATTRIBUTE
7935                            | CALLBACK
7936                            | CONST
7937                            | CONSTRUCTOR
7938                            | DELETER
7939                            | DICTIONARY
7940                            | ENUM
7941                            | EXCEPTION
7942                            | GETTER
7943                            | INCLUDES
7944                            | INHERIT
7945                            | INTERFACE
7946                            | ITERABLE
7947                            | LEGACYCALLER
7948                            | MAPLIKE
7949                            | MIXIN
7950                            | NAMESPACE
7951                            | PARTIAL
7952                            | READONLY
7953                            | REQUIRED
7954                            | SERIALIZER
7955                            | SETLIKE
7956                            | SETTER
7957                            | STATIC
7958                            | STRINGIFIER
7959                            | TYPEDEF
7960                            | UNRESTRICTED
7961        """
7962        p[0] = p[1]
7963
7964    def p_AttributeName(self, p):
7965        """
7966        AttributeName : IDENTIFIER
7967                      | AttributeNameKeyword
7968        """
7969        p[0] = p[1]
7970
7971    def p_AttributeNameKeyword(self, p):
7972        """
7973        AttributeNameKeyword : ASYNC
7974                             | REQUIRED
7975        """
7976        p[0] = p[1]
7977
7978    def p_Ellipsis(self, p):
7979        """
7980        Ellipsis : ELLIPSIS
7981        """
7982        p[0] = True
7983
7984    def p_EllipsisEmpty(self, p):
7985        """
7986        Ellipsis :
7987        """
7988        p[0] = False
7989
7990    def p_ExceptionMember(self, p):
7991        """
7992        ExceptionMember : Const
7993                        | ExceptionField
7994        """
7995        pass
7996
7997    def p_ExceptionField(self, p):
7998        """
7999        ExceptionField : Type IDENTIFIER SEMICOLON
8000        """
8001        pass
8002
8003    def p_ExtendedAttributeList(self, p):
8004        """
8005        ExtendedAttributeList : LBRACKET ExtendedAttribute ExtendedAttributes RBRACKET
8006        """
8007        p[0] = [p[2]]
8008        if p[3]:
8009            p[0].extend(p[3])
8010
8011    def p_ExtendedAttributeListEmpty(self, p):
8012        """
8013        ExtendedAttributeList :
8014        """
8015        p[0] = []
8016
8017    def p_ExtendedAttribute(self, p):
8018        """
8019        ExtendedAttribute : ExtendedAttributeNoArgs
8020                          | ExtendedAttributeArgList
8021                          | ExtendedAttributeIdent
8022                          | ExtendedAttributeNamedArgList
8023                          | ExtendedAttributeIdentList
8024        """
8025        p[0] = IDLExtendedAttribute(self.getLocation(p, 1), p[1])
8026
8027    def p_ExtendedAttributeEmpty(self, p):
8028        """
8029        ExtendedAttribute :
8030        """
8031        pass
8032
8033    def p_ExtendedAttributes(self, p):
8034        """
8035        ExtendedAttributes : COMMA ExtendedAttribute ExtendedAttributes
8036        """
8037        p[0] = [p[2]] if p[2] else []
8038        p[0].extend(p[3])
8039
8040    def p_ExtendedAttributesEmpty(self, p):
8041        """
8042        ExtendedAttributes :
8043        """
8044        p[0] = []
8045
8046    def p_Other(self, p):
8047        """
8048        Other : INTEGER
8049              | FLOATLITERAL
8050              | IDENTIFIER
8051              | STRING
8052              | OTHER
8053              | ELLIPSIS
8054              | COLON
8055              | SCOPE
8056              | SEMICOLON
8057              | LT
8058              | EQUALS
8059              | GT
8060              | QUESTIONMARK
8061              | DOMSTRING
8062              | BYTESTRING
8063              | USVSTRING
8064              | UTF8STRING
8065              | JSSTRING
8066              | PROMISE
8067              | ANY
8068              | BOOLEAN
8069              | BYTE
8070              | DOUBLE
8071              | FALSE
8072              | FLOAT
8073              | LONG
8074              | NULL
8075              | OBJECT
8076              | OCTET
8077              | OR
8078              | OPTIONAL
8079              | RECORD
8080              | SEQUENCE
8081              | SHORT
8082              | SYMBOL
8083              | TRUE
8084              | UNSIGNED
8085              | VOID
8086              | ArgumentNameKeyword
8087        """
8088        pass
8089
8090    def p_OtherOrComma(self, p):
8091        """
8092        OtherOrComma : Other
8093                     | COMMA
8094        """
8095        pass
8096
8097    def p_TypeSingleType(self, p):
8098        """
8099        Type : SingleType
8100        """
8101        p[0] = p[1]
8102
8103    def p_TypeUnionType(self, p):
8104        """
8105        Type : UnionType Null
8106        """
8107        p[0] = self.handleNullable(p[1], p[2])
8108
8109    def p_TypeWithExtendedAttributes(self, p):
8110        """
8111        TypeWithExtendedAttributes : ExtendedAttributeList Type
8112        """
8113        p[0] = p[2].withExtendedAttributes(p[1])
8114
8115    def p_SingleTypeDistinguishableType(self, p):
8116        """
8117        SingleType : DistinguishableType
8118        """
8119        p[0] = p[1]
8120
8121    def p_SingleTypeAnyType(self, p):
8122        """
8123        SingleType : ANY
8124        """
8125        p[0] = BuiltinTypes[IDLBuiltinType.Types.any]
8126
8127    # Note: Promise<void> is allowed, so we want to parametrize on ReturnType,
8128    # not Type.  Promise types can't be null, hence no "Null" in there.
8129    def p_SingleTypePromiseType(self, p):
8130        """
8131        SingleType : PROMISE LT ReturnType GT
8132        """
8133        p[0] = IDLPromiseType(self.getLocation(p, 1), p[3])
8134
8135    def p_UnionType(self, p):
8136        """
8137        UnionType : LPAREN UnionMemberType OR UnionMemberType UnionMemberTypes RPAREN
8138        """
8139        types = [p[2], p[4]]
8140        types.extend(p[5])
8141        p[0] = IDLUnionType(self.getLocation(p, 1), types)
8142
8143    def p_UnionMemberTypeDistinguishableType(self, p):
8144        """
8145        UnionMemberType : ExtendedAttributeList DistinguishableType
8146        """
8147        p[0] = p[2].withExtendedAttributes(p[1])
8148
8149    def p_UnionMemberType(self, p):
8150        """
8151        UnionMemberType : UnionType Null
8152        """
8153        p[0] = self.handleNullable(p[1], p[2])
8154
8155    def p_UnionMemberTypes(self, p):
8156        """
8157        UnionMemberTypes : OR UnionMemberType UnionMemberTypes
8158        """
8159        p[0] = [p[2]]
8160        p[0].extend(p[3])
8161
8162    def p_UnionMemberTypesEmpty(self, p):
8163        """
8164        UnionMemberTypes :
8165        """
8166        p[0] = []
8167
8168    def p_DistinguishableType(self, p):
8169        """
8170        DistinguishableType : PrimitiveType Null
8171                            | ARRAYBUFFER Null
8172                            | READABLESTREAM Null
8173                            | OBJECT Null
8174        """
8175        if p[1] == "object":
8176            type = BuiltinTypes[IDLBuiltinType.Types.object]
8177        elif p[1] == "ArrayBuffer":
8178            type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
8179        elif p[1] == "ReadableStream":
8180            type = BuiltinTypes[IDLBuiltinType.Types.ReadableStream]
8181        else:
8182            type = BuiltinTypes[p[1]]
8183
8184        p[0] = self.handleNullable(type, p[2])
8185
8186    def p_DistinguishableTypeStringType(self, p):
8187        """
8188        DistinguishableType : StringType Null
8189        """
8190        p[0] = self.handleNullable(p[1], p[2])
8191
8192    def p_DistinguishableTypeSequenceType(self, p):
8193        """
8194        DistinguishableType : SEQUENCE LT TypeWithExtendedAttributes GT Null
8195        """
8196        innerType = p[3]
8197        type = IDLSequenceType(self.getLocation(p, 1), innerType)
8198        p[0] = self.handleNullable(type, p[5])
8199
8200    def p_DistinguishableTypeRecordType(self, p):
8201        """
8202        DistinguishableType : RECORD LT StringType COMMA TypeWithExtendedAttributes GT Null
8203        """
8204        keyType = p[3]
8205        valueType = p[5]
8206        type = IDLRecordType(self.getLocation(p, 1), keyType, valueType)
8207        p[0] = self.handleNullable(type, p[7])
8208
8209    def p_DistinguishableTypeScopedName(self, p):
8210        """
8211        DistinguishableType : ScopedName Null
8212        """
8213        assert isinstance(p[1], IDLUnresolvedIdentifier)
8214
8215        if p[1].name == "Promise":
8216            raise WebIDLError(
8217                "Promise used without saying what it's " "parametrized over",
8218                [self.getLocation(p, 1)],
8219            )
8220
8221        type = None
8222
8223        try:
8224            if self.globalScope()._lookupIdentifier(p[1]):
8225                obj = self.globalScope()._lookupIdentifier(p[1])
8226                assert not obj.isType()
8227                if obj.isTypedef():
8228                    type = IDLTypedefType(
8229                        self.getLocation(p, 1), obj.innerType, obj.identifier.name
8230                    )
8231                elif obj.isCallback() and not obj.isInterface():
8232                    type = IDLCallbackType(self.getLocation(p, 1), obj)
8233                else:
8234                    type = IDLWrapperType(self.getLocation(p, 1), p[1])
8235                p[0] = self.handleNullable(type, p[2])
8236                return
8237        except:
8238            pass
8239
8240        type = IDLUnresolvedType(self.getLocation(p, 1), p[1])
8241        p[0] = self.handleNullable(type, p[2])
8242
8243    def p_ConstType(self, p):
8244        """
8245        ConstType : PrimitiveType
8246        """
8247        p[0] = BuiltinTypes[p[1]]
8248
8249    def p_ConstTypeIdentifier(self, p):
8250        """
8251        ConstType : IDENTIFIER
8252        """
8253        identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
8254
8255        p[0] = IDLUnresolvedType(self.getLocation(p, 1), identifier)
8256
8257    def p_PrimitiveTypeUint(self, p):
8258        """
8259        PrimitiveType : UnsignedIntegerType
8260        """
8261        p[0] = p[1]
8262
8263    def p_PrimitiveTypeBoolean(self, p):
8264        """
8265        PrimitiveType : BOOLEAN
8266        """
8267        p[0] = IDLBuiltinType.Types.boolean
8268
8269    def p_PrimitiveTypeByte(self, p):
8270        """
8271        PrimitiveType : BYTE
8272        """
8273        p[0] = IDLBuiltinType.Types.byte
8274
8275    def p_PrimitiveTypeOctet(self, p):
8276        """
8277        PrimitiveType : OCTET
8278        """
8279        p[0] = IDLBuiltinType.Types.octet
8280
8281    def p_PrimitiveTypeFloat(self, p):
8282        """
8283        PrimitiveType : FLOAT
8284        """
8285        p[0] = IDLBuiltinType.Types.float
8286
8287    def p_PrimitiveTypeUnrestictedFloat(self, p):
8288        """
8289        PrimitiveType : UNRESTRICTED FLOAT
8290        """
8291        p[0] = IDLBuiltinType.Types.unrestricted_float
8292
8293    def p_PrimitiveTypeDouble(self, p):
8294        """
8295        PrimitiveType : DOUBLE
8296        """
8297        p[0] = IDLBuiltinType.Types.double
8298
8299    def p_PrimitiveTypeUnrestictedDouble(self, p):
8300        """
8301        PrimitiveType : UNRESTRICTED DOUBLE
8302        """
8303        p[0] = IDLBuiltinType.Types.unrestricted_double
8304
8305    def p_StringType(self, p):
8306        """
8307        StringType : BuiltinStringType
8308        """
8309        p[0] = BuiltinTypes[p[1]]
8310
8311    def p_BuiltinStringTypeDOMString(self, p):
8312        """
8313        BuiltinStringType : DOMSTRING
8314        """
8315        p[0] = IDLBuiltinType.Types.domstring
8316
8317    def p_BuiltinStringTypeBytestring(self, p):
8318        """
8319        BuiltinStringType : BYTESTRING
8320        """
8321        p[0] = IDLBuiltinType.Types.bytestring
8322
8323    def p_BuiltinStringTypeUSVString(self, p):
8324        """
8325        BuiltinStringType : USVSTRING
8326        """
8327        p[0] = IDLBuiltinType.Types.usvstring
8328
8329    def p_BuiltinStringTypeUTF8String(self, p):
8330        """
8331        BuiltinStringType : UTF8STRING
8332        """
8333        p[0] = IDLBuiltinType.Types.utf8string
8334
8335    def p_BuiltinStringTypeJSString(self, p):
8336        """
8337        BuiltinStringType : JSSTRING
8338        """
8339        p[0] = IDLBuiltinType.Types.jsstring
8340
8341    def p_UnsignedIntegerTypeUnsigned(self, p):
8342        """
8343        UnsignedIntegerType : UNSIGNED IntegerType
8344        """
8345        # Adding one to a given signed integer type gets you the unsigned type:
8346        p[0] = p[2] + 1
8347
8348    def p_UnsignedIntegerType(self, p):
8349        """
8350        UnsignedIntegerType : IntegerType
8351        """
8352        p[0] = p[1]
8353
8354    def p_IntegerTypeShort(self, p):
8355        """
8356        IntegerType : SHORT
8357        """
8358        p[0] = IDLBuiltinType.Types.short
8359
8360    def p_IntegerTypeLong(self, p):
8361        """
8362        IntegerType : LONG OptionalLong
8363        """
8364        if p[2]:
8365            p[0] = IDLBuiltinType.Types.long_long
8366        else:
8367            p[0] = IDLBuiltinType.Types.long
8368
8369    def p_OptionalLong(self, p):
8370        """
8371        OptionalLong : LONG
8372        """
8373        p[0] = True
8374
8375    def p_OptionalLongEmpty(self, p):
8376        """
8377        OptionalLong :
8378        """
8379        p[0] = False
8380
8381    def p_Null(self, p):
8382        """
8383        Null : QUESTIONMARK
8384             |
8385        """
8386        if len(p) > 1:
8387            p[0] = self.getLocation(p, 1)
8388        else:
8389            p[0] = None
8390
8391    def p_ReturnTypeType(self, p):
8392        """
8393        ReturnType : Type
8394        """
8395        p[0] = p[1]
8396
8397    def p_ReturnTypeVoid(self, p):
8398        """
8399        ReturnType : VOID
8400        """
8401        p[0] = BuiltinTypes[IDLBuiltinType.Types.void]
8402
8403    def p_ScopedName(self, p):
8404        """
8405        ScopedName : AbsoluteScopedName
8406                   | RelativeScopedName
8407        """
8408        p[0] = p[1]
8409
8410    def p_AbsoluteScopedName(self, p):
8411        """
8412        AbsoluteScopedName : SCOPE IDENTIFIER ScopedNameParts
8413        """
8414        assert False
8415        pass
8416
8417    def p_RelativeScopedName(self, p):
8418        """
8419        RelativeScopedName : IDENTIFIER ScopedNameParts
8420        """
8421        assert not p[2]  # Not implemented!
8422
8423        p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
8424
8425    def p_ScopedNameParts(self, p):
8426        """
8427        ScopedNameParts : SCOPE IDENTIFIER ScopedNameParts
8428        """
8429        assert False
8430        pass
8431
8432    def p_ScopedNamePartsEmpty(self, p):
8433        """
8434        ScopedNameParts :
8435        """
8436        p[0] = None
8437
8438    def p_ExtendedAttributeNoArgs(self, p):
8439        """
8440        ExtendedAttributeNoArgs : IDENTIFIER
8441        """
8442        p[0] = (p[1],)
8443
8444    def p_ExtendedAttributeArgList(self, p):
8445        """
8446        ExtendedAttributeArgList : IDENTIFIER LPAREN ArgumentList RPAREN
8447        """
8448        p[0] = (p[1], p[3])
8449
8450    def p_ExtendedAttributeIdent(self, p):
8451        """
8452        ExtendedAttributeIdent : IDENTIFIER EQUALS STRING
8453                               | IDENTIFIER EQUALS IDENTIFIER
8454        """
8455        p[0] = (p[1], p[3])
8456
8457    def p_ExtendedAttributeNamedArgList(self, p):
8458        """
8459        ExtendedAttributeNamedArgList : IDENTIFIER EQUALS IDENTIFIER LPAREN ArgumentList RPAREN
8460        """
8461        p[0] = (p[1], p[3], p[5])
8462
8463    def p_ExtendedAttributeIdentList(self, p):
8464        """
8465        ExtendedAttributeIdentList : IDENTIFIER EQUALS LPAREN IdentifierList RPAREN
8466        """
8467        p[0] = (p[1], p[4])
8468
8469    def p_IdentifierList(self, p):
8470        """
8471        IdentifierList : IDENTIFIER Identifiers
8472        """
8473        idents = list(p[2])
8474        # This is only used for identifier-list-valued extended attributes, and if
8475        # we're going to restrict to IDENTIFIER here we should at least allow
8476        # escaping with leading '_' as usual for identifiers.
8477        ident = p[1]
8478        if ident[0] == "_":
8479            ident = ident[1:]
8480        idents.insert(0, ident)
8481        p[0] = idents
8482
8483    def p_IdentifiersList(self, p):
8484        """
8485        Identifiers : COMMA IDENTIFIER Identifiers
8486        """
8487        idents = list(p[3])
8488        # This is only used for identifier-list-valued extended attributes, and if
8489        # we're going to restrict to IDENTIFIER here we should at least allow
8490        # escaping with leading '_' as usual for identifiers.
8491        ident = p[2]
8492        if ident[0] == "_":
8493            ident = ident[1:]
8494        idents.insert(0, ident)
8495        p[0] = idents
8496
8497    def p_IdentifiersEmpty(self, p):
8498        """
8499        Identifiers :
8500        """
8501        p[0] = []
8502
8503    def p_error(self, p):
8504        if not p:
8505            raise WebIDLError(
8506                "Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both",
8507                [self._filename],
8508            )
8509        else:
8510            raise WebIDLError(
8511                "invalid syntax",
8512                [Location(self.lexer, p.lineno, p.lexpos, self._filename)],
8513            )
8514
8515    def __init__(self, outputdir="", lexer=None):
8516        Tokenizer.__init__(self, outputdir, lexer)
8517
8518        logger = SqueakyCleanLogger()
8519        try:
8520            self.parser = yacc.yacc(
8521                module=self,
8522                outputdir=outputdir,
8523                errorlog=logger,
8524                write_tables=False,
8525                # Pickling the grammar is a speedup in
8526                # some cases (older Python?) but a
8527                # significant slowdown in others.
8528                # We're not pickling for now, until it
8529                # becomes a speedup again.
8530                # , picklefile='WebIDLGrammar.pkl'
8531            )
8532        finally:
8533            logger.reportGrammarErrors()
8534
8535        self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None)
8536
8537        self._installBuiltins(self._globalScope)
8538        self._productions = []
8539
8540        self._filename = "<builtin>"
8541        self.lexer.input(Parser._builtins)
8542        self._filename = None
8543
8544        self.parser.parse(lexer=self.lexer, tracking=True)
8545
8546    def _installBuiltins(self, scope):
8547        assert isinstance(scope, IDLScope)
8548
8549        # range omits the last value.
8550        for x in range(
8551            IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1
8552        ):
8553            builtin = BuiltinTypes[x]
8554            name = builtin.name
8555            typedef = IDLTypedef(
8556                BuiltinLocation("<builtin type>"), scope, builtin, name
8557            )
8558
8559    @staticmethod
8560    def handleNullable(type, questionMarkLocation):
8561        if questionMarkLocation is not None:
8562            type = IDLNullableType(questionMarkLocation, type)
8563
8564        return type
8565
8566    def parse(self, t, filename=None):
8567        self.lexer.input(t)
8568
8569        # for tok in iter(self.lexer.token, None):
8570        #    print tok
8571
8572        self._filename = filename
8573        self._productions.extend(self.parser.parse(lexer=self.lexer, tracking=True))
8574        self._filename = None
8575
8576    def finish(self):
8577        # If we have interfaces that are iterable, create their
8578        # iterator interfaces and add them to the productions array.
8579        interfaceStatements = []
8580        for p in self._productions:
8581            if isinstance(p, IDLInterface):
8582                interfaceStatements.append(p)
8583
8584        iterableIteratorIface = None
8585        for iface in interfaceStatements:
8586            iterable = None
8587            # We haven't run finish() on the interface yet, so we don't know
8588            # whether our interface is maplike/setlike/iterable or not. This
8589            # means we have to loop through the members to see if we have an
8590            # iterable member.
8591            for m in iface.members:
8592                if isinstance(m, IDLIterable):
8593                    iterable = m
8594                    break
8595            if iterable and iterable.isPairIterator():
8596
8597                def simpleExtendedAttr(str):
8598                    return IDLExtendedAttribute(iface.location, (str,))
8599
8600                nextMethod = IDLMethod(
8601                    iface.location,
8602                    IDLUnresolvedIdentifier(iface.location, "next"),
8603                    BuiltinTypes[IDLBuiltinType.Types.object],
8604                    [],
8605                )
8606                nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
8607                itr_ident = IDLUnresolvedIdentifier(
8608                    iface.location, iface.identifier.name + "Iterator"
8609                )
8610                classNameOverride = iface.identifier.name + " Iterator"
8611                itr_iface = IDLInterface(
8612                    iface.location,
8613                    self.globalScope(),
8614                    itr_ident,
8615                    None,
8616                    [nextMethod],
8617                    isKnownNonPartial=True,
8618                    classNameOverride=classNameOverride,
8619                )
8620                itr_iface.addExtendedAttributes(
8621                    [simpleExtendedAttr("LegacyNoInterfaceObject")]
8622                )
8623                # Make sure the exposure set for the iterator interface is the
8624                # same as the exposure set for the iterable interface, because
8625                # we're going to generate methods on the iterable that return
8626                # instances of the iterator.
8627                itr_iface._exposureGlobalNames = set(iface._exposureGlobalNames)
8628                # Always append generated iterable interfaces after the
8629                # interface they're a member of, otherwise nativeType generation
8630                # won't work correctly.
8631                itr_iface.iterableInterface = iface
8632                self._productions.append(itr_iface)
8633                iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
8634
8635        # Make sure we finish IDLIncludesStatements before we finish the
8636        # IDLInterfaces.
8637        # XXX khuey hates this bit and wants to nuke it from orbit.
8638        includesStatements = [
8639            p for p in self._productions if isinstance(p, IDLIncludesStatement)
8640        ]
8641        otherStatements = [
8642            p for p in self._productions if not isinstance(p, IDLIncludesStatement)
8643        ]
8644        for production in includesStatements:
8645            production.finish(self.globalScope())
8646        for production in otherStatements:
8647            production.finish(self.globalScope())
8648
8649        # Do any post-finish validation we need to do
8650        for production in self._productions:
8651            production.validate()
8652
8653        # De-duplicate self._productions, without modifying its order.
8654        seen = set()
8655        result = []
8656        for p in self._productions:
8657            if p not in seen:
8658                seen.add(p)
8659                result.append(p)
8660        return result
8661
8662    def reset(self):
8663        return Parser(lexer=self.lexer)
8664
8665    # Builtin IDL defined by WebIDL
8666    _builtins = """
8667        typedef unsigned long long DOMTimeStamp;
8668        typedef (ArrayBufferView or ArrayBuffer) BufferSource;
8669    """
8670
8671
8672def main():
8673    # Parse arguments.
8674    from optparse import OptionParser
8675
8676    usageString = "usage: %prog [options] files"
8677    o = OptionParser(usage=usageString)
8678    o.add_option(
8679        "--cachedir",
8680        dest="cachedir",
8681        default=None,
8682        help="Directory in which to cache lex/parse tables.",
8683    )
8684    o.add_option(
8685        "--verbose-errors",
8686        action="store_true",
8687        default=False,
8688        help="When an error happens, display the Python traceback.",
8689    )
8690    (options, args) = o.parse_args()
8691
8692    if len(args) < 1:
8693        o.error(usageString)
8694
8695    fileList = args
8696    baseDir = os.getcwd()
8697
8698    # Parse the WebIDL.
8699    parser = Parser(options.cachedir)
8700    try:
8701        for filename in fileList:
8702            fullPath = os.path.normpath(os.path.join(baseDir, filename))
8703            f = open(fullPath, "rb")
8704            lines = f.readlines()
8705            f.close()
8706            print(fullPath)
8707            parser.parse("".join(lines), fullPath)
8708        parser.finish()
8709    except WebIDLError as e:
8710        if options.verbose_errors:
8711            traceback.print_exc()
8712        else:
8713            print(e)
8714
8715
8716if __name__ == "__main__":
8717    main()
8718