tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

WebIDL.py (335448B)


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