diff options
author | Martin Robinson <mrobinson@igalia.com> | 2023-06-24 13:38:11 +0200 |
---|---|---|
committer | Martin Robinson <mrobinson@igalia.com> | 2023-06-30 09:51:31 +0200 |
commit | 8be014ee46077e78db21c5d73058c35a4ee65fa9 (patch) | |
tree | 9bfd0bc11997381d792fd3015add8be9cd7abd70 /components/script/dom/bindings/codegen/parser/WebIDL.py | |
parent | 7412e28349237055652a08a2216043d0993a3cea (diff) | |
download | servo-8be014ee46077e78db21c5d73058c35a4ee65fa9.tar.gz servo-8be014ee46077e78db21c5d73058c35a4ee65fa9.zip |
Create a top-level "third_party" directory
This directory now contains third_party software that is vendored into
the Servo source tree. The idea is that it would eventually hold
webrender and other crates from mozilla-central as well with a standard
patch management approach for each.
Diffstat (limited to 'components/script/dom/bindings/codegen/parser/WebIDL.py')
-rw-r--r-- | components/script/dom/bindings/codegen/parser/WebIDL.py | 9107 |
1 files changed, 0 insertions, 9107 deletions
diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py deleted file mode 100644 index 2366e3f7027..00000000000 --- a/components/script/dom/bindings/codegen/parser/WebIDL.py +++ /dev/null @@ -1,9107 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -""" A WebIDL parser. """ - -import copy -import math -import os -import re -import string -import traceback -from collections import OrderedDict, defaultdict -from itertools import chain - -from ply import lex, yacc - -# Machinery - - -def parseInt(literal): - string = literal - sign = 0 - base = 0 - - if string[0] == "-": - sign = -1 - string = string[1:] - else: - sign = 1 - - if string[0] == "0" and len(string) > 1: - if string[1] == "x" or string[1] == "X": - base = 16 - string = string[2:] - else: - base = 8 - string = string[1:] - else: - base = 10 - - value = int(string, base) - return value * sign - - -def enum(*names, **kw): - class Foo(object): - attrs = OrderedDict() - - def __init__(self, names): - for v, k in enumerate(names): - self.attrs[k] = v - - def __getattr__(self, attr): - if attr in self.attrs: - return self.attrs[attr] - raise AttributeError - - def __setattr__(self, name, value): # this makes it read-only - raise NotImplementedError - - if "base" not in kw: - return Foo(names) - return Foo(chain(kw["base"].attrs.keys(), names)) - - -class WebIDLError(Exception): - def __init__(self, message, locations, warning=False): - self.message = message - self.locations = [str(loc) for loc in locations] - self.warning = warning - - def __str__(self): - return "%s: %s%s%s" % ( - self.warning and "warning" or "error", - self.message, - ", " if len(self.locations) != 0 else "", - "\n".join(self.locations), - ) - - -class Location(object): - def __init__(self, lexer, lineno, lexpos, filename): - self._line = None - self._lineno = lineno - self._lexpos = lexpos - self._lexdata = lexer.lexdata - self._file = filename if filename else "<unknown>" - - def __eq__(self, other): - return self._lexpos == other._lexpos and self._file == other._file - - def filename(self): - return self._file - - def resolve(self): - if self._line: - return - - startofline = self._lexdata.rfind("\n", 0, self._lexpos) + 1 - endofline = self._lexdata.find("\n", self._lexpos, self._lexpos + 80) - if endofline != -1: - self._line = self._lexdata[startofline:endofline] - else: - self._line = self._lexdata[startofline:] - self._colno = self._lexpos - startofline - - # Our line number seems to point to the start of self._lexdata - self._lineno += self._lexdata.count("\n", 0, startofline) - - def get(self): - self.resolve() - return "%s line %s:%s" % (self._file, self._lineno, self._colno) - - def _pointerline(self): - return " " * self._colno + "^" - - def __str__(self): - self.resolve() - return "%s line %s:%s\n%s\n%s" % ( - self._file, - self._lineno, - self._colno, - self._line, - self._pointerline(), - ) - - -class BuiltinLocation(object): - def __init__(self, text): - self.msg = text + "\n" - - def __eq__(self, other): - return isinstance(other, BuiltinLocation) and self.msg == other.msg - - def filename(self): - return "<builtin>" - - def resolve(self): - pass - - def get(self): - return self.msg - - def __str__(self): - return self.get() - - -# Data Model - - -class IDLObject(object): - def __init__(self, location): - self.location = location - self.userData = dict() - - def filename(self): - return self.location.filename() - - def isInterface(self): - return False - - def isNamespace(self): - return False - - def isInterfaceMixin(self): - return False - - def isEnum(self): - return False - - def isCallback(self): - return False - - def isType(self): - return False - - def isDictionary(self): - return False - - def isUnion(self): - return False - - def isTypedef(self): - return False - - def getUserData(self, key, default): - return self.userData.get(key, default) - - def setUserData(self, key, value): - self.userData[key] = value - - def addExtendedAttributes(self, attrs): - assert False # Override me! - - def handleExtendedAttribute(self, attr): - assert False # Override me! - - def _getDependentObjects(self): - assert False # Override me! - - def getDeps(self, visited=None): - """Return a set of files that this object depends on. If any of - these files are changed the parser needs to be rerun to regenerate - a new IDLObject. - - The visited argument is a set of all the objects already visited. - We must test to see if we are in it, and if so, do nothing. This - prevents infinite recursion.""" - - # NB: We can't use visited=set() above because the default value is - # evaluated when the def statement is evaluated, not when the function - # is executed, so there would be one set for all invocations. - if visited is None: - visited = set() - - if self in visited: - return set() - - visited.add(self) - - deps = set() - if self.filename() != "<builtin>": - deps.add(self.filename()) - - for d in self._getDependentObjects(): - deps.update(d.getDeps(visited)) - - return deps - - -class IDLScope(IDLObject): - def __init__(self, location, parentScope, identifier): - IDLObject.__init__(self, location) - - self.parentScope = parentScope - if identifier: - assert isinstance(identifier, IDLIdentifier) - self._name = identifier - else: - self._name = None - - self._dict = {} - self.globalNames = set() - # A mapping from global name to the set of global interfaces - # that have that global name. - self.globalNameMapping = defaultdict(set) - - def __str__(self): - return self.QName() - - def QName(self): - # It's possible for us to be called before __init__ has been called, for - # the IDLObjectWithScope case. In that case, self._name won't be set yet. - if hasattr(self, "_name"): - name = self._name - else: - name = None - if name: - return name.QName() + "::" - return "::" - - def ensureUnique(self, identifier, object): - """ - Ensure that there is at most one 'identifier' in scope ('self'). - Note that object can be None. This occurs if we end up here for an - interface type we haven't seen yet. - """ - assert isinstance(identifier, IDLUnresolvedIdentifier) - assert not object or isinstance(object, IDLObjectWithIdentifier) - assert not object or object.identifier == identifier - - if identifier.name in self._dict: - if not object: - return - - # ensureUnique twice with the same object is not allowed - assert id(object) != id(self._dict[identifier.name]) - - replacement = self.resolveIdentifierConflict( - self, identifier, self._dict[identifier.name], object - ) - self._dict[identifier.name] = replacement - return - - assert object - - self._dict[identifier.name] = object - - def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject): - if ( - isinstance(originalObject, IDLExternalInterface) - and isinstance(newObject, IDLExternalInterface) - and originalObject.identifier.name == newObject.identifier.name - ): - return originalObject - - if isinstance(originalObject, IDLExternalInterface) or isinstance( - newObject, IDLExternalInterface - ): - raise WebIDLError( - "Name collision between " - "interface declarations for identifier '%s' at '%s' and '%s'" - % (identifier.name, originalObject.location, newObject.location), - [], - ) - - if isinstance(originalObject, IDLDictionary) or isinstance( - newObject, IDLDictionary - ): - raise WebIDLError( - "Name collision between dictionary declarations for " - "identifier '%s'.\n%s\n%s" - % (identifier.name, originalObject.location, newObject.location), - [], - ) - - # We do the merging of overloads here as opposed to in IDLInterface - # because we need to merge overloads of LegacyFactoryFunctions and we need to - # detect conflicts in those across interfaces. See also the comment in - # IDLInterface.addExtendedAttributes for "LegacyFactoryFunction". - if isinstance(originalObject, IDLMethod) and isinstance(newObject, IDLMethod): - return originalObject.addOverload(newObject) - - # Default to throwing, derived classes can override. - conflictdesc = "\n\t%s at %s\n\t%s at %s" % ( - originalObject, - originalObject.location, - newObject, - newObject.location, - ) - - raise WebIDLError( - "Multiple unresolvable definitions of identifier '%s' in scope '%s'%s" - % (identifier.name, str(self), conflictdesc), - [], - ) - - def _lookupIdentifier(self, identifier): - return self._dict[identifier.name] - - def lookupIdentifier(self, identifier): - assert isinstance(identifier, IDLIdentifier) - assert identifier.scope == self - return self._lookupIdentifier(identifier) - - def addIfaceGlobalNames(self, interfaceName, globalNames): - """Record the global names (from |globalNames|) that can be used in - [Exposed] to expose things in a global named |interfaceName|""" - self.globalNames.update(globalNames) - for name in globalNames: - self.globalNameMapping[name].add(interfaceName) - - -class IDLIdentifier(IDLObject): - def __init__(self, location, scope, name): - IDLObject.__init__(self, location) - - self.name = name - assert isinstance(scope, IDLScope) - self.scope = scope - - def __str__(self): - return self.QName() - - def QName(self): - return self.scope.QName() + self.name - - def __hash__(self): - return self.QName().__hash__() - - def __eq__(self, other): - return self.QName() == other.QName() - - def object(self): - return self.scope.lookupIdentifier(self) - - -class IDLUnresolvedIdentifier(IDLObject): - def __init__( - self, location, name, allowDoubleUnderscore=False, allowForbidden=False - ): - IDLObject.__init__(self, location) - - assert len(name) > 0 - - if name == "__noSuchMethod__": - raise WebIDLError("__noSuchMethod__ is deprecated", [location]) - - if name[:2] == "__" and not allowDoubleUnderscore: - raise WebIDLError("Identifiers beginning with __ are reserved", [location]) - if name[0] == "_" and not allowDoubleUnderscore: - name = name[1:] - if name in ["constructor", "toString"] and not allowForbidden: - raise WebIDLError( - "Cannot use reserved identifier '%s'" % (name), [location] - ) - - self.name = name - - def __str__(self): - return self.QName() - - def QName(self): - return "<unresolved scope>::" + self.name - - def resolve(self, scope, object): - assert isinstance(scope, IDLScope) - assert not object or isinstance(object, IDLObjectWithIdentifier) - assert not object or object.identifier == self - - scope.ensureUnique(self, object) - - identifier = IDLIdentifier(self.location, scope, self.name) - if object: - object.identifier = identifier - return identifier - - def finish(self): - assert False # Should replace with a resolved identifier first. - - -class IDLObjectWithIdentifier(IDLObject): - def __init__(self, location, parentScope, identifier): - IDLObject.__init__(self, location) - - assert isinstance(identifier, IDLUnresolvedIdentifier) - - self.identifier = identifier - - if parentScope: - self.resolve(parentScope) - - def resolve(self, parentScope): - assert isinstance(parentScope, IDLScope) - assert isinstance(self.identifier, IDLUnresolvedIdentifier) - self.identifier.resolve(parentScope, self) - - -class IDLObjectWithScope(IDLObjectWithIdentifier, IDLScope): - def __init__(self, location, parentScope, identifier): - assert isinstance(identifier, IDLUnresolvedIdentifier) - - IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier) - IDLScope.__init__(self, location, parentScope, self.identifier) - - -class IDLIdentifierPlaceholder(IDLObjectWithIdentifier): - def __init__(self, location, identifier): - assert isinstance(identifier, IDLUnresolvedIdentifier) - IDLObjectWithIdentifier.__init__(self, location, None, identifier) - - def finish(self, scope): - try: - scope._lookupIdentifier(self.identifier) - except: - raise WebIDLError( - "Unresolved type '%s'." % self.identifier, [self.location] - ) - - obj = self.identifier.resolve(scope, None) - return scope.lookupIdentifier(obj) - - -class IDLExposureMixins: - def __init__(self, location): - # _exposureGlobalNames are the global names listed in our [Exposed] - # extended attribute. exposureSet is the exposure set as defined in the - # Web IDL spec: it contains interface names. - self._exposureGlobalNames = set() - self.exposureSet = set() - self._location = location - self._globalScope = None - - def finish(self, scope): - assert scope.parentScope is None - self._globalScope = scope - - if "*" in self._exposureGlobalNames: - self._exposureGlobalNames = scope.globalNames - else: - # Verify that our [Exposed] value, if any, makes sense. - for globalName in self._exposureGlobalNames: - if globalName not in scope.globalNames: - raise WebIDLError( - "Unknown [Exposed] value %s" % globalName, [self._location] - ) - - # Verify that we are exposed _somwhere_ if we have some place to be - # exposed. We don't want to assert that we're definitely exposed - # because a lot of our parser tests have small-enough IDL snippets that - # they don't include any globals, and we don't really want to go through - # and add global interfaces and [Exposed] annotations to all those - # tests. - if len(scope.globalNames) != 0: - if len(self._exposureGlobalNames) == 0 and not self.isPseudoInterface(): - raise WebIDLError( - ( - "'%s' is not exposed anywhere even though we have " - "globals to be exposed to" - ) - % self, - [self.location], - ) - - globalNameSetToExposureSet(scope, self._exposureGlobalNames, self.exposureSet) - - def isExposedInWindow(self): - return "Window" in self.exposureSet - - def isExposedInAnyWorker(self): - return len(self.getWorkerExposureSet()) > 0 - - def isExposedInWorkerDebugger(self): - return len(self.getWorkerDebuggerExposureSet()) > 0 - - def isExposedInAnyWorklet(self): - return len(self.getWorkletExposureSet()) > 0 - - def isExposedInSomeButNotAllWorkers(self): - """ - Returns true if the Exposed extended attribute for this interface - exposes it in some worker globals but not others. The return value does - not depend on whether the interface is exposed in Window or System - globals. - """ - if not self.isExposedInAnyWorker(): - return False - workerScopes = self.parentScope.globalNameMapping["Worker"] - return len(workerScopes.difference(self.exposureSet)) > 0 - - def isExposedInShadowRealms(self): - return "ShadowRealmGlobalScope" in self.exposureSet - - def getWorkerExposureSet(self): - workerScopes = self._globalScope.globalNameMapping["Worker"] - return workerScopes.intersection(self.exposureSet) - - def getWorkletExposureSet(self): - workletScopes = self._globalScope.globalNameMapping["Worklet"] - return workletScopes.intersection(self.exposureSet) - - def getWorkerDebuggerExposureSet(self): - workerDebuggerScopes = self._globalScope.globalNameMapping["WorkerDebugger"] - return workerDebuggerScopes.intersection(self.exposureSet) - - -class IDLExternalInterface(IDLObjectWithIdentifier): - def __init__(self, location, parentScope, identifier): - assert isinstance(identifier, IDLUnresolvedIdentifier) - assert isinstance(parentScope, IDLScope) - self.parent = None - IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier) - IDLObjectWithIdentifier.resolve(self, parentScope) - - def finish(self, scope): - pass - - def validate(self): - pass - - def isIteratorInterface(self): - return False - - def isAsyncIteratorInterface(self): - return False - - def isExternal(self): - return True - - def isInterface(self): - return True - - def addExtendedAttributes(self, attrs): - if len(attrs) != 0: - raise WebIDLError( - "There are no extended attributes that are " - "allowed on external interfaces", - [attrs[0].location, self.location], - ) - - def resolve(self, parentScope): - pass - - def getJSImplementation(self): - return None - - def isJSImplemented(self): - return False - - def hasProbablyShortLivingWrapper(self): - return False - - def _getDependentObjects(self): - return set() - - -class IDLPartialDictionary(IDLObject): - def __init__(self, location, name, members, nonPartialDictionary): - assert isinstance(name, IDLUnresolvedIdentifier) - - IDLObject.__init__(self, location) - self.identifier = name - self.members = members - self._nonPartialDictionary = nonPartialDictionary - self._finished = False - nonPartialDictionary.addPartialDictionary(self) - - def addExtendedAttributes(self, attrs): - pass - - def finish(self, scope): - if self._finished: - return - self._finished = True - - # Need to make sure our non-partial dictionary gets - # finished so it can report cases when we only have partial - # dictionaries. - self._nonPartialDictionary.finish(scope) - - def validate(self): - pass - - -class IDLPartialInterfaceOrNamespace(IDLObject): - def __init__(self, location, name, members, nonPartialInterfaceOrNamespace): - assert isinstance(name, IDLUnresolvedIdentifier) - - IDLObject.__init__(self, location) - self.identifier = name - self.members = members - # propagatedExtendedAttrs are the ones that should get - # propagated to our non-partial interface. - self.propagatedExtendedAttrs = [] - self._haveSecureContextExtendedAttribute = False - self._nonPartialInterfaceOrNamespace = nonPartialInterfaceOrNamespace - self._finished = False - nonPartialInterfaceOrNamespace.addPartial(self) - - def addExtendedAttributes(self, attrs): - for attr in attrs: - identifier = attr.identifier() - - if identifier == "LegacyFactoryFunction": - self.propagatedExtendedAttrs.append(attr) - elif identifier == "SecureContext": - self._haveSecureContextExtendedAttribute = True - # This gets propagated to all our members. - for member in self.members: - if member.getExtendedAttribute("SecureContext"): - raise WebIDLError( - "[SecureContext] specified on both a " - "partial interface member and on the " - "partial interface itself", - [member.location, attr.location], - ) - member.addExtendedAttributes([attr]) - elif identifier == "Exposed": - # This just gets propagated to all our members. - for member in self.members: - if len(member._exposureGlobalNames) != 0: - raise WebIDLError( - "[Exposed] specified on both a " - "partial interface member and on the " - "partial interface itself", - [member.location, attr.location], - ) - member.addExtendedAttributes([attr]) - else: - raise WebIDLError( - "Unknown extended attribute %s on partial " - "interface" % identifier, - [attr.location], - ) - - def finish(self, scope): - if self._finished: - return - self._finished = True - if ( - not self._haveSecureContextExtendedAttribute - and self._nonPartialInterfaceOrNamespace.getExtendedAttribute( - "SecureContext" - ) - ): - # This gets propagated to all our members. - for member in self.members: - if member.getExtendedAttribute("SecureContext"): - raise WebIDLError( - "[SecureContext] specified on both a " - "partial interface member and on the " - "non-partial interface", - [ - member.location, - self._nonPartialInterfaceOrNamespace.location, - ], - ) - member.addExtendedAttributes( - [ - IDLExtendedAttribute( - self._nonPartialInterfaceOrNamespace.location, - ("SecureContext",), - ) - ] - ) - # Need to make sure our non-partial interface or namespace gets - # finished so it can report cases when we only have partial - # interfaces/namespaces. - self._nonPartialInterfaceOrNamespace.finish(scope) - - def validate(self): - pass - - -def convertExposedAttrToGlobalNameSet(exposedAttr, targetSet): - assert len(targetSet) == 0 - if exposedAttr.hasValue(): - targetSet.add(exposedAttr.value()) - else: - assert exposedAttr.hasArgs() - targetSet.update(exposedAttr.args()) - - -def globalNameSetToExposureSet(globalScope, nameSet, exposureSet): - for name in nameSet: - exposureSet.update(globalScope.globalNameMapping[name]) - - -class IDLInterfaceOrInterfaceMixinOrNamespace(IDLObjectWithScope, IDLExposureMixins): - def __init__(self, location, parentScope, name): - assert isinstance(parentScope, IDLScope) - assert isinstance(name, IDLUnresolvedIdentifier) - - self._finished = False - self.members = [] - self._partials = [] - self._extendedAttrDict = {} - self._isKnownNonPartial = False - - IDLObjectWithScope.__init__(self, location, parentScope, name) - IDLExposureMixins.__init__(self, location) - - def finish(self, scope): - if not self._isKnownNonPartial: - raise WebIDLError( - "%s does not have a non-partial declaration" % str(self), - [self.location], - ) - - IDLExposureMixins.finish(self, scope) - - # Now go ahead and merge in our partials. - for partial in self._partials: - partial.finish(scope) - self.addExtendedAttributes(partial.propagatedExtendedAttrs) - self.members.extend(partial.members) - - def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject): - assert isinstance(scope, IDLScope) - assert isinstance(originalObject, IDLInterfaceMember) - assert isinstance(newObject, IDLInterfaceMember) - - retval = IDLScope.resolveIdentifierConflict( - self, scope, identifier, originalObject, newObject - ) - - # Might be a ctor, which isn't in self.members - if newObject in self.members: - self.members.remove(newObject) - return retval - - def typeName(self): - if self.isInterface(): - return "interface" - if self.isNamespace(): - return "namespace" - assert self.isInterfaceMixin() - return "interface mixin" - - def getExtendedAttribute(self, name): - return self._extendedAttrDict.get(name, None) - - def setNonPartial(self, location, members): - if self._isKnownNonPartial: - raise WebIDLError( - "Two non-partial definitions for the " "same %s" % self.typeName(), - [location, self.location], - ) - self._isKnownNonPartial = True - # Now make it look like we were parsed at this new location, since - # that's the place where the interface is "really" defined - self.location = location - # Put the new members at the beginning - self.members = members + self.members - - def addPartial(self, partial): - assert self.identifier.name == partial.identifier.name - self._partials.append(partial) - - def getPartials(self): - # Don't let people mutate our guts. - return list(self._partials) - - def finishMembers(self, scope): - # Assuming we've merged in our partials, set the _exposureGlobalNames on - # any members that don't have it set yet. Note that any partial - # interfaces that had [Exposed] set have already set up - # _exposureGlobalNames on all the members coming from them, so this is - # just implementing the "members default to interface or interface mixin - # that defined them" and "partial interfaces or interface mixins default - # to interface or interface mixin they're a partial for" rules from the - # spec. - for m in self.members: - # If m, or the partial m came from, had [Exposed] - # specified, it already has a nonempty exposure global names set. - if len(m._exposureGlobalNames) == 0: - m._exposureGlobalNames.update(self._exposureGlobalNames) - if m.isAttr() and m.stringifier: - m.expand(self.members) - - # resolve() will modify self.members, so we need to iterate - # over a copy of the member list here. - for member in list(self.members): - member.resolve(self) - - for member in self.members: - member.finish(scope) - - # Now that we've finished our members, which has updated their exposure - # sets, make sure they aren't exposed in places where we are not. - for member in self.members: - if not member.exposureSet.issubset(self.exposureSet): - raise WebIDLError( - "Interface or interface mixin member has " - "larger exposure set than its container", - [member.location, self.location], - ) - - def isExternal(self): - return False - - -class IDLInterfaceMixin(IDLInterfaceOrInterfaceMixinOrNamespace): - def __init__(self, location, parentScope, name, members, isKnownNonPartial): - self.actualExposureGlobalNames = set() - - assert isKnownNonPartial or len(members) == 0 - IDLInterfaceOrInterfaceMixinOrNamespace.__init__( - self, location, parentScope, name - ) - - if isKnownNonPartial: - self.setNonPartial(location, members) - - def __str__(self): - return "Interface mixin '%s'" % self.identifier.name - - def isInterfaceMixin(self): - return True - - def finish(self, scope): - if self._finished: - return - self._finished = True - - # Expose to the globals of interfaces that includes this mixin if this - # mixin has no explicit [Exposed] so that its members can be exposed - # based on the base interface exposure set. - # - # Make sure this is done before IDLExposureMixins.finish call, since - # that converts our set of exposure global names to an actual exposure - # set. - hasImplicitExposure = len(self._exposureGlobalNames) == 0 - if hasImplicitExposure: - self._exposureGlobalNames.update(self.actualExposureGlobalNames) - - IDLInterfaceOrInterfaceMixinOrNamespace.finish(self, scope) - - self.finishMembers(scope) - - def validate(self): - for member in self.members: - - if member.isAttr(): - if member.inherit: - raise WebIDLError( - "Interface mixin member cannot include " - "an inherited attribute", - [member.location, self.location], - ) - if member.isStatic(): - raise WebIDLError( - "Interface mixin member cannot include " "a static member", - [member.location, self.location], - ) - - if member.isMethod(): - if member.isStatic(): - raise WebIDLError( - "Interface mixin member cannot include " "a static operation", - [member.location, self.location], - ) - if ( - member.isGetter() - or member.isSetter() - or member.isDeleter() - or member.isLegacycaller() - ): - raise WebIDLError( - "Interface mixin member cannot include a " "special operation", - [member.location, self.location], - ) - - def addExtendedAttributes(self, attrs): - for attr in attrs: - identifier = attr.identifier() - - if identifier == "SecureContext": - if not attr.noArguments(): - raise WebIDLError( - "[%s] must take no arguments" % identifier, [attr.location] - ) - # This gets propagated to all our members. - for member in self.members: - if member.getExtendedAttribute("SecureContext"): - raise WebIDLError( - "[SecureContext] specified on both " - "an interface mixin member and on" - "the interface mixin itself", - [member.location, attr.location], - ) - member.addExtendedAttributes([attr]) - elif identifier == "Exposed": - convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) - else: - raise WebIDLError( - "Unknown extended attribute %s on interface" % identifier, - [attr.location], - ) - - attrlist = attr.listValue() - self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True - - def _getDependentObjects(self): - return set(self.members) - - -class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace): - def __init__(self, location, parentScope, name, parent, members, isKnownNonPartial): - assert isKnownNonPartial or not parent - assert isKnownNonPartial or len(members) == 0 - - self.parent = None - self._callback = False - self.maplikeOrSetlikeOrIterable = None - # namedConstructors needs deterministic ordering because bindings code - # outputs the constructs in the order that namedConstructors enumerates - # them. - self.legacyFactoryFunctions = list() - self.legacyWindowAliases = [] - self.includedMixins = set() - # self.interfacesBasedOnSelf is the set of interfaces that inherit from - # self, including self itself. - # Used for distinguishability checking. - self.interfacesBasedOnSelf = set([self]) - self._hasChildInterfaces = False - self._isOnGlobalProtoChain = False - # Pseudo interfaces aren't exposed anywhere, and so shouldn't issue warnings - self._isPseudo = False - - # Tracking of the number of reserved slots we need for our - # members and those of ancestor interfaces. - self.totalMembersInSlots = 0 - # Tracking of the number of own own members we have in slots - self._ownMembersInSlots = 0 - # If this is an iterator interface, we need to know what iterable - # interface we're iterating for in order to get its nativeType. - self.iterableInterface = None - self.asyncIterableInterface = None - # True if we have cross-origin members. - self.hasCrossOriginMembers = False - # True if some descendant (including ourselves) has cross-origin members - self.hasDescendantWithCrossOriginMembers = False - - IDLInterfaceOrInterfaceMixinOrNamespace.__init__( - self, location, parentScope, name - ) - - if isKnownNonPartial: - self.setNonPartial(location, parent, members) - - def ctor(self): - identifier = IDLUnresolvedIdentifier( - self.location, "constructor", allowForbidden=True - ) - try: - return self._lookupIdentifier(identifier) - except: - return None - - def isIterable(self): - return ( - self.maplikeOrSetlikeOrIterable - and self.maplikeOrSetlikeOrIterable.isIterable() - ) - - def isAsyncIterable(self): - return ( - self.maplikeOrSetlikeOrIterable - and self.maplikeOrSetlikeOrIterable.isAsyncIterable() - ) - - def isIteratorInterface(self): - return self.iterableInterface is not None - - def isAsyncIteratorInterface(self): - return self.asyncIterableInterface is not None - - def getClassName(self): - return self.identifier.name - - def finish(self, scope): - if self._finished: - return - - self._finished = True - - IDLInterfaceOrInterfaceMixinOrNamespace.finish(self, scope) - - if len(self.legacyWindowAliases) > 0: - if not self.hasInterfaceObject(): - raise WebIDLError( - "Interface %s unexpectedly has [LegacyWindowAlias] " - "and [LegacyNoInterfaceObject] together" % self.identifier.name, - [self.location], - ) - if not self.isExposedInWindow(): - raise WebIDLError( - "Interface %s has [LegacyWindowAlias] " - "but not exposed in Window" % self.identifier.name, - [self.location], - ) - - # Generate maplike/setlike interface members. Since generated members - # need to be treated like regular interface members, do this before - # things like exposure setting. - for member in self.members: - if member.isMaplikeOrSetlikeOrIterable(): - if self.isJSImplemented(): - raise WebIDLError( - "%s declaration used on " - "interface that is implemented in JS" - % (member.maplikeOrSetlikeOrIterableType), - [member.location], - ) - if member.valueType.isObservableArray() or ( - member.hasKeyType() and member.keyType.isObservableArray() - ): - raise WebIDLError( - "%s declaration uses ObservableArray as value or key type" - % (member.maplikeOrSetlikeOrIterableType), - [member.location], - ) - # Check that we only have one interface declaration (currently - # there can only be one maplike/setlike declaration per - # interface) - if self.maplikeOrSetlikeOrIterable: - raise WebIDLError( - "%s declaration used on " - "interface that already has %s " - "declaration" - % ( - member.maplikeOrSetlikeOrIterableType, - self.maplikeOrSetlikeOrIterable.maplikeOrSetlikeOrIterableType, - ), - [self.maplikeOrSetlikeOrIterable.location, member.location], - ) - self.maplikeOrSetlikeOrIterable = member - # If we've got a maplike or setlike declaration, we'll be building all of - # our required methods in Codegen. Generate members now. - self.maplikeOrSetlikeOrIterable.expand(self.members) - - assert not self.parent or isinstance(self.parent, IDLIdentifierPlaceholder) - parent = self.parent.finish(scope) if self.parent else None - if parent and isinstance(parent, IDLExternalInterface): - raise WebIDLError( - "%s inherits from %s which does not have " - "a definition" % (self.identifier.name, self.parent.identifier.name), - [self.location], - ) - if parent and not isinstance(parent, IDLInterface): - raise WebIDLError( - "%s inherits from %s which is not an interface " - % (self.identifier.name, self.parent.identifier.name), - [self.location, parent.location], - ) - - self.parent = parent - - assert iter(self.members) - - if self.isNamespace(): - assert not self.parent - for m in self.members: - if m.isAttr() or m.isMethod(): - if m.isStatic(): - raise WebIDLError( - "Don't mark things explicitly static " "in namespaces", - [self.location, m.location], - ) - # Just mark all our methods/attributes as static. The other - # option is to duplicate the relevant InterfaceMembers - # production bits but modified to produce static stuff to - # start with, but that sounds annoying. - m.forceStatic() - - if self.parent: - self.parent.finish(scope) - self.parent._hasChildInterfaces = True - - self.totalMembersInSlots = self.parent.totalMembersInSlots - - # Interfaces with [Global] must not have anything inherit from them - if self.parent.getExtendedAttribute("Global"): - # Note: This is not a self.parent.isOnGlobalProtoChain() check - # because ancestors of a [Global] interface can have other - # descendants. - raise WebIDLError( - "[Global] interface has another interface " "inheriting from it", - [self.location, self.parent.location], - ) - - # Make sure that we're not exposed in places where our parent is not - if not self.exposureSet.issubset(self.parent.exposureSet): - raise WebIDLError( - "Interface %s is exposed in globals where its " - "parent interface %s is not exposed." - % (self.identifier.name, self.parent.identifier.name), - [self.location, self.parent.location], - ) - - # Callbacks must not inherit from non-callbacks. - # XXXbz Can non-callbacks inherit from callbacks? Spec issue pending. - if self.isCallback(): - if not self.parent.isCallback(): - raise WebIDLError( - "Callback interface %s inheriting from " - "non-callback interface %s" - % (self.identifier.name, self.parent.identifier.name), - [self.location, self.parent.location], - ) - elif self.parent.isCallback(): - raise WebIDLError( - "Non-callback interface %s inheriting from " - "callback interface %s" - % (self.identifier.name, self.parent.identifier.name), - [self.location, self.parent.location], - ) - - # Interfaces which have interface objects can't inherit - # from [LegacyNoInterfaceObject] interfaces. - if self.parent.getExtendedAttribute( - "LegacyNoInterfaceObject" - ) and not self.getExtendedAttribute("LegacyNoInterfaceObject"): - raise WebIDLError( - "Interface %s does not have " - "[LegacyNoInterfaceObject] but inherits from " - "interface %s which does" - % (self.identifier.name, self.parent.identifier.name), - [self.location, self.parent.location], - ) - - # Interfaces that are not [SecureContext] can't inherit - # from [SecureContext] interfaces. - if self.parent.getExtendedAttribute( - "SecureContext" - ) and not self.getExtendedAttribute("SecureContext"): - raise WebIDLError( - "Interface %s does not have " - "[SecureContext] but inherits from " - "interface %s which does" - % (self.identifier.name, self.parent.identifier.name), - [self.location, self.parent.location], - ) - - for mixin in self.includedMixins: - mixin.finish(scope) - - cycleInGraph = self.findInterfaceLoopPoint(self) - if cycleInGraph: - raise WebIDLError( - "Interface %s has itself as ancestor" % self.identifier.name, - [self.location, cycleInGraph.location], - ) - - self.finishMembers(scope) - - ctor = self.ctor() - if ctor is not None: - if not self.hasInterfaceObject(): - raise WebIDLError( - "Can't have both a constructor and [LegacyNoInterfaceObject]", - [self.location, ctor.location], - ) - - if self.globalNames: - raise WebIDLError( - "Can't have both a constructor and [Global]", - [self.location, ctor.location], - ) - - assert ctor._exposureGlobalNames == self._exposureGlobalNames - ctor._exposureGlobalNames.update(self._exposureGlobalNames) - # Remove the constructor operation from our member list so - # it doesn't get in the way later. - self.members.remove(ctor) - - for ctor in self.legacyFactoryFunctions: - if self.globalNames: - raise WebIDLError( - "Can't have both a legacy factory function and [Global]", - [self.location, ctor.location], - ) - assert len(ctor._exposureGlobalNames) == 0 - ctor._exposureGlobalNames.update(self._exposureGlobalNames) - ctor.finish(scope) - - # Make a copy of our member list, so things that implement us - # can get those without all the stuff we implement ourselves - # admixed. - self.originalMembers = list(self.members) - - for mixin in sorted(self.includedMixins, key=lambda x: x.identifier.name): - for mixinMember in mixin.members: - for member in self.members: - if mixinMember.identifier.name == member.identifier.name: - raise WebIDLError( - "Multiple definitions of %s on %s coming from 'includes' statements" - % (member.identifier.name, self), - [mixinMember.location, member.location], - ) - self.members.extend(mixin.members) - - for ancestor in self.getInheritedInterfaces(): - ancestor.interfacesBasedOnSelf.add(self) - if ( - ancestor.maplikeOrSetlikeOrIterable is not None - and self.maplikeOrSetlikeOrIterable is not None - ): - raise WebIDLError( - "Cannot have maplike/setlike on %s that " - "inherits %s, which is already " - "maplike/setlike" - % (self.identifier.name, ancestor.identifier.name), - [ - self.maplikeOrSetlikeOrIterable.location, - ancestor.maplikeOrSetlikeOrIterable.location, - ], - ) - - # Deal with interfaces marked [LegacyUnforgeable], now that we have our full - # member list, except unforgeables pulled in from parents. We want to - # do this before we set "originatingInterface" on our unforgeable - # members. - if self.getExtendedAttribute("LegacyUnforgeable"): - # Check that the interface already has all the things the - # spec would otherwise require us to synthesize and is - # missing the ones we plan to synthesize. - if not any(m.isMethod() and m.isStringifier() for m in self.members): - raise WebIDLError( - "LegacyUnforgeable interface %s does not have a " - "stringifier" % self.identifier.name, - [self.location], - ) - - for m in self.members: - if m.identifier.name == "toJSON": - raise WebIDLError( - "LegacyUnforgeable interface %s has a " - "toJSON so we won't be able to add " - "one ourselves" % self.identifier.name, - [self.location, m.location], - ) - - if m.identifier.name == "valueOf" and not m.isStatic(): - raise WebIDLError( - "LegacyUnforgeable interface %s has a valueOf " - "member so we won't be able to add one " - "ourselves" % self.identifier.name, - [self.location, m.location], - ) - - for member in self.members: - if ( - (member.isAttr() or member.isMethod()) - and member.isLegacyUnforgeable() - and not hasattr(member, "originatingInterface") - ): - member.originatingInterface = self - - for member in self.members: - if ( - member.isMethod() and member.getExtendedAttribute("CrossOriginCallable") - ) or ( - member.isAttr() - and ( - member.getExtendedAttribute("CrossOriginReadable") - or member.getExtendedAttribute("CrossOriginWritable") - ) - ): - self.hasCrossOriginMembers = True - break - - if self.hasCrossOriginMembers: - parent = self - while parent: - parent.hasDescendantWithCrossOriginMembers = True - parent = parent.parent - - # Compute slot indices for our members before we pull in unforgeable - # members from our parent. Also, maplike/setlike declarations get a - # slot to hold their backing object. - for member in self.members: - if ( - member.isAttr() - and ( - member.getExtendedAttribute("StoreInSlot") - or member.getExtendedAttribute("Cached") - or member.type.isObservableArray() - ) - ) or member.isMaplikeOrSetlike(): - if self.isJSImplemented() and not member.isMaplikeOrSetlike(): - raise WebIDLError( - "Interface %s is JS-implemented and we " - "don't support [Cached] or [StoreInSlot] or ObservableArray " - "on JS-implemented interfaces" % self.identifier.name, - [self.location, member.location], - ) - if member.slotIndices is None: - member.slotIndices = dict() - member.slotIndices[self.identifier.name] = self.totalMembersInSlots - self.totalMembersInSlots += 1 - if member.getExtendedAttribute("StoreInSlot"): - self._ownMembersInSlots += 1 - - if self.parent: - # Make sure we don't shadow any of the [LegacyUnforgeable] attributes on our - # ancestor interfaces. We don't have to worry about mixins here, because - # those have already been imported into the relevant .members lists. And - # we don't have to worry about anything other than our parent, because it - # has already imported its ancestors' unforgeable attributes into its - # member list. - for unforgeableMember in ( - member - for member in self.parent.members - if (member.isAttr() or member.isMethod()) - and member.isLegacyUnforgeable() - ): - shadows = [ - m - for m in self.members - if (m.isAttr() or m.isMethod()) - and not m.isStatic() - and m.identifier.name == unforgeableMember.identifier.name - ] - if len(shadows) != 0: - locs = [unforgeableMember.location] + [s.location for s in shadows] - raise WebIDLError( - "Interface %s shadows [LegacyUnforgeable] " - "members of %s" - % (self.identifier.name, ancestor.identifier.name), - locs, - ) - # And now just stick it in our members, since we won't be - # inheriting this down the proto chain. If we really cared we - # could try to do something where we set up the unforgeable - # attributes/methods of ancestor interfaces, with their - # corresponding getters, on our interface, but that gets pretty - # complicated and seems unnecessary. - self.members.append(unforgeableMember) - - # At this point, we have all of our members. If the current interface - # uses maplike/setlike, check for collisions anywhere in the current - # interface or higher in the inheritance chain. - if self.maplikeOrSetlikeOrIterable: - testInterface = self - isAncestor = False - while testInterface: - self.maplikeOrSetlikeOrIterable.checkCollisions( - testInterface.members, isAncestor - ) - isAncestor = True - testInterface = testInterface.parent - - # Ensure that there's at most one of each {named,indexed} - # {getter,setter,deleter}, at most one stringifier, - # and at most one legacycaller. Note that this last is not - # quite per spec, but in practice no one overloads - # legacycallers. Also note that in practice we disallow - # indexed deleters, but it simplifies some other code to - # treat deleter analogously to getter/setter by - # prefixing it with "named". - specialMembersSeen = {} - for member in self.members: - if not member.isMethod(): - continue - - if member.isGetter(): - memberType = "getters" - elif member.isSetter(): - memberType = "setters" - elif member.isDeleter(): - memberType = "deleters" - elif member.isStringifier(): - memberType = "stringifiers" - elif member.isLegacycaller(): - memberType = "legacycallers" - else: - continue - - if memberType != "stringifiers" and memberType != "legacycallers": - if member.isNamed(): - memberType = "named " + memberType - else: - assert member.isIndexed() - memberType = "indexed " + memberType - - if memberType in specialMembersSeen: - raise WebIDLError( - "Multiple " + memberType + " on %s" % (self), - [ - self.location, - specialMembersSeen[memberType].location, - member.location, - ], - ) - - specialMembersSeen[memberType] = member - - if self.getExtendedAttribute("LegacyUnenumerableNamedProperties"): - # Check that we have a named getter. - if "named getters" not in specialMembersSeen: - raise WebIDLError( - "Interface with [LegacyUnenumerableNamedProperties] does " - "not have a named getter", - [self.location], - ) - ancestor = self.parent - while ancestor: - if ancestor.getExtendedAttribute("LegacyUnenumerableNamedProperties"): - raise WebIDLError( - "Interface with [LegacyUnenumerableNamedProperties] " - "inherits from another interface with " - "[LegacyUnenumerableNamedProperties]", - [self.location, ancestor.location], - ) - ancestor = ancestor.parent - - if self._isOnGlobalProtoChain: - # Make sure we have no named setters or deleters - for memberType in ["setter", "deleter"]: - memberId = "named " + memberType + "s" - if memberId in specialMembersSeen: - raise WebIDLError( - "Interface with [Global] has a named %s" % memberType, - [self.location, specialMembersSeen[memberId].location], - ) - # Make sure we're not [LegacyOverrideBuiltIns] - if self.getExtendedAttribute("LegacyOverrideBuiltIns"): - raise WebIDLError( - "Interface with [Global] also has " "[LegacyOverrideBuiltIns]", - [self.location], - ) - # Mark all of our ancestors as being on the global's proto chain too - parent = self.parent - while parent: - # Must not inherit from an interface with [LegacyOverrideBuiltIns] - if parent.getExtendedAttribute("LegacyOverrideBuiltIns"): - raise WebIDLError( - "Interface with [Global] inherits from " - "interface with [LegacyOverrideBuiltIns]", - [self.location, parent.location], - ) - parent._isOnGlobalProtoChain = True - parent = parent.parent - - def validate(self): - def checkDuplicateNames(member, name, attributeName): - for m in self.members: - if m.identifier.name == name: - raise WebIDLError( - "[%s=%s] has same name as interface member" - % (attributeName, name), - [member.location, m.location], - ) - if m.isMethod() and m != member and name in m.aliases: - raise WebIDLError( - "conflicting [%s=%s] definitions" % (attributeName, name), - [member.location, m.location], - ) - if m.isAttr() and m != member and name in m.bindingAliases: - raise WebIDLError( - "conflicting [%s=%s] definitions" % (attributeName, name), - [member.location, m.location], - ) - - # We also don't support inheriting from unforgeable interfaces. - if self.getExtendedAttribute("LegacyUnforgeable") and self.hasChildInterfaces(): - locations = [self.location] + list( - i.location for i in self.interfacesBasedOnSelf if i.parent == self - ) - raise WebIDLError( - "%s is an unforgeable ancestor interface" % self.identifier.name, - locations, - ) - - ctor = self.ctor() - if ctor is not None: - ctor.validate() - for namedCtor in self.legacyFactoryFunctions: - namedCtor.validate() - - indexedGetter = None - hasLengthAttribute = False - for member in self.members: - member.validate() - - if self.isCallback() and member.getExtendedAttribute("Replaceable"): - raise WebIDLError( - "[Replaceable] used on an attribute on " - "interface %s which is a callback interface" % self.identifier.name, - [self.location, member.location], - ) - - # Check that PutForwards refers to another attribute and that no - # cycles exist in forwarded assignments. Also check for a - # integer-typed "length" attribute. - if member.isAttr(): - if member.identifier.name == "length" and member.type.isInteger(): - hasLengthAttribute = True - - iface = self - attr = member - putForwards = attr.getExtendedAttribute("PutForwards") - if putForwards and self.isCallback(): - raise WebIDLError( - "[PutForwards] used on an attribute " - "on interface %s which is a callback " - "interface" % self.identifier.name, - [self.location, member.location], - ) - - while putForwards is not None: - forwardIface = attr.type.unroll().inner - fowardAttr = None - - for forwardedMember in forwardIface.members: - if ( - not forwardedMember.isAttr() - or forwardedMember.identifier.name != putForwards[0] - ): - continue - if forwardedMember == member: - raise WebIDLError( - "Cycle detected in forwarded " - "assignments for attribute %s on " - "%s" % (member.identifier.name, self), - [member.location], - ) - fowardAttr = forwardedMember - break - - if fowardAttr is None: - raise WebIDLError( - "Attribute %s on %s forwards to " - "missing attribute %s" - % (attr.identifier.name, iface, putForwards), - [attr.location], - ) - - iface = forwardIface - attr = fowardAttr - putForwards = attr.getExtendedAttribute("PutForwards") - - # Check that the name of an [Alias] doesn't conflict with an - # interface member and whether we support indexed properties. - if member.isMethod(): - if member.isGetter() and member.isIndexed(): - indexedGetter = member - - for alias in member.aliases: - if self.isOnGlobalProtoChain(): - raise WebIDLError( - "[Alias] must not be used on a " - "[Global] interface operation", - [member.location], - ) - if ( - member.getExtendedAttribute("Exposed") - or member.getExtendedAttribute("ChromeOnly") - or member.getExtendedAttribute("Pref") - or member.getExtendedAttribute("Func") - or member.getExtendedAttribute("Trial") - or member.getExtendedAttribute("SecureContext") - ): - raise WebIDLError( - "[Alias] must not be used on a " - "conditionally exposed operation", - [member.location], - ) - if member.isStatic(): - raise WebIDLError( - "[Alias] must not be used on a " "static operation", - [member.location], - ) - if member.isIdentifierLess(): - raise WebIDLError( - "[Alias] must not be used on an " - "identifierless operation", - [member.location], - ) - if member.isLegacyUnforgeable(): - raise WebIDLError( - "[Alias] must not be used on an " - "[LegacyUnforgeable] operation", - [member.location], - ) - - checkDuplicateNames(member, alias, "Alias") - - # Check that the name of a [BindingAlias] doesn't conflict with an - # interface member. - if member.isAttr(): - for bindingAlias in member.bindingAliases: - checkDuplicateNames(member, bindingAlias, "BindingAlias") - - # Conditional exposure makes no sense for interfaces with no - # interface object. - # And SecureContext makes sense for interfaces with no interface object, - # since it is also propagated to interface members. - if ( - self.isExposedConditionally(exclusions=["SecureContext"]) - and not self.hasInterfaceObject() - ): - raise WebIDLError( - "Interface with no interface object is " "exposed conditionally", - [self.location], - ) - - # Value iterators are only allowed on interfaces with indexed getters, - # and pair iterators are only allowed on interfaces without indexed - # getters. - if self.isIterable(): - iterableDecl = self.maplikeOrSetlikeOrIterable - if iterableDecl.isValueIterator(): - if not indexedGetter: - raise WebIDLError( - "Interface with value iterator does not " - "support indexed properties", - [self.location, iterableDecl.location], - ) - - if iterableDecl.valueType != indexedGetter.signatures()[0][0]: - raise WebIDLError( - "Iterable type does not match indexed " "getter type", - [iterableDecl.location, indexedGetter.location], - ) - - if not hasLengthAttribute: - raise WebIDLError( - "Interface with value iterator does not " - 'have an integer-typed "length" attribute', - [self.location, iterableDecl.location], - ) - else: - assert iterableDecl.isPairIterator() - if indexedGetter: - raise WebIDLError( - "Interface with pair iterator supports " "indexed properties", - [self.location, iterableDecl.location, indexedGetter.location], - ) - - if indexedGetter and not hasLengthAttribute: - raise WebIDLError( - "Interface with an indexed getter does not have " - 'an integer-typed "length" attribute', - [self.location, indexedGetter.location], - ) - - def setCallback(self, value): - self._callback = value - - def isCallback(self): - return self._callback - - def isSingleOperationInterface(self): - assert self.isCallback() or self.isJSImplemented() - return ( - # JS-implemented things should never need the - # this-handling weirdness of single-operation interfaces. - not self.isJSImplemented() - and - # Not inheriting from another interface - not self.parent - and - # No attributes of any kinds - not any(m.isAttr() for m in self.members) - and - # There is at least one regular operation, and all regular - # operations have the same identifier - len( - set( - m.identifier.name - for m in self.members - if m.isMethod() and not m.isStatic() - ) - ) - == 1 - ) - - def inheritanceDepth(self): - depth = 0 - parent = self.parent - while parent: - depth = depth + 1 - parent = parent.parent - return depth - - def hasConstants(self): - return any(m.isConst() for m in self.members) - - def hasInterfaceObject(self): - if self.isCallback(): - return self.hasConstants() - return not hasattr(self, "_noInterfaceObject") and not self.isPseudoInterface() - - def hasInterfacePrototypeObject(self): - return ( - not self.isCallback() - and not self.isNamespace() - and self.getUserData("hasConcreteDescendant", False) - and not self.isPseudoInterface() - ) - - def addIncludedMixin(self, includedMixin): - assert isinstance(includedMixin, IDLInterfaceMixin) - self.includedMixins.add(includedMixin) - - def getInheritedInterfaces(self): - """ - Returns a list of the interfaces this interface inherits from - (not including this interface itself). The list is in order - from most derived to least derived. - """ - assert self._finished - if not self.parent: - return [] - parentInterfaces = self.parent.getInheritedInterfaces() - parentInterfaces.insert(0, self.parent) - return parentInterfaces - - def findInterfaceLoopPoint(self, otherInterface): - """ - Finds an interface amongst our ancestors that inherits from otherInterface. - If there is no such interface, returns None. - """ - if self.parent: - if self.parent == otherInterface: - return self - loopPoint = self.parent.findInterfaceLoopPoint(otherInterface) - if loopPoint: - return loopPoint - return None - - def setNonPartial(self, location, parent, members): - assert not parent or isinstance(parent, IDLIdentifierPlaceholder) - IDLInterfaceOrInterfaceMixinOrNamespace.setNonPartial(self, location, members) - assert not self.parent - self.parent = parent - - def getJSImplementation(self): - classId = self.getExtendedAttribute("JSImplementation") - if not classId: - return classId - assert isinstance(classId, list) - assert len(classId) == 1 - return classId[0] - - def isJSImplemented(self): - return bool(self.getJSImplementation()) - - def hasProbablyShortLivingWrapper(self): - current = self - while current: - if current.getExtendedAttribute("ProbablyShortLivingWrapper"): - return True - current = current.parent - return False - - def hasChildInterfaces(self): - return self._hasChildInterfaces - - def isOnGlobalProtoChain(self): - return self._isOnGlobalProtoChain - - def isPseudoInterface(self): - return self._isPseudo - - def _getDependentObjects(self): - deps = set(self.members) - deps.update(self.includedMixins) - if self.parent: - deps.add(self.parent) - return deps - - def hasMembersInSlots(self): - return self._ownMembersInSlots != 0 - - conditionExtendedAttributes = [ - "Pref", - "ChromeOnly", - "Func", - "Trial", - "SecureContext", - ] - - def isExposedConditionally(self, exclusions=[]): - return any( - ((not a in exclusions) and self.getExtendedAttribute(a)) - for a in self.conditionExtendedAttributes - ) - - -class IDLInterface(IDLInterfaceOrNamespace): - def __init__( - self, - location, - parentScope, - name, - parent, - members, - isKnownNonPartial, - classNameOverride=None, - ): - IDLInterfaceOrNamespace.__init__( - self, location, parentScope, name, parent, members, isKnownNonPartial - ) - self.classNameOverride = classNameOverride - - def __str__(self): - return "Interface '%s'" % self.identifier.name - - def isInterface(self): - return True - - def getClassName(self): - if self.classNameOverride: - return self.classNameOverride - return IDLInterfaceOrNamespace.getClassName(self) - - def addExtendedAttributes(self, attrs): - for attr in attrs: - identifier = attr.identifier() - - # Special cased attrs - if identifier == "TreatNonCallableAsNull": - raise WebIDLError( - "TreatNonCallableAsNull cannot be specified on interfaces", - [attr.location, self.location], - ) - if identifier == "LegacyTreatNonObjectAsNull": - raise WebIDLError( - "LegacyTreatNonObjectAsNull cannot be specified on interfaces", - [attr.location, self.location], - ) - elif identifier == "LegacyNoInterfaceObject": - if not attr.noArguments(): - raise WebIDLError( - "[LegacyNoInterfaceObject] must take no arguments", - [attr.location], - ) - - self._noInterfaceObject = True - elif identifier == "LegacyFactoryFunction": - if not attr.hasValue(): - raise WebIDLError( - "LegacyFactoryFunction must either take an identifier or take a named argument list", - [attr.location], - ) - - args = attr.args() if attr.hasArgs() else [] - - retType = IDLWrapperType(self.location, self) - - method = IDLConstructor(attr.location, args, attr.value()) - method.reallyInit(self) - - # Named constructors are always assumed to be able to - # throw (since there's no way to indicate otherwise). - method.addExtendedAttributes( - [IDLExtendedAttribute(self.location, ("Throws",))] - ) - - # We need to detect conflicts for LegacyFactoryFunctions across - # interfaces. We first call resolve on the parentScope, - # which will merge all LegacyFactoryFunctions with the same - # identifier accross interfaces as overloads. - method.resolve(self.parentScope) - - # Then we look up the identifier on the parentScope. If the - # result is the same as the method we're adding then it - # hasn't been added as an overload and it's the first time - # we've encountered a LegacyFactoryFunction with that identifier. - # If the result is not the same as the method we're adding - # then it has been added as an overload and we need to check - # whether the result is actually one of our existing - # LegacyFactoryFunctions. - newMethod = self.parentScope.lookupIdentifier(method.identifier) - if newMethod == method: - self.legacyFactoryFunctions.append(method) - elif newMethod not in self.legacyFactoryFunctions: - raise WebIDLError( - "LegacyFactoryFunction conflicts with a " - "LegacyFactoryFunction of a different interface", - [method.location, newMethod.location], - ) - elif identifier == "ExceptionClass": - if not attr.noArguments(): - raise WebIDLError( - "[ExceptionClass] must take no arguments", [attr.location] - ) - if self.parent: - raise WebIDLError( - "[ExceptionClass] must not be specified on " - "an interface with inherited interfaces", - [attr.location, self.location], - ) - elif identifier == "Global": - if attr.hasValue(): - self.globalNames = [attr.value()] - elif attr.hasArgs(): - self.globalNames = attr.args() - else: - self.globalNames = [self.identifier.name] - self.parentScope.addIfaceGlobalNames( - self.identifier.name, self.globalNames - ) - self._isOnGlobalProtoChain = True - elif identifier == "LegacyWindowAlias": - if attr.hasValue(): - self.legacyWindowAliases = [attr.value()] - elif attr.hasArgs(): - self.legacyWindowAliases = attr.args() - else: - raise WebIDLError( - "[%s] must either take an identifier " - "or take an identifier list" % identifier, - [attr.location], - ) - for alias in self.legacyWindowAliases: - unresolved = IDLUnresolvedIdentifier(attr.location, alias) - IDLObjectWithIdentifier(attr.location, self.parentScope, unresolved) - elif identifier == "SecureContext": - if not attr.noArguments(): - raise WebIDLError( - "[%s] must take no arguments" % identifier, [attr.location] - ) - # This gets propagated to all our members. - for member in self.members: - if member.getExtendedAttribute("SecureContext"): - raise WebIDLError( - "[SecureContext] specified on both " - "an interface member and on the " - "interface itself", - [member.location, attr.location], - ) - member.addExtendedAttributes([attr]) - elif ( - identifier == "NeedResolve" - or identifier == "LegacyOverrideBuiltIns" - or identifier == "ChromeOnly" - or identifier == "LegacyUnforgeable" - or identifier == "LegacyEventInit" - or identifier == "ProbablyShortLivingWrapper" - or identifier == "LegacyUnenumerableNamedProperties" - or identifier == "RunConstructorInCallerCompartment" - or identifier == "WantsEventListenerHooks" - or identifier == "Serializable" - or identifier == "Abstract" - or identifier == "Inline" - ): - # Known extended attributes that do not take values - if not attr.noArguments(): - raise WebIDLError( - "[%s] must take no arguments" % identifier, [attr.location] - ) - elif identifier == "Exposed": - convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) - elif ( - identifier == "Pref" - or identifier == "JSImplementation" - or identifier == "HeaderFile" - or identifier == "Func" - or identifier == "Trial" - or identifier == "Deprecated" - ): - # Known extended attributes that take a string value - if not attr.hasValue(): - raise WebIDLError( - "[%s] must have a value" % identifier, [attr.location] - ) - elif identifier == "InstrumentedProps": - # Known extended attributes that take a list - if not attr.hasArgs(): - raise WebIDLError( - "[%s] must have arguments" % identifier, [attr.location] - ) - else: - raise WebIDLError( - "Unknown extended attribute %s on interface" % identifier, - [attr.location], - ) - - attrlist = attr.listValue() - self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True - - def validate(self): - IDLInterfaceOrNamespace.validate(self) - if self.parent and self.isSerializable() and not self.parent.isSerializable(): - raise WebIDLError( - "Serializable interface inherits from non-serializable " - "interface. Per spec, that means the object should not be " - "serializable, so chances are someone made a mistake here " - "somewhere.", - [self.location, self.parent.location], - ) - - def isSerializable(self): - return self.getExtendedAttribute("Serializable") - - def setNonPartial(self, location, parent, members): - # Before we do anything else, finish initializing any constructors that - # might be in "members", so we don't have partially-initialized objects - # hanging around. We couldn't do it before now because we needed to have - # to have the IDLInterface on hand to properly set the return type. - for member in members: - if isinstance(member, IDLConstructor): - member.reallyInit(self) - - IDLInterfaceOrNamespace.setNonPartial(self, location, parent, members) - - -class IDLNamespace(IDLInterfaceOrNamespace): - def __init__(self, location, parentScope, name, members, isKnownNonPartial): - IDLInterfaceOrNamespace.__init__( - self, location, parentScope, name, None, members, isKnownNonPartial - ) - - def __str__(self): - return "Namespace '%s'" % self.identifier.name - - def isNamespace(self): - return True - - def addExtendedAttributes(self, attrs): - # The set of things namespaces support is small enough it's simpler - # to factor out into a separate method than it is to sprinkle - # isNamespace() checks all through - # IDLInterfaceOrNamespace.addExtendedAttributes. - for attr in attrs: - identifier = attr.identifier() - - if identifier == "Exposed": - convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) - elif identifier == "ClassString": - # Takes a string value to override the default "Object" if - # desired. - if not attr.hasValue(): - raise WebIDLError( - "[%s] must have a value" % identifier, [attr.location] - ) - elif identifier == "ProtoObjectHack" or identifier == "ChromeOnly": - if not attr.noArguments(): - raise WebIDLError( - "[%s] must not have arguments" % identifier, [attr.location] - ) - elif ( - identifier == "Pref" - or identifier == "HeaderFile" - or identifier == "Func" - or identifier == "Trial" - ): - # Known extended attributes that take a string value - if not attr.hasValue(): - raise WebIDLError( - "[%s] must have a value" % identifier, [attr.location] - ) - else: - raise WebIDLError( - "Unknown extended attribute %s on namespace" % identifier, - [attr.location], - ) - - attrlist = attr.listValue() - self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True - - def isSerializable(self): - return False - - -class IDLDictionary(IDLObjectWithScope): - def __init__(self, location, parentScope, name, parent, members): - assert isinstance(parentScope, IDLScope) - assert isinstance(name, IDLUnresolvedIdentifier) - assert not parent or isinstance(parent, IDLIdentifierPlaceholder) - - self.parent = parent - self._finished = False - self.members = list(members) - self._partialDictionaries = [] - self._extendedAttrDict = {} - self.needsConversionToJS = False - self.needsConversionFromJS = False - - IDLObjectWithScope.__init__(self, location, parentScope, name) - - def __str__(self): - return "Dictionary '%s'" % self.identifier.name - - def isDictionary(self): - return True - - def canBeEmpty(self): - """ - Returns true if this dictionary can be empty (that is, it has no - required members and neither do any of its ancestors). - """ - return all(member.optional for member in self.members) and ( - not self.parent or self.parent.canBeEmpty() - ) - - def finish(self, scope): - if self._finished: - return - - self._finished = True - - if self.parent: - assert isinstance(self.parent, IDLIdentifierPlaceholder) - oldParent = self.parent - self.parent = self.parent.finish(scope) - if not isinstance(self.parent, IDLDictionary): - raise WebIDLError( - "Dictionary %s has parent that is not a dictionary" - % self.identifier.name, - [oldParent.location, self.parent.location], - ) - - # Make sure the parent resolves all its members before we start - # looking at them. - self.parent.finish(scope) - - # Now go ahead and merge in our partial dictionaries. - for partial in self._partialDictionaries: - partial.finish(scope) - self.members.extend(partial.members) - - for member in self.members: - member.resolve(self) - if not member.isComplete(): - member.complete(scope) - assert member.type.isComplete() - - # Members of a dictionary are sorted in lexicographic order, - # unless the dictionary opts out. - if not self.getExtendedAttribute("Unsorted"): - self.members.sort(key=lambda x: x.identifier.name) - - inheritedMembers = [] - ancestor = self.parent - while ancestor: - if ancestor == self: - raise WebIDLError( - "Dictionary %s has itself as an ancestor" % self.identifier.name, - [self.identifier.location], - ) - inheritedMembers.extend(ancestor.members) - ancestor = ancestor.parent - - # Catch name duplication - for inheritedMember in inheritedMembers: - for member in self.members: - if member.identifier.name == inheritedMember.identifier.name: - raise WebIDLError( - "Dictionary %s has two members with name %s" - % (self.identifier.name, member.identifier.name), - [member.location, inheritedMember.location], - ) - - def validate(self): - def typeContainsDictionary(memberType, dictionary): - """ - Returns a tuple whose: - - - First element is a Boolean value indicating whether - memberType contains dictionary. - - - Second element is: - A list of locations that leads from the type that was passed in - the memberType argument, to the dictionary being validated, - if the boolean value in the first element is True. - - None, if the boolean value in the first element is False. - """ - - if ( - memberType.nullable() - or memberType.isSequence() - or memberType.isRecord() - ): - return typeContainsDictionary(memberType.inner, dictionary) - - if memberType.isDictionary(): - if memberType.inner == dictionary: - return (True, [memberType.location]) - - (contains, locations) = dictionaryContainsDictionary( - memberType.inner, dictionary - ) - if contains: - return (True, [memberType.location] + locations) - - if memberType.isUnion(): - for member in memberType.flatMemberTypes: - (contains, locations) = typeContainsDictionary(member, dictionary) - if contains: - return (True, locations) - - return (False, None) - - def dictionaryContainsDictionary(dictMember, dictionary): - for member in dictMember.members: - (contains, locations) = typeContainsDictionary(member.type, dictionary) - if contains: - return (True, [member.location] + locations) - - if dictMember.parent: - if dictMember.parent == dictionary: - return (True, [dictMember.location]) - else: - (contains, locations) = dictionaryContainsDictionary( - dictMember.parent, dictionary - ) - if contains: - return (True, [dictMember.location] + locations) - - return (False, None) - - for member in self.members: - if member.type.isDictionary() and member.type.nullable(): - raise WebIDLError( - "Dictionary %s has member with nullable " - "dictionary type" % self.identifier.name, - [member.location], - ) - (contains, locations) = typeContainsDictionary(member.type, self) - if contains: - raise WebIDLError( - "Dictionary %s has member with itself as type." - % self.identifier.name, - [member.location] + locations, - ) - - if member.type.isUndefined(): - raise WebIDLError( - "Dictionary %s has member with undefined as its type." - % self.identifier.name, - [member.location], - ) - elif member.type.isUnion(): - for unionMember in member.type.unroll().flatMemberTypes: - if unionMember.isUndefined(): - raise WebIDLError( - "Dictionary %s has member with a union containing " - "undefined as a type." % self.identifier.name, - [unionMember.location], - ) - - def getExtendedAttribute(self, name): - return self._extendedAttrDict.get(name, None) - - def addExtendedAttributes(self, attrs): - for attr in attrs: - identifier = attr.identifier() - - if identifier == "GenerateInitFromJSON" or identifier == "GenerateInit": - if not attr.noArguments(): - raise WebIDLError( - "[%s] must not have arguments" % identifier, [attr.location] - ) - self.needsConversionFromJS = True - elif ( - identifier == "GenerateConversionToJS" or identifier == "GenerateToJSON" - ): - if not attr.noArguments(): - raise WebIDLError( - "[%s] must not have arguments" % identifier, [attr.location] - ) - # ToJSON methods require to-JS conversion, because we - # implement ToJSON by converting to a JS object and - # then using JSON.stringify. - self.needsConversionToJS = True - elif identifier == "Unsorted": - if not attr.noArguments(): - raise WebIDLError( - "[Unsorted] must take no arguments", [attr.location] - ) - else: - raise WebIDLError( - "[%s] extended attribute not allowed on " - "dictionaries" % identifier, - [attr.location], - ) - - self._extendedAttrDict[identifier] = True - - def _getDependentObjects(self): - deps = set(self.members) - if self.parent: - deps.add(self.parent) - return deps - - def addPartialDictionary(self, partial): - assert self.identifier.name == partial.identifier.name - self._partialDictionaries.append(partial) - - -class IDLEnum(IDLObjectWithIdentifier): - def __init__(self, location, parentScope, name, values): - assert isinstance(parentScope, IDLScope) - assert isinstance(name, IDLUnresolvedIdentifier) - - if len(values) != len(set(values)): - raise WebIDLError( - "Enum %s has multiple identical strings" % name.name, [location] - ) - - IDLObjectWithIdentifier.__init__(self, location, parentScope, name) - self._values = values - - def values(self): - return self._values - - def finish(self, scope): - pass - - def validate(self): - pass - - def isEnum(self): - return True - - def addExtendedAttributes(self, attrs): - if len(attrs) != 0: - raise WebIDLError( - "There are no extended attributes that are " "allowed on enums", - [attrs[0].location, self.location], - ) - - def _getDependentObjects(self): - return set() - - -class IDLType(IDLObject): - Tags = enum( - # The integer types - "int8", - "uint8", - "int16", - "uint16", - "int32", - "uint32", - "int64", - "uint64", - # Additional primitive types - "bool", - "unrestricted_float", - "float", - "unrestricted_double", - # "double" last primitive type to match IDLBuiltinType - "double", - # Other types - "any", - "undefined", - "domstring", - "bytestring", - "usvstring", - "utf8string", - "jsstring", - "object", - # Funny stuff - "interface", - "dictionary", - "enum", - "callback", - "union", - "sequence", - "record", - "promise", - "observablearray", - ) - - def __init__(self, location, name): - IDLObject.__init__(self, location) - self.name = name - self.builtin = False - self.legacyNullToEmptyString = False - self._clamp = False - self._enforceRange = False - self._allowShared = False - self._extendedAttrDict = {} - - def __hash__(self): - return ( - hash(self.builtin) - + hash(self.name) - + hash(self._clamp) - + hash(self._enforceRange) - + hash(self.legacyNullToEmptyString) - + hash(self._allowShared) - ) - - def __eq__(self, other): - return ( - other - and self.builtin == other.builtin - and self.name == other.name - and self._clamp == other.hasClamp() - and self._enforceRange == other.hasEnforceRange() - and self.legacyNullToEmptyString == other.legacyNullToEmptyString - and self._allowShared == other.hasAllowShared() - ) - - def __ne__(self, other): - return not self == other - - def __str__(self): - return str(self.name) - - def prettyName(self): - """ - A name that looks like what this type is named in the IDL spec. By default - this is just our .name, but types that have more interesting spec - representations should override this. - """ - return str(self.name) - - def isType(self): - return True - - def nullable(self): - return False - - def isPrimitive(self): - return False - - def isBoolean(self): - return False - - def isNumeric(self): - return False - - def isString(self): - return False - - def isByteString(self): - return False - - def isDOMString(self): - return False - - def isUSVString(self): - return False - - def isUTF8String(self): - return False - - def isJSString(self): - return False - - def isUndefined(self): - return False - - def isSequence(self): - return False - - def isRecord(self): - return False - - def isReadableStream(self): - return False - - def isArrayBuffer(self): - return False - - def isArrayBufferView(self): - return False - - def isTypedArray(self): - return False - - def isBufferSource(self): - return self.isArrayBuffer() or self.isArrayBufferView() or self.isTypedArray() - - def isCallbackInterface(self): - return False - - def isNonCallbackInterface(self): - return False - - def isGeckoInterface(self): - """Returns a boolean indicating whether this type is an 'interface' - type that is implemented in Gecko. At the moment, this returns - true for all interface types that are not types from the TypedArray - spec.""" - return self.isInterface() and not self.isSpiderMonkeyInterface() - - def isSpiderMonkeyInterface(self): - """Returns a boolean indicating whether this type is an 'interface' - type that is implemented in SpiderMonkey.""" - return self.isInterface() and (self.isBufferSource() or self.isReadableStream()) - - def isAny(self): - return self.tag() == IDLType.Tags.any - - def isObject(self): - return self.tag() == IDLType.Tags.object - - def isPromise(self): - return False - - def isComplete(self): - return True - - def includesRestrictedFloat(self): - return False - - def isFloat(self): - return False - - def isUnrestricted(self): - # Should only call this on float types - assert self.isFloat() - - def isJSONType(self): - return False - - def isObservableArray(self): - return False - - def isDictionaryLike(self): - return self.isDictionary() or self.isRecord() or self.isCallbackInterface() - - def hasClamp(self): - return self._clamp - - def hasEnforceRange(self): - return self._enforceRange - - def hasAllowShared(self): - return self._allowShared - - def tag(self): - assert False # Override me! - - def treatNonCallableAsNull(self): - assert self.tag() == IDLType.Tags.callback - return self.nullable() and self.inner.callback._treatNonCallableAsNull - - def treatNonObjectAsNull(self): - assert self.tag() == IDLType.Tags.callback - return self.nullable() and self.inner.callback._treatNonObjectAsNull - - def withExtendedAttributes(self, attrs): - if len(attrs) > 0: - raise WebIDLError( - "Extended attributes on types only supported for builtins", - [attrs[0].location, self.location], - ) - return self - - def getExtendedAttribute(self, name): - return self._extendedAttrDict.get(name, None) - - def resolveType(self, parentScope): - pass - - def unroll(self): - return self - - def isDistinguishableFrom(self, other): - raise TypeError( - "Can't tell whether a generic type is or is not " - "distinguishable from other things" - ) - - def isExposedInAllOf(self, exposureSet): - return True - - -class IDLUnresolvedType(IDLType): - """ - Unresolved types are interface types - """ - - def __init__(self, location, name, attrs=[]): - IDLType.__init__(self, location, name) - self.extraTypeAttributes = attrs - - def isComplete(self): - return False - - def complete(self, scope): - obj = None - try: - obj = scope._lookupIdentifier(self.name) - except: - raise WebIDLError("Unresolved type '%s'." % self.name, [self.location]) - - assert obj - assert not obj.isType() - if obj.isTypedef(): - assert self.name.name == obj.identifier.name - typedefType = IDLTypedefType(self.location, obj.innerType, obj.identifier) - assert not typedefType.isComplete() - return typedefType.complete(scope).withExtendedAttributes( - self.extraTypeAttributes - ) - elif obj.isCallback() and not obj.isInterface(): - assert self.name.name == obj.identifier.name - return IDLCallbackType(obj.location, obj) - - name = self.name.resolve(scope, None) - return IDLWrapperType(self.location, obj) - - def withExtendedAttributes(self, attrs): - return IDLUnresolvedType(self.location, self.name, attrs) - - def isDistinguishableFrom(self, other): - raise TypeError( - "Can't tell whether an unresolved type is or is not " - "distinguishable from other things" - ) - - -class IDLParametrizedType(IDLType): - def __init__(self, location, name, innerType): - IDLType.__init__(self, location, name) - self.builtin = False - self.inner = innerType - - def includesRestrictedFloat(self): - return self.inner.includesRestrictedFloat() - - def resolveType(self, parentScope): - assert isinstance(parentScope, IDLScope) - self.inner.resolveType(parentScope) - - def isComplete(self): - return self.inner.isComplete() - - def unroll(self): - return self.inner.unroll() - - def _getDependentObjects(self): - return self.inner._getDependentObjects() - - -class IDLNullableType(IDLParametrizedType): - def __init__(self, location, innerType): - assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any] - - IDLParametrizedType.__init__(self, location, None, innerType) - - def __hash__(self): - return hash(self.inner) - - def __eq__(self, other): - return isinstance(other, IDLNullableType) and self.inner == other.inner - - def __str__(self): - return self.inner.__str__() + "OrNull" - - def prettyName(self): - return self.inner.prettyName() + "?" - - def nullable(self): - return True - - def isCallback(self): - return self.inner.isCallback() - - def isPrimitive(self): - return self.inner.isPrimitive() - - def isBoolean(self): - return self.inner.isBoolean() - - def isNumeric(self): - return self.inner.isNumeric() - - def isString(self): - return self.inner.isString() - - def isByteString(self): - return self.inner.isByteString() - - def isDOMString(self): - return self.inner.isDOMString() - - def isUSVString(self): - return self.inner.isUSVString() - - def isUTF8String(self): - return self.inner.isUTF8String() - - def isJSString(self): - return self.inner.isJSString() - - def isFloat(self): - return self.inner.isFloat() - - def isUnrestricted(self): - return self.inner.isUnrestricted() - - def isInteger(self): - return self.inner.isInteger() - - def isUndefined(self): - return self.inner.isUndefined() - - def isSequence(self): - return self.inner.isSequence() - - def isRecord(self): - return self.inner.isRecord() - - def isReadableStream(self): - return self.inner.isReadableStream() - - def isArrayBuffer(self): - return self.inner.isArrayBuffer() - - def isArrayBufferView(self): - return self.inner.isArrayBufferView() - - def isTypedArray(self): - return self.inner.isTypedArray() - - def isDictionary(self): - return self.inner.isDictionary() - - def isInterface(self): - return self.inner.isInterface() - - def isPromise(self): - # There is no such thing as a nullable Promise. - assert not self.inner.isPromise() - return False - - def isCallbackInterface(self): - return self.inner.isCallbackInterface() - - def isNonCallbackInterface(self): - return self.inner.isNonCallbackInterface() - - def isEnum(self): - return self.inner.isEnum() - - def isUnion(self): - return self.inner.isUnion() - - def isJSONType(self): - return self.inner.isJSONType() - - def isObservableArray(self): - return self.inner.isObservableArray() - - def hasClamp(self): - return self.inner.hasClamp() - - def hasEnforceRange(self): - return self.inner.hasEnforceRange() - - def hasAllowShared(self): - return self.inner.hasAllowShared() - - def isComplete(self): - return self.name is not None - - def tag(self): - return self.inner.tag() - - def complete(self, scope): - if not self.inner.isComplete(): - self.inner = self.inner.complete(scope) - assert self.inner.isComplete() - - if self.inner.nullable(): - raise WebIDLError( - "The inner type of a nullable type must not be a nullable type", - [self.location, self.inner.location], - ) - if self.inner.isUnion(): - if self.inner.hasNullableType: - raise WebIDLError( - "The inner type of a nullable type must not " - "be a union type that itself has a nullable " - "type as a member type", - [self.location], - ) - if self.inner.isDOMString(): - if self.inner.legacyNullToEmptyString: - raise WebIDLError( - "[LegacyNullToEmptyString] not allowed on a nullable DOMString", - [self.location, self.inner.location], - ) - if self.inner.isObservableArray(): - raise WebIDLError( - "The inner type of a nullable type must not be an ObservableArray type", - [self.location, self.inner.location], - ) - - self.name = self.inner.name + "OrNull" - return self - - def isDistinguishableFrom(self, other): - if ( - other.nullable() - or other.isDictionary() - or ( - other.isUnion() and (other.hasNullableType or other.hasDictionaryType()) - ) - ): - # Can't tell which type null should become - return False - return self.inner.isDistinguishableFrom(other) - - def withExtendedAttributes(self, attrs): - # See https://github.com/heycam/webidl/issues/827#issuecomment-565131350 - # Allowing extended attributes to apply to a nullable type is an intermediate solution. - # A potential longer term solution is to introduce a null type and get rid of nullables. - # For example, we could do `([Clamp] long or null) foo` in the future. - return IDLNullableType(self.location, self.inner.withExtendedAttributes(attrs)) - - -class IDLSequenceType(IDLParametrizedType): - def __init__(self, location, parameterType): - assert not parameterType.isUndefined() - - IDLParametrizedType.__init__(self, location, parameterType.name, parameterType) - # Need to set self.name up front if our inner type is already complete, - # since in that case our .complete() won't be called. - if self.inner.isComplete(): - self.name = self.inner.name + "Sequence" - - def __hash__(self): - return hash(self.inner) - - def __eq__(self, other): - return isinstance(other, IDLSequenceType) and self.inner == other.inner - - def __str__(self): - return self.inner.__str__() + "Sequence" - - def prettyName(self): - return "sequence<%s>" % self.inner.prettyName() - - def isSequence(self): - return True - - def isJSONType(self): - return self.inner.isJSONType() - - def tag(self): - return IDLType.Tags.sequence - - def complete(self, scope): - if self.inner.isObservableArray(): - raise WebIDLError( - "The inner type of a sequence type must not be an ObservableArray type", - [self.location, self.inner.location], - ) - - self.inner = self.inner.complete(scope) - self.name = self.inner.name + "Sequence" - return self - - def isDistinguishableFrom(self, other): - if other.isPromise(): - return False - if other.isUnion(): - # Just forward to the union; it'll deal - return other.isDistinguishableFrom(self) - return ( - other.isUndefined() - or other.isPrimitive() - or other.isString() - or other.isEnum() - or other.isInterface() - or other.isDictionary() - or other.isCallback() - or other.isRecord() - ) - - -class IDLRecordType(IDLParametrizedType): - def __init__(self, location, keyType, valueType): - assert keyType.isString() - assert keyType.isComplete() - assert not valueType.isUndefined() - - IDLParametrizedType.__init__(self, location, valueType.name, valueType) - self.keyType = keyType - - # Need to set self.name up front if our inner type is already complete, - # since in that case our .complete() won't be called. - if self.inner.isComplete(): - self.name = self.keyType.name + self.inner.name + "Record" - - def __hash__(self): - return hash(self.inner) - - def __eq__(self, other): - return isinstance(other, IDLRecordType) and self.inner == other.inner - - def __str__(self): - return self.keyType.__str__() + self.inner.__str__() + "Record" - - def prettyName(self): - return "record<%s, %s>" % (self.keyType.prettyName(), self.inner.prettyName()) - - def isRecord(self): - return True - - def isJSONType(self): - return self.inner.isJSONType() - - def tag(self): - return IDLType.Tags.record - - def complete(self, scope): - if self.inner.isObservableArray(): - raise WebIDLError( - "The value type of a record type must not be an ObservableArray type", - [self.location, self.inner.location], - ) - - self.inner = self.inner.complete(scope) - self.name = self.keyType.name + self.inner.name + "Record" - return self - - def unroll(self): - # We do not unroll our inner. Just stop at ourselves. That - # lets us add headers for both ourselves and our inner as - # needed. - return self - - def isDistinguishableFrom(self, other): - if other.isPromise(): - return False - if other.isUnion(): - # Just forward to the union; it'll deal - return other.isDistinguishableFrom(self) - return ( - other.isPrimitive() - or other.isString() - or other.isEnum() - or other.isNonCallbackInterface() - or other.isSequence() - ) - - def isExposedInAllOf(self, exposureSet): - return self.inner.unroll().isExposedInAllOf(exposureSet) - - -class IDLObservableArrayType(IDLParametrizedType): - def __init__(self, location, innerType): - assert not innerType.isUndefined() - IDLParametrizedType.__init__(self, location, None, innerType) - - def __hash__(self): - return hash(self.inner) - - def __eq__(self, other): - return isinstance(other, IDLObservableArrayType) and self.inner == other.inner - - def __str__(self): - return self.inner.__str__() + "ObservableArray" - - def prettyName(self): - return "ObservableArray<%s>" % self.inner.prettyName() - - def isJSONType(self): - return self.inner.isJSONType() - - def isObservableArray(self): - return True - - def isComplete(self): - return self.name is not None - - def tag(self): - return IDLType.Tags.observablearray - - def complete(self, scope): - if not self.inner.isComplete(): - self.inner = self.inner.complete(scope) - assert self.inner.isComplete() - - if self.inner.isDictionary(): - raise WebIDLError( - "The inner type of an ObservableArray type must not " - "be a dictionary type", - [self.location, self.inner.location], - ) - if self.inner.isSequence(): - raise WebIDLError( - "The inner type of an ObservableArray type must not " - "be a sequence type", - [self.location, self.inner.location], - ) - if self.inner.isRecord(): - raise WebIDLError( - "The inner type of an ObservableArray type must not be a record type", - [self.location, self.inner.location], - ) - if self.inner.isObservableArray(): - raise WebIDLError( - "The inner type of an ObservableArray type must not " - "be an ObservableArray type", - [self.location, self.inner.location], - ) - - self.name = self.inner.name + "ObservableArray" - return self - - def isDistinguishableFrom(self, other): - # ObservableArrays are not distinguishable from anything. - return False - - -class IDLUnionType(IDLType): - def __init__(self, location, memberTypes): - IDLType.__init__(self, location, "") - self.memberTypes = memberTypes - self.hasNullableType = False - self._dictionaryType = None - self.flatMemberTypes = None - self.builtin = False - - def __eq__(self, other): - return isinstance(other, IDLUnionType) and self.memberTypes == other.memberTypes - - def __hash__(self): - assert self.isComplete() - return self.name.__hash__() - - def prettyName(self): - return "(" + " or ".join(m.prettyName() for m in self.memberTypes) + ")" - - def isUnion(self): - return True - - def isJSONType(self): - return all(m.isJSONType() for m in self.memberTypes) - - def includesRestrictedFloat(self): - return any(t.includesRestrictedFloat() for t in self.memberTypes) - - def tag(self): - return IDLType.Tags.union - - def resolveType(self, parentScope): - assert isinstance(parentScope, IDLScope) - for t in self.memberTypes: - t.resolveType(parentScope) - - def isComplete(self): - return self.flatMemberTypes is not None - - def complete(self, scope): - def typeName(type): - if isinstance(type, IDLNullableType): - return typeName(type.inner) + "OrNull" - if isinstance(type, IDLWrapperType): - return typeName(type._identifier.object()) - if isinstance(type, IDLObjectWithIdentifier): - return typeName(type.identifier) - if isinstance(type, IDLBuiltinType) and type.hasAllowShared(): - assert type.isBufferSource() - return "MaybeShared" + type.name - return type.name - - for (i, type) in enumerate(self.memberTypes): - # Exclude typedefs because if given "typedef (B or C) test", - # we want AOrTest, not AOrBOrC - if not type.isComplete() and not isinstance(type, IDLTypedefType): - self.memberTypes[i] = type.complete(scope) - - self.name = "Or".join(typeName(type) for type in self.memberTypes) - - # We do this again to complete the typedef types - for (i, type) in enumerate(self.memberTypes): - if not type.isComplete(): - self.memberTypes[i] = type.complete(scope) - - self.flatMemberTypes = list(self.memberTypes) - i = 0 - while i < len(self.flatMemberTypes): - if self.flatMemberTypes[i].nullable(): - if self.hasNullableType: - raise WebIDLError( - "Can't have more than one nullable types in a union", - [nullableType.location, self.flatMemberTypes[i].location], - ) - if self.hasDictionaryType(): - raise WebIDLError( - "Can't have a nullable type and a " - "dictionary type in a union", - [ - self._dictionaryType.location, - self.flatMemberTypes[i].location, - ], - ) - self.hasNullableType = True - nullableType = self.flatMemberTypes[i] - self.flatMemberTypes[i] = self.flatMemberTypes[i].inner - continue - if self.flatMemberTypes[i].isDictionary(): - if self.hasNullableType: - raise WebIDLError( - "Can't have a nullable type and a " - "dictionary type in a union", - [nullableType.location, self.flatMemberTypes[i].location], - ) - self._dictionaryType = self.flatMemberTypes[i] - self.flatMemberTypes[i].inner.needsConversionFromJS = True - elif self.flatMemberTypes[i].isUnion(): - self.flatMemberTypes[i : i + 1] = self.flatMemberTypes[i].memberTypes - continue - i += 1 - - for (i, t) in enumerate(self.flatMemberTypes[:-1]): - for u in self.flatMemberTypes[i + 1 :]: - if not t.isDistinguishableFrom(u): - raise WebIDLError( - "Flat member types of a union should be " - "distinguishable, " + str(t) + " is not " - "distinguishable from " + str(u), - [self.location, t.location, u.location], - ) - - return self - - def isDistinguishableFrom(self, other): - if self.hasNullableType and other.nullable(): - # Can't tell which type null should become - return False - if other.isUnion(): - otherTypes = other.unroll().memberTypes - else: - otherTypes = [other] - # For every type in otherTypes, check that it's distinguishable from - # every type in our types - for u in otherTypes: - if any(not t.isDistinguishableFrom(u) for t in self.memberTypes): - return False - return True - - def isExposedInAllOf(self, exposureSet): - # We could have different member types in different globals. Just make sure that each thing in exposureSet has one of our member types exposed in it. - for globalName in exposureSet: - if not any( - t.unroll().isExposedInAllOf(set([globalName])) - for t in self.flatMemberTypes - ): - return False - return True - - def hasDictionaryType(self): - return self._dictionaryType is not None - - def hasPossiblyEmptyDictionaryType(self): - return ( - self._dictionaryType is not None and self._dictionaryType.inner.canBeEmpty() - ) - - def _getDependentObjects(self): - return set(self.memberTypes) - - -class IDLTypedefType(IDLType): - def __init__(self, location, innerType, name): - IDLType.__init__(self, location, name) - self.inner = innerType - self.builtin = False - - def __hash__(self): - return hash(self.inner) - - def __eq__(self, other): - return isinstance(other, IDLTypedefType) and self.inner == other.inner - - def __str__(self): - return self.name - - def nullable(self): - return self.inner.nullable() - - def isPrimitive(self): - return self.inner.isPrimitive() - - def isBoolean(self): - return self.inner.isBoolean() - - def isNumeric(self): - return self.inner.isNumeric() - - def isString(self): - return self.inner.isString() - - def isByteString(self): - return self.inner.isByteString() - - def isDOMString(self): - return self.inner.isDOMString() - - def isUSVString(self): - return self.inner.isUSVString() - - def isUTF8String(self): - return self.inner.isUTF8String() - - def isJSString(self): - return self.inner.isJSString() - - def isUndefined(self): - return self.inner.isUndefined() - - def isJSONType(self): - return self.inner.isJSONType() - - def isSequence(self): - return self.inner.isSequence() - - def isRecord(self): - return self.inner.isRecord() - - def isReadableStream(self): - return self.inner.isReadableStream() - - def isDictionary(self): - return self.inner.isDictionary() - - def isArrayBuffer(self): - return self.inner.isArrayBuffer() - - def isArrayBufferView(self): - return self.inner.isArrayBufferView() - - def isTypedArray(self): - return self.inner.isTypedArray() - - def isInterface(self): - return self.inner.isInterface() - - def isCallbackInterface(self): - return self.inner.isCallbackInterface() - - def isNonCallbackInterface(self): - return self.inner.isNonCallbackInterface() - - def isComplete(self): - return False - - def complete(self, parentScope): - if not self.inner.isComplete(): - self.inner = self.inner.complete(parentScope) - assert self.inner.isComplete() - return self.inner - - # Do we need a resolveType impl? I don't think it's particularly useful.... - - def tag(self): - return self.inner.tag() - - def unroll(self): - return self.inner.unroll() - - def isDistinguishableFrom(self, other): - return self.inner.isDistinguishableFrom(other) - - def _getDependentObjects(self): - return self.inner._getDependentObjects() - - def withExtendedAttributes(self, attrs): - return IDLTypedefType( - self.location, self.inner.withExtendedAttributes(attrs), self.name - ) - - -class IDLTypedef(IDLObjectWithIdentifier): - def __init__(self, location, parentScope, innerType, name): - # Set self.innerType first, because IDLObjectWithIdentifier.__init__ - # will call our __str__, which wants to use it. - self.innerType = innerType - identifier = IDLUnresolvedIdentifier(location, name) - IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier) - - def __str__(self): - return "Typedef %s %s" % (self.identifier.name, self.innerType) - - def finish(self, parentScope): - if not self.innerType.isComplete(): - self.innerType = self.innerType.complete(parentScope) - - def validate(self): - pass - - def isTypedef(self): - return True - - def addExtendedAttributes(self, attrs): - if len(attrs) != 0: - raise WebIDLError( - "There are no extended attributes that are " "allowed on typedefs", - [attrs[0].location, self.location], - ) - - def _getDependentObjects(self): - return self.innerType._getDependentObjects() - - -class IDLWrapperType(IDLType): - def __init__(self, location, inner): - IDLType.__init__(self, location, inner.identifier.name) - self.inner = inner - self._identifier = inner.identifier - self.builtin = False - - def __hash__(self): - return hash(self._identifier) + hash(self.builtin) - - def __eq__(self, other): - return ( - isinstance(other, IDLWrapperType) - and self._identifier == other._identifier - and self.builtin == other.builtin - ) - - def __str__(self): - return str(self.name) + " (Wrapper)" - - def isDictionary(self): - return isinstance(self.inner, IDLDictionary) - - def isInterface(self): - return isinstance(self.inner, IDLInterface) or isinstance( - self.inner, IDLExternalInterface - ) - - def isCallbackInterface(self): - return self.isInterface() and self.inner.isCallback() - - def isNonCallbackInterface(self): - return self.isInterface() and not self.inner.isCallback() - - def isEnum(self): - return isinstance(self.inner, IDLEnum) - - def isJSONType(self): - if self.isInterface(): - if self.inner.isExternal(): - return False - iface = self.inner - while iface: - if any(m.isMethod() and m.isToJSON() for m in iface.members): - return True - iface = iface.parent - return False - elif self.isEnum(): - return True - elif self.isDictionary(): - dictionary = self.inner - while dictionary: - if not all(m.type.isJSONType() for m in dictionary.members): - return False - dictionary = dictionary.parent - return True - else: - raise WebIDLError( - "IDLWrapperType wraps type %s that we don't know if " - "is serializable" % type(self.inner), - [self.location], - ) - - def resolveType(self, parentScope): - assert isinstance(parentScope, IDLScope) - self.inner.resolve(parentScope) - - def isComplete(self): - return True - - def tag(self): - if self.isInterface(): - return IDLType.Tags.interface - elif self.isEnum(): - return IDLType.Tags.enum - elif self.isDictionary(): - return IDLType.Tags.dictionary - else: - assert False - - def isDistinguishableFrom(self, other): - if other.isPromise(): - return False - if other.isUnion(): - # Just forward to the union; it'll deal - return other.isDistinguishableFrom(self) - assert self.isInterface() or self.isEnum() or self.isDictionary() - if self.isEnum(): - return ( - other.isUndefined() - or other.isPrimitive() - or other.isInterface() - or other.isObject() - or other.isCallback() - or other.isDictionary() - or other.isSequence() - or other.isRecord() - ) - if self.isDictionary() and (other.nullable() or other.isUndefined()): - return False - if ( - other.isPrimitive() - or other.isString() - or other.isEnum() - or other.isSequence() - ): - return True - if self.isDictionary(): - return other.isNonCallbackInterface() - - assert self.isInterface() - if other.isInterface(): - if other.isSpiderMonkeyInterface(): - # Just let |other| handle things - return other.isDistinguishableFrom(self) - assert self.isGeckoInterface() and other.isGeckoInterface() - if self.inner.isExternal() or other.unroll().inner.isExternal(): - return self != other - return len( - self.inner.interfacesBasedOnSelf - & other.unroll().inner.interfacesBasedOnSelf - ) == 0 and (self.isNonCallbackInterface() or other.isNonCallbackInterface()) - if ( - other.isUndefined() - or other.isDictionary() - or other.isCallback() - or other.isRecord() - ): - return self.isNonCallbackInterface() - - # Not much else |other| can be - assert other.isObject() - return False - - def isExposedInAllOf(self, exposureSet): - if not self.isInterface(): - return True - iface = self.inner - if iface.isExternal(): - # Let's say true, so we don't have to implement exposure mixins on - # external interfaces and sprinkle [Exposed=Window] on every single - # external interface declaration. - return True - return iface.exposureSet.issuperset(exposureSet) - - def _getDependentObjects(self): - # NB: The codegen for an interface type depends on - # a) That the identifier is in fact an interface (as opposed to - # a dictionary or something else). - # b) The native type of the interface. - # If we depend on the interface object we will also depend on - # anything the interface depends on which is undesirable. We - # considered implementing a dependency just on the interface type - # file, but then every modification to an interface would cause this - # to be regenerated which is still undesirable. We decided not to - # depend on anything, reasoning that: - # 1) Changing the concrete type of the interface requires modifying - # Bindings.conf, which is still a global dependency. - # 2) Changing an interface to a dictionary (or vice versa) with the - # same identifier should be incredibly rare. - # - # On the other hand, if our type is a dictionary, we should - # depend on it, because the member types of a dictionary - # affect whether a method taking the dictionary as an argument - # takes a JSContext* argument or not. - if self.isDictionary(): - return set([self.inner]) - return set() - - -class IDLPromiseType(IDLParametrizedType): - def __init__(self, location, innerType): - IDLParametrizedType.__init__(self, location, "Promise", innerType) - - def __hash__(self): - return hash(self.promiseInnerType()) - - def __eq__(self, other): - return ( - isinstance(other, IDLPromiseType) - and self.promiseInnerType() == other.promiseInnerType() - ) - - def __str__(self): - return self.inner.__str__() + "Promise" - - def prettyName(self): - return "Promise<%s>" % self.inner.prettyName() - - def isPromise(self): - return True - - def promiseInnerType(self): - return self.inner - - def tag(self): - return IDLType.Tags.promise - - def complete(self, scope): - if self.inner.isObservableArray(): - raise WebIDLError( - "The inner type of a promise type must not be an ObservableArray type", - [self.location, self.inner.location], - ) - - self.inner = self.promiseInnerType().complete(scope) - return self - - def unroll(self): - # We do not unroll our inner. Just stop at ourselves. That - # lets us add headers for both ourselves and our inner as - # needed. - return self - - def isDistinguishableFrom(self, other): - # Promises are not distinguishable from anything. - return False - - def isExposedInAllOf(self, exposureSet): - # Check the internal type - return self.promiseInnerType().unroll().isExposedInAllOf(exposureSet) - - -class IDLBuiltinType(IDLType): - - Types = enum( - # The integer types - "byte", - "octet", - "short", - "unsigned_short", - "long", - "unsigned_long", - "long_long", - "unsigned_long_long", - # Additional primitive types - "boolean", - "unrestricted_float", - "float", - "unrestricted_double", - # IMPORTANT: "double" must be the last primitive type listed - "double", - # Other types - "any", - "undefined", - "domstring", - "bytestring", - "usvstring", - "utf8string", - "jsstring", - "object", - # Funny stuff - "ArrayBuffer", - "ArrayBufferView", - "Int8Array", - "Uint8Array", - "Uint8ClampedArray", - "Int16Array", - "Uint16Array", - "Int32Array", - "Uint32Array", - "Float32Array", - "Float64Array", - "ReadableStream", - ) - - TagLookup = { - Types.byte: IDLType.Tags.int8, - Types.octet: IDLType.Tags.uint8, - Types.short: IDLType.Tags.int16, - Types.unsigned_short: IDLType.Tags.uint16, - Types.long: IDLType.Tags.int32, - Types.unsigned_long: IDLType.Tags.uint32, - Types.long_long: IDLType.Tags.int64, - Types.unsigned_long_long: IDLType.Tags.uint64, - Types.boolean: IDLType.Tags.bool, - Types.unrestricted_float: IDLType.Tags.unrestricted_float, - Types.float: IDLType.Tags.float, - Types.unrestricted_double: IDLType.Tags.unrestricted_double, - Types.double: IDLType.Tags.double, - Types.any: IDLType.Tags.any, - Types.undefined: IDLType.Tags.undefined, - Types.domstring: IDLType.Tags.domstring, - Types.bytestring: IDLType.Tags.bytestring, - Types.usvstring: IDLType.Tags.usvstring, - Types.utf8string: IDLType.Tags.utf8string, - Types.jsstring: IDLType.Tags.jsstring, - Types.object: IDLType.Tags.object, - Types.ArrayBuffer: IDLType.Tags.interface, - Types.ArrayBufferView: IDLType.Tags.interface, - Types.Int8Array: IDLType.Tags.interface, - Types.Uint8Array: IDLType.Tags.interface, - Types.Uint8ClampedArray: IDLType.Tags.interface, - Types.Int16Array: IDLType.Tags.interface, - Types.Uint16Array: IDLType.Tags.interface, - Types.Int32Array: IDLType.Tags.interface, - Types.Uint32Array: IDLType.Tags.interface, - Types.Float32Array: IDLType.Tags.interface, - Types.Float64Array: IDLType.Tags.interface, - Types.ReadableStream: IDLType.Tags.interface, - } - - PrettyNames = { - Types.byte: "byte", - Types.octet: "octet", - Types.short: "short", - Types.unsigned_short: "unsigned short", - Types.long: "long", - Types.unsigned_long: "unsigned long", - Types.long_long: "long long", - Types.unsigned_long_long: "unsigned long long", - Types.boolean: "boolean", - Types.unrestricted_float: "unrestricted float", - Types.float: "float", - Types.unrestricted_double: "unrestricted double", - Types.double: "double", - Types.any: "any", - Types.undefined: "undefined", - Types.domstring: "DOMString", - Types.bytestring: "ByteString", - Types.usvstring: "USVString", - Types.utf8string: "USVString", # That's what it is in spec terms - Types.jsstring: "USVString", # Again, that's what it is in spec terms - Types.object: "object", - Types.ArrayBuffer: "ArrayBuffer", - Types.ArrayBufferView: "ArrayBufferView", - Types.Int8Array: "Int8Array", - Types.Uint8Array: "Uint8Array", - Types.Uint8ClampedArray: "Uint8ClampedArray", - Types.Int16Array: "Int16Array", - Types.Uint16Array: "Uint16Array", - Types.Int32Array: "Int32Array", - Types.Uint32Array: "Uint32Array", - Types.Float32Array: "Float32Array", - Types.Float64Array: "Float64Array", - Types.ReadableStream: "ReadableStream", - } - - def __init__( - self, - location, - name, - type, - clamp=False, - enforceRange=False, - legacyNullToEmptyString=False, - allowShared=False, - attrLocation=[], - ): - """ - The mutually exclusive clamp/enforceRange/legacyNullToEmptyString/allowShared arguments are used - to create instances of this type with the appropriate attributes attached. Use .clamped(), - .rangeEnforced(), .withLegacyNullToEmptyString() and .withAllowShared(). - - attrLocation is an array of source locations of these attributes for error reporting. - """ - IDLType.__init__(self, location, name) - self.builtin = True - self._typeTag = type - self._clamped = None - self._rangeEnforced = None - self._withLegacyNullToEmptyString = None - self._withAllowShared = None - if self.isInteger(): - if clamp: - self._clamp = True - self.name = "Clamped" + self.name - self._extendedAttrDict["Clamp"] = True - elif enforceRange: - self._enforceRange = True - self.name = "RangeEnforced" + self.name - self._extendedAttrDict["EnforceRange"] = True - elif clamp or enforceRange: - raise WebIDLError( - "Non-integer types cannot be [Clamp] or [EnforceRange]", attrLocation - ) - if self.isDOMString() or self.isUTF8String(): - if legacyNullToEmptyString: - self.legacyNullToEmptyString = True - self.name = "NullIsEmpty" + self.name - self._extendedAttrDict["LegacyNullToEmptyString"] = True - elif legacyNullToEmptyString: - raise WebIDLError( - "Non-string types cannot be [LegacyNullToEmptyString]", attrLocation - ) - if self.isBufferSource(): - if allowShared: - self._allowShared = True - self._extendedAttrDict["AllowShared"] = True - elif allowShared: - raise WebIDLError( - "Types that are not buffer source types cannot be [AllowShared]", - attrLocation, - ) - - def __str__(self): - if self._allowShared: - assert self.isBufferSource() - return "MaybeShared" + str(self.name) - return str(self.name) - - def prettyName(self): - return IDLBuiltinType.PrettyNames[self._typeTag] - - def clamped(self, attrLocation): - if not self._clamped: - self._clamped = IDLBuiltinType( - self.location, - self.name, - self._typeTag, - clamp=True, - attrLocation=attrLocation, - ) - return self._clamped - - def rangeEnforced(self, attrLocation): - if not self._rangeEnforced: - self._rangeEnforced = IDLBuiltinType( - self.location, - self.name, - self._typeTag, - enforceRange=True, - attrLocation=attrLocation, - ) - return self._rangeEnforced - - def withLegacyNullToEmptyString(self, attrLocation): - if not self._withLegacyNullToEmptyString: - self._withLegacyNullToEmptyString = IDLBuiltinType( - self.location, - self.name, - self._typeTag, - legacyNullToEmptyString=True, - attrLocation=attrLocation, - ) - return self._withLegacyNullToEmptyString - - def withAllowShared(self, attrLocation): - if not self._withAllowShared: - self._withAllowShared = IDLBuiltinType( - self.location, - self.name, - self._typeTag, - allowShared=True, - attrLocation=attrLocation, - ) - return self._withAllowShared - - def isPrimitive(self): - return self._typeTag <= IDLBuiltinType.Types.double - - def isBoolean(self): - return self._typeTag == IDLBuiltinType.Types.boolean - - def isUndefined(self): - return self._typeTag == IDLBuiltinType.Types.undefined - - def isNumeric(self): - return self.isPrimitive() and not self.isBoolean() - - def isString(self): - return ( - self._typeTag == IDLBuiltinType.Types.domstring - or self._typeTag == IDLBuiltinType.Types.bytestring - or self._typeTag == IDLBuiltinType.Types.usvstring - or self._typeTag == IDLBuiltinType.Types.utf8string - or self._typeTag == IDLBuiltinType.Types.jsstring - ) - - def isByteString(self): - return self._typeTag == IDLBuiltinType.Types.bytestring - - def isDOMString(self): - return self._typeTag == IDLBuiltinType.Types.domstring - - def isUSVString(self): - return self._typeTag == IDLBuiltinType.Types.usvstring - - def isUTF8String(self): - return self._typeTag == IDLBuiltinType.Types.utf8string - - def isJSString(self): - return self._typeTag == IDLBuiltinType.Types.jsstring - - def isInteger(self): - return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long - - def isArrayBuffer(self): - return self._typeTag == IDLBuiltinType.Types.ArrayBuffer - - def isArrayBufferView(self): - return self._typeTag == IDLBuiltinType.Types.ArrayBufferView - - def isTypedArray(self): - return ( - self._typeTag >= IDLBuiltinType.Types.Int8Array - and self._typeTag <= IDLBuiltinType.Types.Float64Array - ) - - def isReadableStream(self): - return self._typeTag == IDLBuiltinType.Types.ReadableStream - - def isInterface(self): - # TypedArray things are interface types per the TypedArray spec, - # but we handle them as builtins because SpiderMonkey implements - # all of it internally. - return ( - self.isArrayBuffer() - or self.isArrayBufferView() - or self.isTypedArray() - or self.isReadableStream() - ) - - def isNonCallbackInterface(self): - # All the interfaces we can be are non-callback - return self.isInterface() - - def isFloat(self): - return ( - self._typeTag == IDLBuiltinType.Types.float - or self._typeTag == IDLBuiltinType.Types.double - or self._typeTag == IDLBuiltinType.Types.unrestricted_float - or self._typeTag == IDLBuiltinType.Types.unrestricted_double - ) - - def isUnrestricted(self): - assert self.isFloat() - return ( - self._typeTag == IDLBuiltinType.Types.unrestricted_float - or self._typeTag == IDLBuiltinType.Types.unrestricted_double - ) - - def isJSONType(self): - return self.isPrimitive() or self.isString() or self.isObject() - - def includesRestrictedFloat(self): - return self.isFloat() and not self.isUnrestricted() - - def tag(self): - return IDLBuiltinType.TagLookup[self._typeTag] - - def isDistinguishableFrom(self, other): - if other.isPromise(): - return False - if other.isUnion(): - # Just forward to the union; it'll deal - return other.isDistinguishableFrom(self) - if self.isUndefined(): - return not (other.isUndefined() or other.isDictionaryLike()) - if self.isPrimitive(): - if ( - other.isUndefined() - or other.isString() - or other.isEnum() - or other.isInterface() - or other.isObject() - or other.isCallback() - or other.isDictionary() - or other.isSequence() - or other.isRecord() - ): - return True - if self.isBoolean(): - return other.isNumeric() - assert self.isNumeric() - return other.isBoolean() - if self.isString(): - return ( - other.isUndefined() - or other.isPrimitive() - or other.isInterface() - or other.isObject() - or other.isCallback() - or other.isDictionary() - or other.isSequence() - or other.isRecord() - ) - if self.isAny(): - # Can't tell "any" apart from anything - return False - if self.isObject(): - return ( - other.isUndefined() - or other.isPrimitive() - or other.isString() - or other.isEnum() - ) - # Not much else we could be! - assert self.isSpiderMonkeyInterface() - # Like interfaces, but we know we're not a callback - return ( - other.isUndefined() - or other.isPrimitive() - or other.isString() - or other.isEnum() - or other.isCallback() - or other.isDictionary() - or other.isSequence() - or other.isRecord() - or ( - other.isInterface() - and ( - # ArrayBuffer is distinguishable from everything - # that's not an ArrayBuffer or a callback interface - (self.isArrayBuffer() and not other.isArrayBuffer()) - or (self.isReadableStream() and not other.isReadableStream()) - or - # ArrayBufferView is distinguishable from everything - # that's not an ArrayBufferView or typed array. - ( - self.isArrayBufferView() - and not other.isArrayBufferView() - and not other.isTypedArray() - ) - or - # Typed arrays are distinguishable from everything - # except ArrayBufferView and the same type of typed - # array - ( - self.isTypedArray() - and not other.isArrayBufferView() - and not (other.isTypedArray() and other.name == self.name) - ) - ) - ) - ) - - def _getDependentObjects(self): - return set() - - def withExtendedAttributes(self, attrs): - ret = self - for attribute in attrs: - identifier = attribute.identifier() - if identifier == "Clamp": - if not attribute.noArguments(): - raise WebIDLError( - "[Clamp] must take no arguments", [attribute.location] - ) - if ret.hasEnforceRange() or self._enforceRange: - raise WebIDLError( - "[EnforceRange] and [Clamp] are mutually exclusive", - [self.location, attribute.location], - ) - ret = self.clamped([self.location, attribute.location]) - elif identifier == "EnforceRange": - if not attribute.noArguments(): - raise WebIDLError( - "[EnforceRange] must take no arguments", [attribute.location] - ) - if ret.hasClamp() or self._clamp: - raise WebIDLError( - "[EnforceRange] and [Clamp] are mutually exclusive", - [self.location, attribute.location], - ) - ret = self.rangeEnforced([self.location, attribute.location]) - elif identifier == "LegacyNullToEmptyString": - if not (self.isDOMString() or self.isUTF8String()): - raise WebIDLError( - "[LegacyNullToEmptyString] only allowed on DOMStrings and UTF8Strings", - [self.location, attribute.location], - ) - assert not self.nullable() - if attribute.hasValue(): - raise WebIDLError( - "[LegacyNullToEmptyString] must take no identifier argument", - [attribute.location], - ) - ret = self.withLegacyNullToEmptyString( - [self.location, attribute.location] - ) - elif identifier == "AllowShared": - if not attribute.noArguments(): - raise WebIDLError( - "[AllowShared] must take no arguments", [attribute.location] - ) - if not self.isBufferSource(): - raise WebIDLError( - "[AllowShared] only allowed on buffer source types", - [self.location, attribute.location], - ) - ret = self.withAllowShared([self.location, attribute.location]) - - else: - raise WebIDLError( - "Unhandled extended attribute on type", - [self.location, attribute.location], - ) - return ret - - -BuiltinTypes = { - IDLBuiltinType.Types.byte: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Byte", IDLBuiltinType.Types.byte - ), - IDLBuiltinType.Types.octet: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Octet", IDLBuiltinType.Types.octet - ), - IDLBuiltinType.Types.short: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Short", IDLBuiltinType.Types.short - ), - IDLBuiltinType.Types.unsigned_short: IDLBuiltinType( - BuiltinLocation("<builtin type>"), - "UnsignedShort", - IDLBuiltinType.Types.unsigned_short, - ), - IDLBuiltinType.Types.long: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Long", IDLBuiltinType.Types.long - ), - IDLBuiltinType.Types.unsigned_long: IDLBuiltinType( - BuiltinLocation("<builtin type>"), - "UnsignedLong", - IDLBuiltinType.Types.unsigned_long, - ), - IDLBuiltinType.Types.long_long: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "LongLong", IDLBuiltinType.Types.long_long - ), - IDLBuiltinType.Types.unsigned_long_long: IDLBuiltinType( - BuiltinLocation("<builtin type>"), - "UnsignedLongLong", - IDLBuiltinType.Types.unsigned_long_long, - ), - IDLBuiltinType.Types.undefined: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Undefined", IDLBuiltinType.Types.undefined - ), - IDLBuiltinType.Types.boolean: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Boolean", IDLBuiltinType.Types.boolean - ), - IDLBuiltinType.Types.float: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Float", IDLBuiltinType.Types.float - ), - IDLBuiltinType.Types.unrestricted_float: IDLBuiltinType( - BuiltinLocation("<builtin type>"), - "UnrestrictedFloat", - IDLBuiltinType.Types.unrestricted_float, - ), - IDLBuiltinType.Types.double: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Double", IDLBuiltinType.Types.double - ), - IDLBuiltinType.Types.unrestricted_double: IDLBuiltinType( - BuiltinLocation("<builtin type>"), - "UnrestrictedDouble", - IDLBuiltinType.Types.unrestricted_double, - ), - IDLBuiltinType.Types.any: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Any", IDLBuiltinType.Types.any - ), - IDLBuiltinType.Types.domstring: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "String", IDLBuiltinType.Types.domstring - ), - IDLBuiltinType.Types.bytestring: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "ByteString", IDLBuiltinType.Types.bytestring - ), - IDLBuiltinType.Types.usvstring: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "USVString", IDLBuiltinType.Types.usvstring - ), - IDLBuiltinType.Types.utf8string: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "UTF8String", IDLBuiltinType.Types.utf8string - ), - IDLBuiltinType.Types.jsstring: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "JSString", IDLBuiltinType.Types.jsstring - ), - IDLBuiltinType.Types.object: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Object", IDLBuiltinType.Types.object - ), - IDLBuiltinType.Types.ArrayBuffer: IDLBuiltinType( - BuiltinLocation("<builtin type>"), - "ArrayBuffer", - IDLBuiltinType.Types.ArrayBuffer, - ), - IDLBuiltinType.Types.ArrayBufferView: IDLBuiltinType( - BuiltinLocation("<builtin type>"), - "ArrayBufferView", - IDLBuiltinType.Types.ArrayBufferView, - ), - IDLBuiltinType.Types.Int8Array: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Int8Array", IDLBuiltinType.Types.Int8Array - ), - IDLBuiltinType.Types.Uint8Array: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Uint8Array", IDLBuiltinType.Types.Uint8Array - ), - IDLBuiltinType.Types.Uint8ClampedArray: IDLBuiltinType( - BuiltinLocation("<builtin type>"), - "Uint8ClampedArray", - IDLBuiltinType.Types.Uint8ClampedArray, - ), - IDLBuiltinType.Types.Int16Array: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Int16Array", IDLBuiltinType.Types.Int16Array - ), - IDLBuiltinType.Types.Uint16Array: IDLBuiltinType( - BuiltinLocation("<builtin type>"), - "Uint16Array", - IDLBuiltinType.Types.Uint16Array, - ), - IDLBuiltinType.Types.Int32Array: IDLBuiltinType( - BuiltinLocation("<builtin type>"), "Int32Array", IDLBuiltinType.Types.Int32Array - ), - IDLBuiltinType.Types.Uint32Array: IDLBuiltinType( - BuiltinLocation("<builtin type>"), - "Uint32Array", - IDLBuiltinType.Types.Uint32Array, - ), - IDLBuiltinType.Types.Float32Array: IDLBuiltinType( - BuiltinLocation("<builtin type>"), - "Float32Array", - IDLBuiltinType.Types.Float32Array, - ), - IDLBuiltinType.Types.Float64Array: IDLBuiltinType( - BuiltinLocation("<builtin type>"), - "Float64Array", - IDLBuiltinType.Types.Float64Array, - ), - IDLBuiltinType.Types.ReadableStream: IDLBuiltinType( - BuiltinLocation("<builtin type>"), - "ReadableStream", - IDLBuiltinType.Types.ReadableStream, - ), -} - - -integerTypeSizes = { - IDLBuiltinType.Types.byte: (-128, 127), - IDLBuiltinType.Types.octet: (0, 255), - IDLBuiltinType.Types.short: (-32768, 32767), - IDLBuiltinType.Types.unsigned_short: (0, 65535), - IDLBuiltinType.Types.long: (-2147483648, 2147483647), - IDLBuiltinType.Types.unsigned_long: (0, 4294967295), - IDLBuiltinType.Types.long_long: (-9223372036854775808, 9223372036854775807), - IDLBuiltinType.Types.unsigned_long_long: (0, 18446744073709551615), -} - - -def matchIntegerValueToType(value): - for type, extremes in integerTypeSizes.items(): - (min, max) = extremes - if value <= max and value >= min: - return BuiltinTypes[type] - - return None - - -class NoCoercionFoundError(WebIDLError): - """ - A class we use to indicate generic coercion failures because none of the - types worked out in IDLValue.coerceToType. - """ - - -class IDLValue(IDLObject): - def __init__(self, location, type, value): - IDLObject.__init__(self, location) - self.type = type - assert isinstance(type, IDLType) - - self.value = value - - def coerceToType(self, type, location): - if type == self.type: - return self # Nothing to do - - # We first check for unions to ensure that even if the union is nullable - # we end up with the right flat member type, not the union's type. - if type.isUnion(): - # We use the flat member types here, because if we have a nullable - # member type, or a nested union, we want the type the value - # actually coerces to, not the nullable or nested union type. - for subtype in type.unroll().flatMemberTypes: - try: - coercedValue = self.coerceToType(subtype, location) - # Create a new IDLValue to make sure that we have the - # correct float/double type. This is necessary because we - # use the value's type when it is a default value of a - # union, and the union cares about the exact float type. - return IDLValue(self.location, subtype, coercedValue.value) - except Exception as e: - # Make sure to propagate out WebIDLErrors that are not the - # generic "hey, we could not coerce to this type at all" - # exception, because those are specific "coercion failed for - # reason X" exceptions. Note that we want to swallow - # non-WebIDLErrors here, because those can just happen if - # "type" is not something that can have a default value at - # all. - if isinstance(e, WebIDLError) and not isinstance( - e, NoCoercionFoundError - ): - raise e - - # If the type allows null, rerun this matching on the inner type, except - # nullable enums. We handle those specially, because we want our - # default string values to stay strings even when assigned to a nullable - # enum. - elif type.nullable() and not type.isEnum(): - innerValue = self.coerceToType(type.inner, location) - return IDLValue(self.location, type, innerValue.value) - - elif self.type.isInteger() and type.isInteger(): - # We're both integer types. See if we fit. - - (min, max) = integerTypeSizes[type._typeTag] - if self.value <= max and self.value >= min: - # Promote - return IDLValue(self.location, type, self.value) - else: - raise WebIDLError( - "Value %s is out of range for type %s." % (self.value, type), - [location], - ) - elif self.type.isInteger() and type.isFloat(): - # Convert an integer literal into float - if -(2 ** 24) <= self.value <= 2 ** 24: - return IDLValue(self.location, type, float(self.value)) - else: - raise WebIDLError( - "Converting value %s to %s will lose precision." - % (self.value, type), - [location], - ) - elif self.type.isString() and type.isEnum(): - # Just keep our string, but make sure it's a valid value for this enum - enum = type.unroll().inner - if self.value not in enum.values(): - raise WebIDLError( - "'%s' is not a valid default value for enum %s" - % (self.value, enum.identifier.name), - [location, enum.location], - ) - return self - elif self.type.isFloat() and type.isFloat(): - if not type.isUnrestricted() and ( - self.value == float("inf") - or self.value == float("-inf") - or math.isnan(self.value) - ): - raise WebIDLError( - "Trying to convert unrestricted value %s to non-unrestricted" - % self.value, - [location], - ) - return IDLValue(self.location, type, self.value) - elif self.type.isString() and type.isUSVString(): - # Allow USVStrings to use default value just like - # DOMString. No coercion is required in this case as Codegen.py - # treats USVString just like DOMString, but with an - # extra normalization step. - assert self.type.isDOMString() - return self - elif self.type.isString() and ( - type.isByteString() or type.isJSString() or type.isUTF8String() - ): - # Allow ByteStrings, UTF8String, and JSStrings to use a default - # value like DOMString. - # No coercion is required as Codegen.py will handle the - # extra steps. We want to make sure that our string contains - # only valid characters, so we check that here. - valid_ascii_lit = ( - " " + string.ascii_letters + string.digits + string.punctuation - ) - for idx, c in enumerate(self.value): - if c not in valid_ascii_lit: - raise WebIDLError( - "Coercing this string literal %s to a ByteString is not supported yet. " - "Coercion failed due to an unsupported byte %d at index %d." - % (self.value.__repr__(), ord(c), idx), - [location], - ) - - return IDLValue(self.location, type, self.value) - elif self.type.isDOMString() and type.legacyNullToEmptyString: - # LegacyNullToEmptyString is a different type for resolution reasons, - # however once you have a value it doesn't matter - return self - - raise NoCoercionFoundError( - "Cannot coerce type %s to type %s." % (self.type, type), [location] - ) - - def _getDependentObjects(self): - return set() - - -class IDLNullValue(IDLObject): - def __init__(self, location): - IDLObject.__init__(self, location) - self.type = None - self.value = None - - def coerceToType(self, type, location): - if ( - not isinstance(type, IDLNullableType) - and not (type.isUnion() and type.hasNullableType) - and not type.isAny() - ): - raise WebIDLError("Cannot coerce null value to type %s." % type, [location]) - - nullValue = IDLNullValue(self.location) - if type.isUnion() and not type.nullable() and type.hasDictionaryType(): - # We're actually a default value for the union's dictionary member. - # Use its type. - for t in type.flatMemberTypes: - if t.isDictionary(): - nullValue.type = t - return nullValue - nullValue.type = type - return nullValue - - def _getDependentObjects(self): - return set() - - -class IDLEmptySequenceValue(IDLObject): - def __init__(self, location): - IDLObject.__init__(self, location) - self.type = None - self.value = None - - def coerceToType(self, type, location): - if type.isUnion(): - # We use the flat member types here, because if we have a nullable - # member type, or a nested union, we want the type the value - # actually coerces to, not the nullable or nested union type. - for subtype in type.unroll().flatMemberTypes: - try: - return self.coerceToType(subtype, location) - except: - pass - - if not type.isSequence(): - raise WebIDLError( - "Cannot coerce empty sequence value to type %s." % type, [location] - ) - - emptySequenceValue = IDLEmptySequenceValue(self.location) - emptySequenceValue.type = type - return emptySequenceValue - - def _getDependentObjects(self): - return set() - - -class IDLDefaultDictionaryValue(IDLObject): - def __init__(self, location): - IDLObject.__init__(self, location) - self.type = None - self.value = None - - def coerceToType(self, type, location): - if type.isUnion(): - # We use the flat member types here, because if we have a nullable - # member type, or a nested union, we want the type the value - # actually coerces to, not the nullable or nested union type. - for subtype in type.unroll().flatMemberTypes: - try: - return self.coerceToType(subtype, location) - except: - pass - - if not type.isDictionary(): - raise WebIDLError( - "Cannot coerce default dictionary value to type %s." % type, [location] - ) - - defaultDictionaryValue = IDLDefaultDictionaryValue(self.location) - defaultDictionaryValue.type = type - return defaultDictionaryValue - - def _getDependentObjects(self): - return set() - - -class IDLUndefinedValue(IDLObject): - def __init__(self, location): - IDLObject.__init__(self, location) - self.type = None - self.value = None - - def coerceToType(self, type, location): - if not type.isAny(): - raise WebIDLError( - "Cannot coerce undefined value to type %s." % type, [location] - ) - - undefinedValue = IDLUndefinedValue(self.location) - undefinedValue.type = type - return undefinedValue - - def _getDependentObjects(self): - return set() - - -class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins): - - Tags = enum( - "Const", "Attr", "Method", "MaplikeOrSetlike", "AsyncIterable", "Iterable" - ) - - Special = enum("Static", "Stringifier") - - AffectsValues = ("Nothing", "Everything") - DependsOnValues = ("Nothing", "DOMState", "DeviceState", "Everything") - - def __init__(self, location, identifier, tag, extendedAttrDict=None): - IDLObjectWithIdentifier.__init__(self, location, None, identifier) - IDLExposureMixins.__init__(self, location) - self.tag = tag - if extendedAttrDict is None: - self._extendedAttrDict = {} - else: - self._extendedAttrDict = extendedAttrDict - - def isMethod(self): - return self.tag == IDLInterfaceMember.Tags.Method - - def isAttr(self): - return self.tag == IDLInterfaceMember.Tags.Attr - - def isConst(self): - return self.tag == IDLInterfaceMember.Tags.Const - - def isMaplikeOrSetlikeOrIterable(self): - return ( - self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike - or self.tag == IDLInterfaceMember.Tags.AsyncIterable - or self.tag == IDLInterfaceMember.Tags.Iterable - ) - - def isMaplikeOrSetlike(self): - return self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike - - def addExtendedAttributes(self, attrs): - for attr in attrs: - self.handleExtendedAttribute(attr) - attrlist = attr.listValue() - self._extendedAttrDict[attr.identifier()] = ( - attrlist if len(attrlist) else True - ) - - def handleExtendedAttribute(self, attr): - pass - - def getExtendedAttribute(self, name): - return self._extendedAttrDict.get(name, None) - - def finish(self, scope): - IDLExposureMixins.finish(self, scope) - - def validate(self): - if self.isAttr() or self.isMethod(): - if self.affects == "Everything" and self.dependsOn != "Everything": - raise WebIDLError( - "Interface member is flagged as affecting " - "everything but not depending on everything. " - "That seems rather unlikely.", - [self.location], - ) - - if self.getExtendedAttribute("NewObject"): - if self.dependsOn == "Nothing" or self.dependsOn == "DOMState": - raise WebIDLError( - "A [NewObject] method is not idempotent, " - "so it has to depend on something other than DOM state.", - [self.location], - ) - if self.getExtendedAttribute("Cached") or self.getExtendedAttribute( - "StoreInSlot" - ): - raise WebIDLError( - "A [NewObject] attribute shouldnt be " - "[Cached] or [StoreInSlot], since the point " - "of those is to keep returning the same " - "thing across multiple calls, which is not " - "what [NewObject] does.", - [self.location], - ) - - def _setDependsOn(self, dependsOn): - if self.dependsOn != "Everything": - raise WebIDLError( - "Trying to specify multiple different DependsOn, " - "Pure, or Constant extended attributes for " - "attribute", - [self.location], - ) - if dependsOn not in IDLInterfaceMember.DependsOnValues: - raise WebIDLError( - "Invalid [DependsOn=%s] on attribute" % dependsOn, [self.location] - ) - self.dependsOn = dependsOn - - def _setAffects(self, affects): - if self.affects != "Everything": - raise WebIDLError( - "Trying to specify multiple different Affects, " - "Pure, or Constant extended attributes for " - "attribute", - [self.location], - ) - if affects not in IDLInterfaceMember.AffectsValues: - raise WebIDLError( - "Invalid [Affects=%s] on attribute" % dependsOn, [self.location] - ) - self.affects = affects - - def _addAlias(self, alias): - if alias in self.aliases: - raise WebIDLError( - "Duplicate [Alias=%s] on attribute" % alias, [self.location] - ) - self.aliases.append(alias) - - def _addBindingAlias(self, bindingAlias): - if bindingAlias in self.bindingAliases: - raise WebIDLError( - "Duplicate [BindingAlias=%s] on attribute" % bindingAlias, - [self.location], - ) - self.bindingAliases.append(bindingAlias) - - -class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember): - def __init__(self, location, identifier, ifaceType, keyType, valueType, ifaceKind): - IDLInterfaceMember.__init__(self, location, identifier, ifaceKind) - if keyType is not None: - assert isinstance(keyType, IDLType) - else: - assert valueType is not None - assert ifaceType in ["maplike", "setlike", "iterable", "asynciterable"] - if valueType is not None: - assert isinstance(valueType, IDLType) - self.keyType = keyType - self.valueType = valueType - self.maplikeOrSetlikeOrIterableType = ifaceType - self.disallowedMemberNames = [] - self.disallowedNonMethodNames = [] - - def isMaplike(self): - return self.maplikeOrSetlikeOrIterableType == "maplike" - - def isSetlike(self): - return self.maplikeOrSetlikeOrIterableType == "setlike" - - def isIterable(self): - return self.maplikeOrSetlikeOrIterableType == "iterable" - - def isAsyncIterable(self): - return self.maplikeOrSetlikeOrIterableType == "asynciterable" - - def hasKeyType(self): - return self.keyType is not None - - def hasValueType(self): - return self.valueType is not None - - def checkCollisions(self, members, isAncestor): - for member in members: - # Check that there are no disallowed members - if member.identifier.name in self.disallowedMemberNames and not ( - (member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod()) - or (member.isAttr() and member.isMaplikeOrSetlikeAttr()) - ): - raise WebIDLError( - "Member '%s' conflicts " - "with reserved %s name." - % (member.identifier.name, self.maplikeOrSetlikeOrIterableType), - [self.location, member.location], - ) - # Check that there are no disallowed non-method members. - # Ancestor members are always disallowed here; own members - # are disallowed only if they're non-methods. - if ( - isAncestor or member.isAttr() or member.isConst() - ) and member.identifier.name in self.disallowedNonMethodNames: - raise WebIDLError( - "Member '%s' conflicts " - "with reserved %s method." - % (member.identifier.name, self.maplikeOrSetlikeOrIterableType), - [self.location, member.location], - ) - - def addMethod( - self, - name, - members, - allowExistingOperations, - returnType, - args=[], - chromeOnly=False, - isPure=False, - affectsNothing=False, - newObject=False, - isIteratorAlias=False, - ): - """ - Create an IDLMethod based on the parameters passed in. - - - members is the member list to add this function to, since this is - called during the member expansion portion of interface object - building. - - - chromeOnly is only True for read-only js implemented classes, to - implement underscore prefixed convenience functions which would - otherwise not be available, unlike the case of C++ bindings. - - - isPure is only True for idempotent functions, so it is not valid for - things like keys, values, etc. that return a new object every time. - - - affectsNothing means that nothing changes due to this method, which - affects JIT optimization behavior - - - newObject means the method creates and returns a new object. - - """ - # Only add name to lists for collision checks if it's not chrome - # only. - if chromeOnly: - name = "__" + name - else: - if not allowExistingOperations: - self.disallowedMemberNames.append(name) - else: - self.disallowedNonMethodNames.append(name) - # If allowExistingOperations is True, and another operation exists - # with the same name as the one we're trying to add, don't add the - # maplike/setlike operation. However, if the operation is static, - # then fail by way of creating the function, which will cause a - # naming conflict, per the spec. - if allowExistingOperations: - for m in members: - if m.identifier.name == name and m.isMethod() and not m.isStatic(): - return - method = IDLMethod( - self.location, - IDLUnresolvedIdentifier( - self.location, name, allowDoubleUnderscore=chromeOnly - ), - returnType, - args, - maplikeOrSetlikeOrIterable=self, - ) - # We need to be able to throw from declaration methods - method.addExtendedAttributes([IDLExtendedAttribute(self.location, ("Throws",))]) - if chromeOnly: - method.addExtendedAttributes( - [IDLExtendedAttribute(self.location, ("ChromeOnly",))] - ) - if isPure: - method.addExtendedAttributes( - [IDLExtendedAttribute(self.location, ("Pure",))] - ) - # Following attributes are used for keys/values/entries. Can't mark - # them pure, since they return a new object each time they are run. - if affectsNothing: - method.addExtendedAttributes( - [ - IDLExtendedAttribute(self.location, ("DependsOn", "Everything")), - IDLExtendedAttribute(self.location, ("Affects", "Nothing")), - ] - ) - if newObject: - method.addExtendedAttributes( - [IDLExtendedAttribute(self.location, ("NewObject",))] - ) - if isIteratorAlias: - if not self.isAsyncIterable(): - method.addExtendedAttributes( - [IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))] - ) - else: - method.addExtendedAttributes( - [IDLExtendedAttribute(self.location, ("Alias", "@@asyncIterator"))] - ) - members.append(method) - - def resolve(self, parentScope): - if self.keyType: - self.keyType.resolveType(parentScope) - if self.valueType: - self.valueType.resolveType(parentScope) - - def finish(self, scope): - IDLInterfaceMember.finish(self, scope) - if self.keyType and not self.keyType.isComplete(): - t = self.keyType.complete(scope) - - assert not isinstance(t, IDLUnresolvedType) - assert not isinstance(t, IDLTypedefType) - assert not isinstance(t.name, IDLUnresolvedIdentifier) - self.keyType = t - if self.valueType and not self.valueType.isComplete(): - t = self.valueType.complete(scope) - - assert not isinstance(t, IDLUnresolvedType) - assert not isinstance(t, IDLTypedefType) - assert not isinstance(t.name, IDLUnresolvedIdentifier) - self.valueType = t - - def validate(self): - IDLInterfaceMember.validate(self) - - def handleExtendedAttribute(self, attr): - IDLInterfaceMember.handleExtendedAttribute(self, attr) - - def _getDependentObjects(self): - deps = set() - if self.keyType: - deps.add(self.keyType) - if self.valueType: - deps.add(self.valueType) - return deps - - def getForEachArguments(self): - return [ - IDLArgument( - self.location, - IDLUnresolvedIdentifier( - BuiltinLocation("<auto-generated-identifier>"), "callback" - ), - BuiltinTypes[IDLBuiltinType.Types.object], - ), - IDLArgument( - self.location, - IDLUnresolvedIdentifier( - BuiltinLocation("<auto-generated-identifier>"), "thisArg" - ), - BuiltinTypes[IDLBuiltinType.Types.any], - optional=True, - ), - ] - - -# Iterable adds ES6 iterator style functions and traits -# (keys/values/entries/@@iterator) to an interface. -class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase): - def __init__(self, location, identifier, keyType, valueType, scope): - IDLMaplikeOrSetlikeOrIterableBase.__init__( - self, - location, - identifier, - "iterable", - keyType, - valueType, - IDLInterfaceMember.Tags.Iterable, - ) - self.iteratorType = None - - def __str__(self): - return "declared iterable with key '%s' and value '%s'" % ( - self.keyType, - self.valueType, - ) - - def expand(self, members): - """ - In order to take advantage of all of the method machinery in Codegen, - we generate our functions as if they were part of the interface - specification during parsing. - """ - # We only need to add entries/keys/values here if we're a pair iterator. - # Value iterators just copy these from %ArrayPrototype% instead. - if not self.isPairIterator(): - return - - # object entries() - self.addMethod( - "entries", - members, - False, - self.iteratorType, - affectsNothing=True, - newObject=True, - isIteratorAlias=True, - ) - # object keys() - self.addMethod( - "keys", - members, - False, - self.iteratorType, - affectsNothing=True, - newObject=True, - ) - # object values() - self.addMethod( - "values", - members, - False, - self.iteratorType, - affectsNothing=True, - newObject=True, - ) - - # undefined forEach(callback(valueType, keyType), optional any thisArg) - self.addMethod( - "forEach", - members, - False, - BuiltinTypes[IDLBuiltinType.Types.undefined], - self.getForEachArguments(), - ) - - def isValueIterator(self): - return not self.isPairIterator() - - def isPairIterator(self): - return self.hasKeyType() - - -class IDLAsyncIterable(IDLMaplikeOrSetlikeOrIterableBase): - def __init__(self, location, identifier, keyType, valueType, argList, scope): - for arg in argList: - if not arg.optional: - raise WebIDLError( - "The arguments of the asynchronously iterable declaration on " - "%s must all be optional arguments." % identifier, - [arg.location], - ) - - IDLMaplikeOrSetlikeOrIterableBase.__init__( - self, - location, - identifier, - "asynciterable", - keyType, - valueType, - IDLInterfaceMember.Tags.AsyncIterable, - ) - self.iteratorType = None - self.argList = argList - - def __str__(self): - return "declared async iterable with key '%s' and value '%s'" % ( - self.keyType, - self.valueType, - ) - - def expand(self, members): - """ - In order to take advantage of all of the method machinery in Codegen, - we generate our functions as if they were part of the interface - specification during parsing. - """ - # object values() - self.addMethod( - "values", - members, - False, - self.iteratorType, - self.argList, - affectsNothing=True, - newObject=True, - isIteratorAlias=(not self.isPairIterator()), - ) - - # We only need to add entries/keys here if we're a pair iterator. - if not self.isPairIterator(): - return - - # Methods can't share their IDLArguments, so we need to make copies here. - def copyArgList(argList): - return map(copy.copy, argList) - - # object entries() - self.addMethod( - "entries", - members, - False, - self.iteratorType, - copyArgList(self.argList), - affectsNothing=True, - newObject=True, - isIteratorAlias=True, - ) - # object keys() - self.addMethod( - "keys", - members, - False, - self.iteratorType, - copyArgList(self.argList), - affectsNothing=True, - newObject=True, - ) - - def isValueIterator(self): - return not self.isPairIterator() - - def isPairIterator(self): - return self.hasKeyType() - - -# MaplikeOrSetlike adds ES6 map-or-set-like traits to an interface. -class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase): - def __init__( - self, location, identifier, maplikeOrSetlikeType, readonly, keyType, valueType - ): - IDLMaplikeOrSetlikeOrIterableBase.__init__( - self, - location, - identifier, - maplikeOrSetlikeType, - keyType, - valueType, - IDLInterfaceMember.Tags.MaplikeOrSetlike, - ) - self.readonly = readonly - self.slotIndices = None - - # When generating JSAPI access code, we need to know the backing object - # type prefix to create the correct function. Generate here for reuse. - if self.isMaplike(): - self.prefix = "Map" - elif self.isSetlike(): - self.prefix = "Set" - - def __str__(self): - return "declared '%s' with key '%s'" % ( - self.maplikeOrSetlikeOrIterableType, - self.keyType, - ) - - def expand(self, members): - """ - In order to take advantage of all of the method machinery in Codegen, - we generate our functions as if they were part of the interface - specification during parsing. - """ - # Both maplike and setlike have a size attribute - members.append( - IDLAttribute( - self.location, - IDLUnresolvedIdentifier( - BuiltinLocation("<auto-generated-identifier>"), "size" - ), - BuiltinTypes[IDLBuiltinType.Types.unsigned_long], - True, - maplikeOrSetlike=self, - ) - ) - self.reserved_ro_names = ["size"] - self.disallowedMemberNames.append("size") - - # object entries() - self.addMethod( - "entries", - members, - False, - BuiltinTypes[IDLBuiltinType.Types.object], - affectsNothing=True, - isIteratorAlias=self.isMaplike(), - ) - # object keys() - self.addMethod( - "keys", - members, - False, - BuiltinTypes[IDLBuiltinType.Types.object], - affectsNothing=True, - ) - # object values() - self.addMethod( - "values", - members, - False, - BuiltinTypes[IDLBuiltinType.Types.object], - affectsNothing=True, - isIteratorAlias=self.isSetlike(), - ) - - # undefined forEach(callback(valueType, keyType), thisVal) - self.addMethod( - "forEach", - members, - False, - BuiltinTypes[IDLBuiltinType.Types.undefined], - self.getForEachArguments(), - ) - - def getKeyArg(): - return IDLArgument( - self.location, - IDLUnresolvedIdentifier(self.location, "key"), - self.keyType, - ) - - # boolean has(keyType key) - self.addMethod( - "has", - members, - False, - BuiltinTypes[IDLBuiltinType.Types.boolean], - [getKeyArg()], - isPure=True, - ) - - if not self.readonly: - # undefined clear() - self.addMethod( - "clear", members, True, BuiltinTypes[IDLBuiltinType.Types.undefined], [] - ) - # boolean delete(keyType key) - self.addMethod( - "delete", - members, - True, - BuiltinTypes[IDLBuiltinType.Types.boolean], - [getKeyArg()], - ) - - if self.isSetlike(): - if not self.readonly: - # Add returns the set object it just added to. - # object add(keyType key) - - self.addMethod( - "add", - members, - True, - BuiltinTypes[IDLBuiltinType.Types.object], - [getKeyArg()], - ) - return - - # If we get this far, we're a maplike declaration. - - # valueType get(keyType key) - # - # Note that instead of the value type, we're using any here. The - # validity checks should happen as things are inserted into the map, - # and using any as the return type makes code generation much simpler. - # - # TODO: Bug 1155340 may change this to use specific type to provide - # more info to JIT. - self.addMethod( - "get", - members, - False, - BuiltinTypes[IDLBuiltinType.Types.any], - [getKeyArg()], - isPure=True, - ) - - def getValueArg(): - return IDLArgument( - self.location, - IDLUnresolvedIdentifier(self.location, "value"), - self.valueType, - ) - - if not self.readonly: - self.addMethod( - "set", - members, - True, - BuiltinTypes[IDLBuiltinType.Types.object], - [getKeyArg(), getValueArg()], - ) - - -class IDLConst(IDLInterfaceMember): - def __init__(self, location, identifier, type, value): - IDLInterfaceMember.__init__( - self, location, identifier, IDLInterfaceMember.Tags.Const - ) - - assert isinstance(type, IDLType) - if type.isDictionary(): - raise WebIDLError( - "A constant cannot be of a dictionary type", [self.location] - ) - if type.isRecord(): - raise WebIDLError("A constant cannot be of a record type", [self.location]) - self.type = type - self.value = value - - if identifier.name == "prototype": - raise WebIDLError( - "The identifier of a constant must not be 'prototype'", [location] - ) - - def __str__(self): - return "'%s' const '%s'" % (self.type, self.identifier) - - def finish(self, scope): - IDLInterfaceMember.finish(self, scope) - - if not self.type.isComplete(): - type = self.type.complete(scope) - if not type.isPrimitive() and not type.isString(): - locations = [self.type.location, type.location] - try: - locations.append(type.inner.location) - except: - pass - raise WebIDLError("Incorrect type for constant", locations) - self.type = type - - # The value might not match the type - coercedValue = self.value.coerceToType(self.type, self.location) - assert coercedValue - - self.value = coercedValue - - def validate(self): - IDLInterfaceMember.validate(self) - - def handleExtendedAttribute(self, attr): - identifier = attr.identifier() - if identifier == "Exposed": - convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) - elif ( - identifier == "Pref" - or identifier == "ChromeOnly" - or identifier == "Func" - or identifier == "Trial" - or identifier == "SecureContext" - or identifier == "NonEnumerable" - ): - # Known attributes that we don't need to do anything with here - pass - else: - raise WebIDLError( - "Unknown extended attribute %s on constant" % identifier, - [attr.location], - ) - IDLInterfaceMember.handleExtendedAttribute(self, attr) - - def _getDependentObjects(self): - return set([self.type, self.value]) - - -class IDLAttribute(IDLInterfaceMember): - def __init__( - self, - location, - identifier, - type, - readonly, - inherit=False, - static=False, - stringifier=False, - maplikeOrSetlike=None, - extendedAttrDict=None, - ): - IDLInterfaceMember.__init__( - self, - location, - identifier, - IDLInterfaceMember.Tags.Attr, - extendedAttrDict=extendedAttrDict, - ) - - assert isinstance(type, IDLType) - self.type = type - self.readonly = readonly - self.inherit = inherit - self._static = static - self.legacyLenientThis = False - self._legacyUnforgeable = False - self.stringifier = stringifier - self.slotIndices = None - assert maplikeOrSetlike is None or isinstance( - maplikeOrSetlike, IDLMaplikeOrSetlike - ) - self.maplikeOrSetlike = maplikeOrSetlike - self.dependsOn = "Everything" - self.affects = "Everything" - self.bindingAliases = [] - - if static and identifier.name == "prototype": - raise WebIDLError( - "The identifier of a static attribute must not be 'prototype'", - [location], - ) - - if readonly and inherit: - raise WebIDLError( - "An attribute cannot be both 'readonly' and 'inherit'", [self.location] - ) - - def isStatic(self): - return self._static - - def forceStatic(self): - self._static = True - - def __str__(self): - return "'%s' attribute '%s'" % (self.type, self.identifier) - - def finish(self, scope): - IDLInterfaceMember.finish(self, scope) - - if not self.type.isComplete(): - t = self.type.complete(scope) - - assert not isinstance(t, IDLUnresolvedType) - assert not isinstance(t, IDLTypedefType) - assert not isinstance(t.name, IDLUnresolvedIdentifier) - self.type = t - - if self.readonly and ( - self.type.hasClamp() - or self.type.hasEnforceRange() - or self.type.hasAllowShared() - or self.type.legacyNullToEmptyString - ): - raise WebIDLError( - "A readonly attribute cannot be [Clamp] or [EnforceRange] or [AllowShared]", - [self.location], - ) - if self.type.isDictionary() and not self.getExtendedAttribute("Cached"): - raise WebIDLError( - "An attribute cannot be of a dictionary type", [self.location] - ) - if self.type.isSequence() and not self.getExtendedAttribute("Cached"): - raise WebIDLError( - "A non-cached attribute cannot be of a sequence " "type", - [self.location], - ) - if self.type.isRecord() and not self.getExtendedAttribute("Cached"): - raise WebIDLError( - "A non-cached attribute cannot be of a record " "type", [self.location] - ) - if self.type.isUnion(): - for f in self.type.unroll().flatMemberTypes: - if f.isDictionary(): - raise WebIDLError( - "An attribute cannot be of a union " - "type if one of its member types (or " - "one of its member types's member " - "types, and so on) is a dictionary " - "type", - [self.location, f.location], - ) - if f.isSequence(): - raise WebIDLError( - "An attribute cannot be of a union " - "type if one of its member types (or " - "one of its member types's member " - "types, and so on) is a sequence " - "type", - [self.location, f.location], - ) - if f.isRecord(): - raise WebIDLError( - "An attribute cannot be of a union " - "type if one of its member types (or " - "one of its member types's member " - "types, and so on) is a record " - "type", - [self.location, f.location], - ) - if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"): - raise WebIDLError( - "An attribute with [PutForwards] must have an " - "interface type as its type", - [self.location], - ) - - if not self.type.isInterface() and self.getExtendedAttribute("SameObject"): - raise WebIDLError( - "An attribute with [SameObject] must have an " - "interface type as its type", - [self.location], - ) - - if self.type.isPromise() and not self.readonly: - raise WebIDLError( - "Promise-returning attributes must be readonly", [self.location] - ) - - if self.type.isObservableArray(): - if self.isStatic(): - raise WebIDLError( - "A static attribute cannot have an ObservableArray type", - [self.location], - ) - if self.getExtendedAttribute("Cached") or self.getExtendedAttribute( - "StoreInSlot" - ): - raise WebIDLError( - "[Cached] and [StoreInSlot] must not be used " - "on an attribute whose type is ObservableArray", - [self.location], - ) - - def validate(self): - def typeContainsChromeOnlyDictionaryMember(type): - if type.nullable() or type.isSequence() or type.isRecord(): - return typeContainsChromeOnlyDictionaryMember(type.inner) - - if type.isUnion(): - for memberType in type.flatMemberTypes: - (contains, location) = typeContainsChromeOnlyDictionaryMember( - memberType - ) - if contains: - return (True, location) - - if type.isDictionary(): - dictionary = type.inner - while dictionary: - (contains, location) = dictionaryContainsChromeOnlyMember( - dictionary - ) - if contains: - return (True, location) - dictionary = dictionary.parent - - return (False, None) - - def dictionaryContainsChromeOnlyMember(dictionary): - for member in dictionary.members: - if member.getExtendedAttribute("ChromeOnly"): - return (True, member.location) - (contains, location) = typeContainsChromeOnlyDictionaryMember( - member.type - ) - if contains: - return (True, location) - return (False, None) - - IDLInterfaceMember.validate(self) - - if self.getExtendedAttribute("Cached") or self.getExtendedAttribute( - "StoreInSlot" - ): - if not self.affects == "Nothing": - raise WebIDLError( - "Cached attributes and attributes stored in " - "slots must be Constant or Pure or " - "Affects=Nothing, since the getter won't always " - "be called.", - [self.location], - ) - (contains, location) = typeContainsChromeOnlyDictionaryMember(self.type) - if contains: - raise WebIDLError( - "[Cached] and [StoreInSlot] must not be used " - "on an attribute whose type contains a " - "[ChromeOnly] dictionary member", - [self.location, location], - ) - if self.getExtendedAttribute("Frozen"): - if ( - not self.type.isSequence() - and not self.type.isDictionary() - and not self.type.isRecord() - ): - raise WebIDLError( - "[Frozen] is only allowed on " - "sequence-valued, dictionary-valued, and " - "record-valued attributes", - [self.location], - ) - if not self.type.unroll().isExposedInAllOf(self.exposureSet): - raise WebIDLError( - "Attribute returns a type that is not exposed " - "everywhere where the attribute is exposed", - [self.location], - ) - if self.getExtendedAttribute("CEReactions"): - if self.readonly: - raise WebIDLError( - "[CEReactions] is not allowed on " "readonly attributes", - [self.location], - ) - - def handleExtendedAttribute(self, attr): - identifier = attr.identifier() - if ( - identifier == "SetterThrows" - or identifier == "SetterCanOOM" - or identifier == "SetterNeedsSubjectPrincipal" - ) and self.readonly: - raise WebIDLError( - "Readonly attributes must not be flagged as " "[%s]" % identifier, - [self.location], - ) - elif identifier == "BindingAlias": - if not attr.hasValue(): - raise WebIDLError( - "[BindingAlias] takes an identifier or string", [attr.location] - ) - self._addBindingAlias(attr.value()) - elif ( - ( - identifier == "Throws" - or identifier == "GetterThrows" - or identifier == "CanOOM" - or identifier == "GetterCanOOM" - ) - and self.getExtendedAttribute("StoreInSlot") - ) or ( - identifier == "StoreInSlot" - and ( - self.getExtendedAttribute("Throws") - or self.getExtendedAttribute("GetterThrows") - or self.getExtendedAttribute("CanOOM") - or self.getExtendedAttribute("GetterCanOOM") - ) - ): - raise WebIDLError("Throwing things can't be [StoreInSlot]", [attr.location]) - elif identifier == "LegacyLenientThis": - if not attr.noArguments(): - raise WebIDLError( - "[LegacyLenientThis] must take no arguments", [attr.location] - ) - if self.isStatic(): - raise WebIDLError( - "[LegacyLenientThis] is only allowed on non-static " "attributes", - [attr.location, self.location], - ) - if self.getExtendedAttribute("CrossOriginReadable"): - raise WebIDLError( - "[LegacyLenientThis] is not allowed in combination " - "with [CrossOriginReadable]", - [attr.location, self.location], - ) - if self.getExtendedAttribute("CrossOriginWritable"): - raise WebIDLError( - "[LegacyLenientThis] is not allowed in combination " - "with [CrossOriginWritable]", - [attr.location, self.location], - ) - self.legacyLenientThis = True - elif identifier == "LegacyUnforgeable": - if self.isStatic(): - raise WebIDLError( - "[LegacyUnforgeable] is only allowed on non-static " "attributes", - [attr.location, self.location], - ) - self._legacyUnforgeable = True - elif identifier == "SameObject" and not self.readonly: - raise WebIDLError( - "[SameObject] only allowed on readonly attributes", - [attr.location, self.location], - ) - elif identifier == "Constant" and not self.readonly: - raise WebIDLError( - "[Constant] only allowed on readonly attributes", - [attr.location, self.location], - ) - elif identifier == "PutForwards": - if not self.readonly: - raise WebIDLError( - "[PutForwards] is only allowed on readonly " "attributes", - [attr.location, self.location], - ) - if self.type.isPromise(): - raise WebIDLError( - "[PutForwards] is not allowed on " "Promise-typed attributes", - [attr.location, self.location], - ) - if self.isStatic(): - raise WebIDLError( - "[PutForwards] is only allowed on non-static " "attributes", - [attr.location, self.location], - ) - if self.getExtendedAttribute("Replaceable") is not None: - raise WebIDLError( - "[PutForwards] and [Replaceable] can't both " - "appear on the same attribute", - [attr.location, self.location], - ) - if not attr.hasValue(): - raise WebIDLError( - "[PutForwards] takes an identifier", [attr.location, self.location] - ) - elif identifier == "Replaceable": - if not attr.noArguments(): - raise WebIDLError( - "[Replaceable] must take no arguments", [attr.location] - ) - if not self.readonly: - raise WebIDLError( - "[Replaceable] is only allowed on readonly " "attributes", - [attr.location, self.location], - ) - if self.type.isPromise(): - raise WebIDLError( - "[Replaceable] is not allowed on " "Promise-typed attributes", - [attr.location, self.location], - ) - if self.isStatic(): - raise WebIDLError( - "[Replaceable] is only allowed on non-static " "attributes", - [attr.location, self.location], - ) - if self.getExtendedAttribute("PutForwards") is not None: - raise WebIDLError( - "[PutForwards] and [Replaceable] can't both " - "appear on the same attribute", - [attr.location, self.location], - ) - elif identifier == "LegacyLenientSetter": - if not attr.noArguments(): - raise WebIDLError( - "[LegacyLenientSetter] must take no arguments", [attr.location] - ) - if not self.readonly: - raise WebIDLError( - "[LegacyLenientSetter] is only allowed on readonly " "attributes", - [attr.location, self.location], - ) - if self.type.isPromise(): - raise WebIDLError( - "[LegacyLenientSetter] is not allowed on " - "Promise-typed attributes", - [attr.location, self.location], - ) - if self.isStatic(): - raise WebIDLError( - "[LegacyLenientSetter] is only allowed on non-static " "attributes", - [attr.location, self.location], - ) - if self.getExtendedAttribute("PutForwards") is not None: - raise WebIDLError( - "[LegacyLenientSetter] and [PutForwards] can't both " - "appear on the same attribute", - [attr.location, self.location], - ) - if self.getExtendedAttribute("Replaceable") is not None: - raise WebIDLError( - "[LegacyLenientSetter] and [Replaceable] can't both " - "appear on the same attribute", - [attr.location, self.location], - ) - elif identifier == "LenientFloat": - if self.readonly: - raise WebIDLError( - "[LenientFloat] used on a readonly attribute", - [attr.location, self.location], - ) - if not self.type.includesRestrictedFloat(): - raise WebIDLError( - "[LenientFloat] used on an attribute with a " - "non-restricted-float type", - [attr.location, self.location], - ) - elif identifier == "StoreInSlot": - if self.getExtendedAttribute("Cached"): - raise WebIDLError( - "[StoreInSlot] and [Cached] must not be " - "specified on the same attribute", - [attr.location, self.location], - ) - elif identifier == "Cached": - if self.getExtendedAttribute("StoreInSlot"): - raise WebIDLError( - "[Cached] and [StoreInSlot] must not be " - "specified on the same attribute", - [attr.location, self.location], - ) - elif identifier == "CrossOriginReadable" or identifier == "CrossOriginWritable": - if not attr.noArguments(): - raise WebIDLError( - "[%s] must take no arguments" % identifier, [attr.location] - ) - if self.isStatic(): - raise WebIDLError( - "[%s] is only allowed on non-static " "attributes" % identifier, - [attr.location, self.location], - ) - if self.getExtendedAttribute("LegacyLenientThis"): - raise WebIDLError( - "[LegacyLenientThis] is not allowed in combination " - "with [%s]" % identifier, - [attr.location, self.location], - ) - elif identifier == "Exposed": - convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) - elif identifier == "Pure": - if not attr.noArguments(): - raise WebIDLError("[Pure] must take no arguments", [attr.location]) - self._setDependsOn("DOMState") - self._setAffects("Nothing") - elif identifier == "Constant" or identifier == "SameObject": - if not attr.noArguments(): - raise WebIDLError( - "[%s] must take no arguments" % identifier, [attr.location] - ) - self._setDependsOn("Nothing") - self._setAffects("Nothing") - elif identifier == "Affects": - if not attr.hasValue(): - raise WebIDLError("[Affects] takes an identifier", [attr.location]) - self._setAffects(attr.value()) - elif identifier == "DependsOn": - if not attr.hasValue(): - raise WebIDLError("[DependsOn] takes an identifier", [attr.location]) - if ( - attr.value() != "Everything" - and attr.value() != "DOMState" - and not self.readonly - ): - raise WebIDLError( - "[DependsOn=%s] only allowed on " - "readonly attributes" % attr.value(), - [attr.location, self.location], - ) - self._setDependsOn(attr.value()) - elif identifier == "UseCounter": - if self.stringifier: - raise WebIDLError( - "[UseCounter] must not be used on a " "stringifier attribute", - [attr.location, self.location], - ) - elif identifier == "Unscopable": - if not attr.noArguments(): - raise WebIDLError( - "[Unscopable] must take no arguments", [attr.location] - ) - if self.isStatic(): - raise WebIDLError( - "[Unscopable] is only allowed on non-static " - "attributes and operations", - [attr.location, self.location], - ) - elif identifier == "CEReactions": - if not attr.noArguments(): - raise WebIDLError( - "[CEReactions] must take no arguments", [attr.location] - ) - elif ( - identifier == "Pref" - or identifier == "Deprecated" - or identifier == "SetterThrows" - or identifier == "Throws" - or identifier == "GetterThrows" - or identifier == "SetterCanOOM" - or identifier == "CanOOM" - or identifier == "GetterCanOOM" - or identifier == "ChromeOnly" - or identifier == "Func" - or identifier == "Trial" - or identifier == "SecureContext" - or identifier == "Frozen" - or identifier == "NewObject" - or identifier == "NeedsSubjectPrincipal" - or identifier == "SetterNeedsSubjectPrincipal" - or identifier == "GetterNeedsSubjectPrincipal" - or identifier == "NeedsCallerType" - or identifier == "ReturnValueNeedsContainsHack" - or identifier == "BinaryName" - or identifier == "NonEnumerable" - ): - # Known attributes that we don't need to do anything with here - pass - else: - raise WebIDLError( - "Unknown extended attribute %s on attribute" % identifier, - [attr.location], - ) - IDLInterfaceMember.handleExtendedAttribute(self, attr) - - def resolve(self, parentScope): - assert isinstance(parentScope, IDLScope) - self.type.resolveType(parentScope) - IDLObjectWithIdentifier.resolve(self, parentScope) - - def hasLegacyLenientThis(self): - return self.legacyLenientThis - - def isMaplikeOrSetlikeAttr(self): - """ - True if this attribute was generated from an interface with - maplike/setlike (e.g. this is the size attribute for - maplike/setlike) - """ - return self.maplikeOrSetlike is not None - - def isLegacyUnforgeable(self): - return self._legacyUnforgeable - - def _getDependentObjects(self): - return set([self.type]) - - def expand(self, members): - assert self.stringifier - if ( - not self.type.isDOMString() - and not self.type.isUSVString() - and not self.type.isUTF8String() - ): - raise WebIDLError( - "The type of a stringifer attribute must be " - "either DOMString, USVString or UTF8String", - [self.location], - ) - identifier = IDLUnresolvedIdentifier( - self.location, "__stringifier", allowDoubleUnderscore=True - ) - method = IDLMethod( - self.location, - identifier, - returnType=self.type, - arguments=[], - stringifier=True, - underlyingAttr=self, - ) - allowedExtAttrs = ["Throws", "NeedsSubjectPrincipal", "Pure"] - # Safe to ignore these as they are only meaningful for attributes - attributeOnlyExtAttrs = [ - "CEReactions", - "CrossOriginWritable", - "SetterThrows", - ] - for (key, value) in self._extendedAttrDict.items(): - if key in allowedExtAttrs: - if value is not True: - raise WebIDLError( - "[%s] with a value is currently " - "unsupported in stringifier attributes, " - "please file a bug to add support" % key, - [self.location], - ) - method.addExtendedAttributes( - [IDLExtendedAttribute(self.location, (key,))] - ) - elif not key in attributeOnlyExtAttrs: - raise WebIDLError( - "[%s] is currently unsupported in " - "stringifier attributes, please file a bug " - "to add support" % key, - [self.location], - ) - members.append(method) - - -class IDLArgument(IDLObjectWithIdentifier): - def __init__( - self, - location, - identifier, - type, - optional=False, - defaultValue=None, - variadic=False, - dictionaryMember=False, - allowTypeAttributes=False, - ): - IDLObjectWithIdentifier.__init__(self, location, None, identifier) - - assert isinstance(type, IDLType) - self.type = type - - self.optional = optional - self.defaultValue = defaultValue - self.variadic = variadic - self.dictionaryMember = dictionaryMember - self._isComplete = False - self._allowTreatNonCallableAsNull = False - self._extendedAttrDict = {} - self.allowTypeAttributes = allowTypeAttributes - - assert not variadic or optional - assert not variadic or not defaultValue - - def addExtendedAttributes(self, attrs): - for attribute in attrs: - identifier = attribute.identifier() - if self.allowTypeAttributes and ( - identifier == "EnforceRange" - or identifier == "Clamp" - or identifier == "LegacyNullToEmptyString" - or identifier == "AllowShared" - ): - self.type = self.type.withExtendedAttributes([attribute]) - elif identifier == "TreatNonCallableAsNull": - self._allowTreatNonCallableAsNull = True - elif self.dictionaryMember and ( - identifier == "ChromeOnly" - or identifier == "Func" - or identifier == "Trial" - or identifier == "Pref" - ): - if not self.optional: - raise WebIDLError( - "[%s] must not be used on a required " - "dictionary member" % identifier, - [attribute.location], - ) - else: - raise WebIDLError( - "Unhandled extended attribute on %s" - % ( - "a dictionary member" - if self.dictionaryMember - else "an argument" - ), - [attribute.location], - ) - attrlist = attribute.listValue() - self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True - - def getExtendedAttribute(self, name): - return self._extendedAttrDict.get(name, None) - - def isComplete(self): - return self._isComplete - - def complete(self, scope): - if self._isComplete: - return - - self._isComplete = True - - if not self.type.isComplete(): - type = self.type.complete(scope) - assert not isinstance(type, IDLUnresolvedType) - assert not isinstance(type, IDLTypedefType) - assert not isinstance(type.name, IDLUnresolvedIdentifier) - self.type = type - - if self.type.isUndefined(): - raise WebIDLError( - "undefined must not be used as the type of an argument in any circumstance", - [self.location], - ) - - if self.type.isAny(): - assert self.defaultValue is None or isinstance( - self.defaultValue, IDLNullValue - ) - # optional 'any' values always have a default value - if self.optional and not self.defaultValue and not self.variadic: - # Set the default value to undefined, for simplicity, so the - # codegen doesn't have to special-case this. - self.defaultValue = IDLUndefinedValue(self.location) - - if self.dictionaryMember and self.type.legacyNullToEmptyString: - raise WebIDLError( - "Dictionary members cannot be [LegacyNullToEmptyString]", - [self.location], - ) - if self.type.isObservableArray(): - raise WebIDLError( - "%s cannot have an ObservableArray type" - % ("Dictionary members" if self.dictionaryMember else "Arguments"), - [self.location], - ) - # Now do the coercing thing; this needs to happen after the - # above creation of a default value. - if self.defaultValue: - self.defaultValue = self.defaultValue.coerceToType(self.type, self.location) - assert self.defaultValue - - def allowTreatNonCallableAsNull(self): - return self._allowTreatNonCallableAsNull - - def _getDependentObjects(self): - deps = set([self.type]) - if self.defaultValue: - deps.add(self.defaultValue) - return deps - - def canHaveMissingValue(self): - return self.optional and not self.defaultValue - - -class IDLCallback(IDLObjectWithScope): - def __init__( - self, location, parentScope, identifier, returnType, arguments, isConstructor - ): - assert isinstance(returnType, IDLType) - - self._returnType = returnType - # Clone the list - self._arguments = list(arguments) - - IDLObjectWithScope.__init__(self, location, parentScope, identifier) - - for (returnType, arguments) in self.signatures(): - for argument in arguments: - argument.resolve(self) - - self._treatNonCallableAsNull = False - self._treatNonObjectAsNull = False - self._isRunScriptBoundary = False - self._isConstructor = isConstructor - - def isCallback(self): - return True - - def isConstructor(self): - return self._isConstructor - - def signatures(self): - return [(self._returnType, self._arguments)] - - def finish(self, scope): - if not self._returnType.isComplete(): - type = self._returnType.complete(scope) - - assert not isinstance(type, IDLUnresolvedType) - assert not isinstance(type, IDLTypedefType) - assert not isinstance(type.name, IDLUnresolvedIdentifier) - self._returnType = type - - for argument in self._arguments: - if argument.type.isComplete(): - continue - - type = argument.type.complete(scope) - - assert not isinstance(type, IDLUnresolvedType) - assert not isinstance(type, IDLTypedefType) - assert not isinstance(type.name, IDLUnresolvedIdentifier) - argument.type = type - - def validate(self): - for argument in self._arguments: - if argument.type.isUndefined(): - raise WebIDLError( - "undefined must not be used as the type of an argument in any circumstance", - [self.location], - ) - - def addExtendedAttributes(self, attrs): - unhandledAttrs = [] - for attr in attrs: - if attr.identifier() == "TreatNonCallableAsNull": - self._treatNonCallableAsNull = True - elif attr.identifier() == "LegacyTreatNonObjectAsNull": - if self._isConstructor: - raise WebIDLError( - "[LegacyTreatNonObjectAsNull] is not supported " - "on constructors", - [self.location], - ) - self._treatNonObjectAsNull = True - elif attr.identifier() == "MOZ_CAN_RUN_SCRIPT_BOUNDARY": - if self._isConstructor: - raise WebIDLError( - "[MOZ_CAN_RUN_SCRIPT_BOUNDARY] is not " - "permitted on constructors", - [self.location], - ) - self._isRunScriptBoundary = True - else: - unhandledAttrs.append(attr) - if self._treatNonCallableAsNull and self._treatNonObjectAsNull: - raise WebIDLError( - "Cannot specify both [TreatNonCallableAsNull] " - "and [LegacyTreatNonObjectAsNull]", - [self.location], - ) - if len(unhandledAttrs) != 0: - IDLType.addExtendedAttributes(self, unhandledAttrs) - - def _getDependentObjects(self): - return set([self._returnType] + self._arguments) - - def isRunScriptBoundary(self): - return self._isRunScriptBoundary - - -class IDLCallbackType(IDLType): - def __init__(self, location, callback): - IDLType.__init__(self, location, callback.identifier.name) - self.callback = callback - - def isCallback(self): - return True - - def tag(self): - return IDLType.Tags.callback - - def isDistinguishableFrom(self, other): - if other.isPromise(): - return False - if other.isUnion(): - # Just forward to the union; it'll deal - return other.isDistinguishableFrom(self) - return ( - other.isUndefined() - or other.isPrimitive() - or other.isString() - or other.isEnum() - or other.isNonCallbackInterface() - or other.isSequence() - ) - - def _getDependentObjects(self): - return self.callback._getDependentObjects() - - -class IDLMethodOverload: - """ - A class that represents a single overload of a WebIDL method. This is not - quite the same as an element of the "effective overload set" in the spec, - because separate IDLMethodOverloads are not created based on arguments being - optional. Rather, when multiple methods have the same name, there is an - IDLMethodOverload for each one, all hanging off an IDLMethod representing - the full set of overloads. - """ - - def __init__(self, returnType, arguments, location): - self.returnType = returnType - # Clone the list of arguments, just in case - self.arguments = list(arguments) - self.location = location - - def _getDependentObjects(self): - deps = set(self.arguments) - deps.add(self.returnType) - return deps - - def includesRestrictedFloatArgument(self): - return any(arg.type.includesRestrictedFloat() for arg in self.arguments) - - -class IDLMethod(IDLInterfaceMember, IDLScope): - - Special = enum( - "Getter", "Setter", "Deleter", "LegacyCaller", base=IDLInterfaceMember.Special - ) - - NamedOrIndexed = enum("Neither", "Named", "Indexed") - - def __init__( - self, - location, - identifier, - returnType, - arguments, - static=False, - getter=False, - setter=False, - deleter=False, - specialType=NamedOrIndexed.Neither, - legacycaller=False, - stringifier=False, - maplikeOrSetlikeOrIterable=None, - underlyingAttr=None, - ): - # REVIEW: specialType is NamedOrIndexed -- wow, this is messed up. - IDLInterfaceMember.__init__( - self, location, identifier, IDLInterfaceMember.Tags.Method - ) - - self._hasOverloads = False - - assert isinstance(returnType, IDLType) - - # self._overloads is a list of IDLMethodOverloads - self._overloads = [IDLMethodOverload(returnType, arguments, location)] - - assert isinstance(static, bool) - self._static = static - assert isinstance(getter, bool) - self._getter = getter - assert isinstance(setter, bool) - self._setter = setter - assert isinstance(deleter, bool) - self._deleter = deleter - assert isinstance(legacycaller, bool) - self._legacycaller = legacycaller - assert isinstance(stringifier, bool) - self._stringifier = stringifier - assert maplikeOrSetlikeOrIterable is None or isinstance( - maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase - ) - self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable - self._htmlConstructor = False - self.underlyingAttr = underlyingAttr - self._specialType = specialType - self._legacyUnforgeable = False - self.dependsOn = "Everything" - self.affects = "Everything" - self.aliases = [] - - if static and identifier.name == "prototype": - raise WebIDLError( - "The identifier of a static operation must not be 'prototype'", - [location], - ) - - self.assertSignatureConstraints() - - def __str__(self): - return "Method '%s'" % self.identifier - - def assertSignatureConstraints(self): - if self._getter or self._deleter: - assert len(self._overloads) == 1 - overload = self._overloads[0] - arguments = overload.arguments - assert len(arguments) == 1 - assert ( - arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] - or arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long] - ) - assert not arguments[0].optional and not arguments[0].variadic - assert not self._getter or not overload.returnType.isUndefined() - - if self._setter: - assert len(self._overloads) == 1 - arguments = self._overloads[0].arguments - assert len(arguments) == 2 - assert ( - arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] - or arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long] - ) - assert not arguments[0].optional and not arguments[0].variadic - assert not arguments[1].optional and not arguments[1].variadic - - if self._stringifier: - assert len(self._overloads) == 1 - overload = self._overloads[0] - assert len(overload.arguments) == 0 - if not self.underlyingAttr: - assert ( - overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring] - ) - - def isStatic(self): - return self._static - - def forceStatic(self): - self._static = True - - def isGetter(self): - return self._getter - - def isSetter(self): - return self._setter - - def isDeleter(self): - return self._deleter - - def isNamed(self): - assert ( - self._specialType == IDLMethod.NamedOrIndexed.Named - or self._specialType == IDLMethod.NamedOrIndexed.Indexed - ) - return self._specialType == IDLMethod.NamedOrIndexed.Named - - def isIndexed(self): - assert ( - self._specialType == IDLMethod.NamedOrIndexed.Named - or self._specialType == IDLMethod.NamedOrIndexed.Indexed - ) - return self._specialType == IDLMethod.NamedOrIndexed.Indexed - - def isLegacycaller(self): - return self._legacycaller - - def isStringifier(self): - return self._stringifier - - def isToJSON(self): - return self.identifier.name == "toJSON" - - def isDefaultToJSON(self): - return self.isToJSON() and self.getExtendedAttribute("Default") - - def isMaplikeOrSetlikeOrIterableMethod(self): - """ - True if this method was generated as part of a - maplike/setlike/etc interface (e.g. has/get methods) - """ - return self.maplikeOrSetlikeOrIterable is not None - - def isSpecial(self): - return ( - self.isGetter() - or self.isSetter() - or self.isDeleter() - or self.isLegacycaller() - or self.isStringifier() - ) - - def isHTMLConstructor(self): - return self._htmlConstructor - - def hasOverloads(self): - return self._hasOverloads - - def isIdentifierLess(self): - """ - True if the method name started with __, and if the method is not a - maplike/setlike method. Interfaces with maplike/setlike will generate - methods starting with __ for chrome only backing object access in JS - implemented interfaces, so while these functions use what is considered - an non-identifier name, they actually DO have an identifier. - """ - return ( - self.identifier.name[:2] == "__" - and not self.isMaplikeOrSetlikeOrIterableMethod() - ) - - def resolve(self, parentScope): - assert isinstance(parentScope, IDLScope) - IDLObjectWithIdentifier.resolve(self, parentScope) - IDLScope.__init__(self, self.location, parentScope, self.identifier) - for (returnType, arguments) in self.signatures(): - for argument in arguments: - argument.resolve(self) - - def addOverload(self, method): - assert len(method._overloads) == 1 - - if self._extendedAttrDict != method._extendedAttrDict: - extendedAttrDiff = set(self._extendedAttrDict.keys()) ^ set( - method._extendedAttrDict.keys() - ) - - if extendedAttrDiff == {"LenientFloat"}: - if "LenientFloat" not in self._extendedAttrDict: - for overload in self._overloads: - if overload.includesRestrictedFloatArgument(): - raise WebIDLError( - "Restricted float behavior differs on different " - "overloads of %s" % method.identifier, - [overload.location, method.location], - ) - self._extendedAttrDict["LenientFloat"] = method._extendedAttrDict[ - "LenientFloat" - ] - elif method._overloads[0].includesRestrictedFloatArgument(): - raise WebIDLError( - "Restricted float behavior differs on different " - "overloads of %s" % method.identifier, - [self.location, method.location], - ) - else: - raise WebIDLError( - "Extended attributes differ on different " - "overloads of %s" % method.identifier, - [self.location, method.location], - ) - - self._overloads.extend(method._overloads) - - self._hasOverloads = True - - if self.isStatic() != method.isStatic(): - raise WebIDLError( - "Overloaded identifier %s appears with different values of the 'static' attribute" - % method.identifier, - [method.location], - ) - - if self.isLegacycaller() != method.isLegacycaller(): - raise WebIDLError( - "Overloaded identifier %s appears with different values of the 'legacycaller' attribute" - % method.identifier, - [method.location], - ) - - # Can't overload special things! - assert not self.isGetter() - assert not method.isGetter() - assert not self.isSetter() - assert not method.isSetter() - assert not self.isDeleter() - assert not method.isDeleter() - assert not self.isStringifier() - assert not method.isStringifier() - assert not self.isHTMLConstructor() - assert not method.isHTMLConstructor() - - return self - - def signatures(self): - return [ - (overload.returnType, overload.arguments) for overload in self._overloads - ] - - def finish(self, scope): - IDLInterfaceMember.finish(self, scope) - - for overload in self._overloads: - returnType = overload.returnType - if not returnType.isComplete(): - returnType = returnType.complete(scope) - assert not isinstance(returnType, IDLUnresolvedType) - assert not isinstance(returnType, IDLTypedefType) - assert not isinstance(returnType.name, IDLUnresolvedIdentifier) - overload.returnType = returnType - - for argument in overload.arguments: - if not argument.isComplete(): - argument.complete(scope) - assert argument.type.isComplete() - - # Now compute various information that will be used by the - # WebIDL overload resolution algorithm. - self.maxArgCount = max(len(s[1]) for s in self.signatures()) - self.allowedArgCounts = [ - i - for i in range(self.maxArgCount + 1) - if len(self.signaturesForArgCount(i)) != 0 - ] - - def validate(self): - IDLInterfaceMember.validate(self) - - # Make sure our overloads are properly distinguishable and don't have - # different argument types before the distinguishing args. - for argCount in self.allowedArgCounts: - possibleOverloads = self.overloadsForArgCount(argCount) - if len(possibleOverloads) == 1: - continue - distinguishingIndex = self.distinguishingIndexForArgCount(argCount) - for idx in range(distinguishingIndex): - firstSigType = possibleOverloads[0].arguments[idx].type - for overload in possibleOverloads[1:]: - if overload.arguments[idx].type != firstSigType: - raise WebIDLError( - "Signatures for method '%s' with %d arguments have " - "different types of arguments at index %d, which " - "is before distinguishing index %d" - % ( - self.identifier.name, - argCount, - idx, - distinguishingIndex, - ), - [self.location, overload.location], - ) - - overloadWithPromiseReturnType = None - overloadWithoutPromiseReturnType = None - for overload in self._overloads: - returnType = overload.returnType - if not returnType.unroll().isExposedInAllOf(self.exposureSet): - raise WebIDLError( - "Overload returns a type that is not exposed " - "everywhere where the method is exposed", - [overload.location], - ) - - variadicArgument = None - - arguments = overload.arguments - for (idx, argument) in enumerate(arguments): - assert argument.type.isComplete() - - if ( - argument.type.isDictionary() - and argument.type.unroll().inner.canBeEmpty() - ) or ( - argument.type.isUnion() - and argument.type.unroll().hasPossiblyEmptyDictionaryType() - ): - # Optional dictionaries and unions containing optional - # dictionaries at the end of the list or followed by - # optional arguments must be optional. - if not argument.optional and all( - arg.optional for arg in arguments[idx + 1 :] - ): - raise WebIDLError( - "Dictionary argument without any " - "required fields or union argument " - "containing such dictionary not " - "followed by a required argument " - "must be optional", - [argument.location], - ) - - if not argument.defaultValue and all( - arg.optional for arg in arguments[idx + 1 :] - ): - raise WebIDLError( - "Dictionary argument without any " - "required fields or union argument " - "containing such dictionary not " - "followed by a required argument " - "must have a default value", - [argument.location], - ) - - # An argument cannot be a nullable dictionary or a - # nullable union containing a dictionary. - if argument.type.nullable() and ( - argument.type.isDictionary() - or ( - argument.type.isUnion() - and argument.type.unroll().hasDictionaryType() - ) - ): - raise WebIDLError( - "An argument cannot be a nullable " - "dictionary or nullable union " - "containing a dictionary", - [argument.location], - ) - - # Only the last argument can be variadic - if variadicArgument: - raise WebIDLError( - "Variadic argument is not last argument", - [variadicArgument.location], - ) - if argument.variadic: - variadicArgument = argument - - if returnType.isPromise(): - overloadWithPromiseReturnType = overload - else: - overloadWithoutPromiseReturnType = overload - - # Make sure either all our overloads return Promises or none do - if overloadWithPromiseReturnType and overloadWithoutPromiseReturnType: - raise WebIDLError( - "We have overloads with both Promise and " "non-Promise return types", - [ - overloadWithPromiseReturnType.location, - overloadWithoutPromiseReturnType.location, - ], - ) - - if overloadWithPromiseReturnType and self._legacycaller: - raise WebIDLError( - "May not have a Promise return type for a " "legacycaller.", - [overloadWithPromiseReturnType.location], - ) - - if self.getExtendedAttribute("StaticClassOverride") and not ( - self.identifier.scope.isJSImplemented() and self.isStatic() - ): - raise WebIDLError( - "StaticClassOverride can be applied to static" - " methods on JS-implemented classes only.", - [self.location], - ) - - # Ensure that toJSON methods satisfy the spec constraints on them. - if self.identifier.name == "toJSON": - if len(self.signatures()) != 1: - raise WebIDLError( - "toJSON method has multiple overloads", - [self._overloads[0].location, self._overloads[1].location], - ) - if len(self.signatures()[0][1]) != 0: - raise WebIDLError("toJSON method has arguments", [self.location]) - if not self.signatures()[0][0].isJSONType(): - raise WebIDLError( - "toJSON method has non-JSON return type", [self.location] - ) - - def overloadsForArgCount(self, argc): - return [ - overload - for overload in self._overloads - if len(overload.arguments) == argc - or ( - len(overload.arguments) > argc - and all(arg.optional for arg in overload.arguments[argc:]) - ) - or ( - len(overload.arguments) < argc - and len(overload.arguments) > 0 - and overload.arguments[-1].variadic - ) - ] - - def signaturesForArgCount(self, argc): - return [ - (overload.returnType, overload.arguments) - for overload in self.overloadsForArgCount(argc) - ] - - def locationsForArgCount(self, argc): - return [overload.location for overload in self.overloadsForArgCount(argc)] - - def distinguishingIndexForArgCount(self, argc): - def isValidDistinguishingIndex(idx, signatures): - for (firstSigIndex, (firstRetval, firstArgs)) in enumerate(signatures[:-1]): - for (secondRetval, secondArgs) in signatures[firstSigIndex + 1 :]: - if idx < len(firstArgs): - firstType = firstArgs[idx].type - else: - assert firstArgs[-1].variadic - firstType = firstArgs[-1].type - if idx < len(secondArgs): - secondType = secondArgs[idx].type - else: - assert secondArgs[-1].variadic - secondType = secondArgs[-1].type - if not firstType.isDistinguishableFrom(secondType): - return False - return True - - signatures = self.signaturesForArgCount(argc) - for idx in range(argc): - if isValidDistinguishingIndex(idx, signatures): - return idx - # No valid distinguishing index. Time to throw - locations = self.locationsForArgCount(argc) - raise WebIDLError( - "Signatures with %d arguments for method '%s' are not " - "distinguishable" % (argc, self.identifier.name), - locations, - ) - - def handleExtendedAttribute(self, attr): - identifier = attr.identifier() - if ( - identifier == "GetterThrows" - or identifier == "SetterThrows" - or identifier == "GetterCanOOM" - or identifier == "SetterCanOOM" - or identifier == "SetterNeedsSubjectPrincipal" - or identifier == "GetterNeedsSubjectPrincipal" - ): - raise WebIDLError( - "Methods must not be flagged as " "[%s]" % identifier, - [attr.location, self.location], - ) - elif identifier == "LegacyUnforgeable": - if self.isStatic(): - raise WebIDLError( - "[LegacyUnforgeable] is only allowed on non-static " "methods", - [attr.location, self.location], - ) - self._legacyUnforgeable = True - elif identifier == "SameObject": - raise WebIDLError( - "Methods must not be flagged as [SameObject]", - [attr.location, self.location], - ) - elif identifier == "Constant": - raise WebIDLError( - "Methods must not be flagged as [Constant]", - [attr.location, self.location], - ) - elif identifier == "PutForwards": - raise WebIDLError( - "Only attributes support [PutForwards]", [attr.location, self.location] - ) - elif identifier == "LegacyLenientSetter": - raise WebIDLError( - "Only attributes support [LegacyLenientSetter]", - [attr.location, self.location], - ) - elif identifier == "LenientFloat": - # This is called before we've done overload resolution - overloads = self._overloads - assert len(overloads) == 1 - if not overloads[0].returnType.isUndefined(): - raise WebIDLError( - "[LenientFloat] used on a non-undefined method", - [attr.location, self.location], - ) - if not overloads[0].includesRestrictedFloatArgument(): - raise WebIDLError( - "[LenientFloat] used on an operation with no " - "restricted float type arguments", - [attr.location, self.location], - ) - elif identifier == "Exposed": - convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames) - elif ( - identifier == "CrossOriginCallable" - or identifier == "WebGLHandlesContextLoss" - ): - # Known no-argument attributes. - if not attr.noArguments(): - raise WebIDLError( - "[%s] must take no arguments" % identifier, [attr.location] - ) - if identifier == "CrossOriginCallable" and self.isStatic(): - raise WebIDLError( - "[CrossOriginCallable] is only allowed on non-static " "attributes", - [attr.location, self.location], - ) - elif identifier == "Pure": - if not attr.noArguments(): - raise WebIDLError("[Pure] must take no arguments", [attr.location]) - self._setDependsOn("DOMState") - self._setAffects("Nothing") - elif identifier == "Affects": - if not attr.hasValue(): - raise WebIDLError("[Affects] takes an identifier", [attr.location]) - self._setAffects(attr.value()) - elif identifier == "DependsOn": - if not attr.hasValue(): - raise WebIDLError("[DependsOn] takes an identifier", [attr.location]) - self._setDependsOn(attr.value()) - elif identifier == "Alias": - if not attr.hasValue(): - raise WebIDLError( - "[Alias] takes an identifier or string", [attr.location] - ) - self._addAlias(attr.value()) - elif identifier == "UseCounter": - if self.isSpecial(): - raise WebIDLError( - "[UseCounter] must not be used on a special " "operation", - [attr.location, self.location], - ) - elif identifier == "Unscopable": - if not attr.noArguments(): - raise WebIDLError( - "[Unscopable] must take no arguments", [attr.location] - ) - if self.isStatic(): - raise WebIDLError( - "[Unscopable] is only allowed on non-static " - "attributes and operations", - [attr.location, self.location], - ) - elif identifier == "CEReactions": - if not attr.noArguments(): - raise WebIDLError( - "[CEReactions] must take no arguments", [attr.location] - ) - - if self.isSpecial() and not self.isSetter() and not self.isDeleter(): - raise WebIDLError( - "[CEReactions] is only allowed on operation, " - "attribute, setter, and deleter", - [attr.location, self.location], - ) - elif identifier == "Default": - if not attr.noArguments(): - raise WebIDLError("[Default] must take no arguments", [attr.location]) - - if not self.isToJSON(): - raise WebIDLError( - "[Default] is only allowed on toJSON operations", - [attr.location, self.location], - ) - - if self.signatures()[0][0] != BuiltinTypes[IDLBuiltinType.Types.object]: - raise WebIDLError( - "The return type of the default toJSON " - "operation must be 'object'", - [attr.location, self.location], - ) - elif ( - identifier == "Throws" - or identifier == "CanOOM" - or identifier == "NewObject" - or identifier == "ChromeOnly" - or identifier == "Pref" - or identifier == "Deprecated" - or identifier == "Func" - or identifier == "Trial" - or identifier == "SecureContext" - or identifier == "BinaryName" - or identifier == "NeedsSubjectPrincipal" - or identifier == "NeedsCallerType" - or identifier == "StaticClassOverride" - or identifier == "NonEnumerable" - or identifier == "Unexposed" - or identifier == "WebExtensionStub" - ): - # Known attributes that we don't need to do anything with here - pass - else: - raise WebIDLError( - "Unknown extended attribute %s on method" % identifier, [attr.location] - ) - IDLInterfaceMember.handleExtendedAttribute(self, attr) - - def returnsPromise(self): - return self._overloads[0].returnType.isPromise() - - def isLegacyUnforgeable(self): - return self._legacyUnforgeable - - def _getDependentObjects(self): - deps = set() - for overload in self._overloads: - deps.update(overload._getDependentObjects()) - return deps - - -class IDLConstructor(IDLMethod): - def __init__(self, location, args, name): - # We can't actually init our IDLMethod yet, because we do not know the - # return type yet. Just save the info we have for now and we will init - # it later. - self._initLocation = location - self._initArgs = args - self._initName = name - self._inited = False - self._initExtendedAttrs = [] - - def addExtendedAttributes(self, attrs): - if self._inited: - return IDLMethod.addExtendedAttributes(self, attrs) - self._initExtendedAttrs.extend(attrs) - - def handleExtendedAttribute(self, attr): - identifier = attr.identifier() - if ( - identifier == "BinaryName" - or identifier == "ChromeOnly" - or identifier == "NewObject" - or identifier == "SecureContext" - or identifier == "Throws" - or identifier == "Func" - or identifier == "Trial" - or identifier == "Pref" - or identifier == "UseCounter" - ): - IDLMethod.handleExtendedAttribute(self, attr) - elif identifier == "HTMLConstructor": - if not attr.noArguments(): - raise WebIDLError( - "[HTMLConstructor] must take no arguments", [attr.location] - ) - # We shouldn't end up here for legacy factory functions. - assert self.identifier.name == "constructor" - - if any(len(sig[1]) != 0 for sig in self.signatures()): - raise WebIDLError( - "[HTMLConstructor] must not be applied to a " - "constructor operation that has arguments.", - [attr.location], - ) - self._htmlConstructor = True - else: - raise WebIDLError( - "Unknown extended attribute %s on method" % identifier, [attr.location] - ) - - def reallyInit(self, parentInterface): - name = self._initName - location = self._initLocation - identifier = IDLUnresolvedIdentifier(location, name, allowForbidden=True) - retType = IDLWrapperType(parentInterface.location, parentInterface) - IDLMethod.__init__( - self, location, identifier, retType, self._initArgs, static=True - ) - self._inited = True - # Propagate through whatever extended attributes we already had - self.addExtendedAttributes(self._initExtendedAttrs) - self._initExtendedAttrs = [] - # Constructors are always NewObject. Whether they throw or not is - # indicated by [Throws] annotations in the usual way. - self.addExtendedAttributes( - [IDLExtendedAttribute(self.location, ("NewObject",))] - ) - - -class IDLIncludesStatement(IDLObject): - def __init__(self, location, interface, mixin): - IDLObject.__init__(self, location) - self.interface = interface - self.mixin = mixin - self._finished = False - - def finish(self, scope): - if self._finished: - return - self._finished = True - assert isinstance(self.interface, IDLIdentifierPlaceholder) - assert isinstance(self.mixin, IDLIdentifierPlaceholder) - interface = self.interface.finish(scope) - mixin = self.mixin.finish(scope) - # NOTE: we depend on not setting self.interface and - # self.mixin here to keep track of the original - # locations. - if not isinstance(interface, IDLInterface): - raise WebIDLError( - "Left-hand side of 'includes' is not an " "interface", - [self.interface.location, interface.location], - ) - if interface.isCallback(): - raise WebIDLError( - "Left-hand side of 'includes' is a callback " "interface", - [self.interface.location, interface.location], - ) - if not isinstance(mixin, IDLInterfaceMixin): - raise WebIDLError( - "Right-hand side of 'includes' is not an " "interface mixin", - [self.mixin.location, mixin.location], - ) - - mixin.actualExposureGlobalNames.update(interface._exposureGlobalNames) - - interface.addIncludedMixin(mixin) - self.interface = interface - self.mixin = mixin - - def validate(self): - pass - - def addExtendedAttributes(self, attrs): - if len(attrs) != 0: - raise WebIDLError( - "There are no extended attributes that are " - "allowed on includes statements", - [attrs[0].location, self.location], - ) - - -class IDLExtendedAttribute(IDLObject): - """ - A class to represent IDL extended attributes so we can give them locations - """ - - def __init__(self, location, tuple): - IDLObject.__init__(self, location) - self._tuple = tuple - - def identifier(self): - return self._tuple[0] - - def noArguments(self): - return len(self._tuple) == 1 - - def hasValue(self): - return len(self._tuple) >= 2 and isinstance(self._tuple[1], str) - - def value(self): - assert self.hasValue() - return self._tuple[1] - - def hasArgs(self): - return ( - len(self._tuple) == 2 - and isinstance(self._tuple[1], list) - or len(self._tuple) == 3 - ) - - def args(self): - assert self.hasArgs() - # Our args are our last element - return self._tuple[-1] - - def listValue(self): - """ - Backdoor for storing random data in _extendedAttrDict - """ - return list(self._tuple)[1:] - - -# Parser - - -class Tokenizer(object): - tokens = ["INTEGER", "FLOATLITERAL", "IDENTIFIER", "STRING", "WHITESPACE", "OTHER"] - - def t_FLOATLITERAL(self, t): - r"(-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+|Infinity))|NaN" - t.value = float(t.value) - return t - - def t_INTEGER(self, t): - r"-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)" - try: - # Can't use int(), because that doesn't handle octal properly. - t.value = parseInt(t.value) - except: - raise WebIDLError( - "Invalid integer literal", - [ - Location( - lexer=self.lexer, - lineno=self.lexer.lineno, - lexpos=self.lexer.lexpos, - filename=self._filename, - ) - ], - ) - return t - - def t_IDENTIFIER(self, t): - r"[_-]?[A-Za-z][0-9A-Z_a-z-]*" - t.type = self.keywords.get(t.value, "IDENTIFIER") - # If Builtin readable streams are disabled, mark ReadableStream as an identifier. - if t.type == "READABLESTREAM" and not self._use_builtin_readable_streams: - t.type = "IDENTIFIER" - return t - - def t_STRING(self, t): - r'"[^"]*"' - t.value = t.value[1:-1] - return t - - def t_WHITESPACE(self, t): - r"[\t\n\r ]+|[\t\n\r ]*((//[^\n]*|/\*.*?\*/)[\t\n\r ]*)+" - pass - - def t_ELLIPSIS(self, t): - r"\.\.\." - t.type = self.keywords.get(t.value) - return t - - def t_OTHER(self, t): - r"[^\t\n\r 0-9A-Z_a-z]" - t.type = self.keywords.get(t.value, "OTHER") - return t - - keywords = { - "interface": "INTERFACE", - "partial": "PARTIAL", - "mixin": "MIXIN", - "dictionary": "DICTIONARY", - "exception": "EXCEPTION", - "enum": "ENUM", - "callback": "CALLBACK", - "typedef": "TYPEDEF", - "includes": "INCLUDES", - "const": "CONST", - "null": "NULL", - "true": "TRUE", - "false": "FALSE", - "serializer": "SERIALIZER", - "stringifier": "STRINGIFIER", - "unrestricted": "UNRESTRICTED", - "attribute": "ATTRIBUTE", - "readonly": "READONLY", - "inherit": "INHERIT", - "static": "STATIC", - "getter": "GETTER", - "setter": "SETTER", - "deleter": "DELETER", - "legacycaller": "LEGACYCALLER", - "optional": "OPTIONAL", - "...": "ELLIPSIS", - "::": "SCOPE", - "DOMString": "DOMSTRING", - "ByteString": "BYTESTRING", - "USVString": "USVSTRING", - "JSString": "JSSTRING", - "UTF8String": "UTF8STRING", - "any": "ANY", - "boolean": "BOOLEAN", - "byte": "BYTE", - "double": "DOUBLE", - "float": "FLOAT", - "long": "LONG", - "object": "OBJECT", - "ObservableArray": "OBSERVABLEARRAY", - "octet": "OCTET", - "Promise": "PROMISE", - "required": "REQUIRED", - "sequence": "SEQUENCE", - "record": "RECORD", - "short": "SHORT", - "unsigned": "UNSIGNED", - "undefined": "UNDEFINED", - ":": "COLON", - ";": "SEMICOLON", - "{": "LBRACE", - "}": "RBRACE", - "(": "LPAREN", - ")": "RPAREN", - "[": "LBRACKET", - "]": "RBRACKET", - "?": "QUESTIONMARK", - "*": "ASTERISK", - ",": "COMMA", - "=": "EQUALS", - "<": "LT", - ">": "GT", - "ArrayBuffer": "ARRAYBUFFER", - "or": "OR", - "maplike": "MAPLIKE", - "setlike": "SETLIKE", - "iterable": "ITERABLE", - "namespace": "NAMESPACE", - "ReadableStream": "READABLESTREAM", - "constructor": "CONSTRUCTOR", - "symbol": "SYMBOL", - "async": "ASYNC", - } - - tokens.extend(keywords.values()) - - def t_error(self, t): - raise WebIDLError( - "Unrecognized Input", - [ - Location( - lexer=self.lexer, - lineno=self.lexer.lineno, - lexpos=self.lexer.lexpos, - filename=self.filename, - ) - ], - ) - - def __init__(self, outputdir, lexer=None, use_builtin_readable_streams=True): - self._use_builtin_readable_streams = use_builtin_readable_streams - if lexer: - self.lexer = lexer - else: - self.lexer = lex.lex(object=self, reflags=re.DOTALL) - - -class SqueakyCleanLogger(object): - errorWhitelist = [ - # Web IDL defines the WHITESPACE token, but doesn't actually - # use it ... so far. - "Token 'WHITESPACE' defined, but not used", - # And that means we have an unused token - "There is 1 unused token", - # Web IDL defines a OtherOrComma rule that's only used in - # ExtendedAttributeInner, which we don't use yet. - "Rule 'OtherOrComma' defined, but not used", - # And an unused rule - "There is 1 unused rule", - # And the OtherOrComma grammar symbol is unreachable. - "Symbol 'OtherOrComma' is unreachable", - # Which means the Other symbol is unreachable. - "Symbol 'Other' is unreachable", - ] - - def __init__(self): - self.errors = [] - - def debug(self, msg, *args, **kwargs): - pass - - info = debug - - def warning(self, msg, *args, **kwargs): - if ( - msg == "%s:%d: Rule %r defined, but not used" - or msg == "%s:%d: Rule '%s' defined, but not used" - ): - # Munge things so we don't have to hardcode filenames and - # line numbers in our whitelist. - whitelistmsg = "Rule %r defined, but not used" - whitelistargs = args[2:] - else: - whitelistmsg = msg - whitelistargs = args - if (whitelistmsg % whitelistargs) not in SqueakyCleanLogger.errorWhitelist: - self.errors.append(msg % args) - - error = warning - - def reportGrammarErrors(self): - if self.errors: - raise WebIDLError("\n".join(self.errors), []) - - -class Parser(Tokenizer): - def getLocation(self, p, i): - return Location(self.lexer, p.lineno(i), p.lexpos(i), self._filename) - - def globalScope(self): - return self._globalScope - - # The p_Foo functions here must match the WebIDL spec's grammar. - # It's acceptable to split things at '|' boundaries. - def p_Definitions(self, p): - """ - Definitions : ExtendedAttributeList Definition Definitions - """ - if p[2]: - p[0] = [p[2]] - p[2].addExtendedAttributes(p[1]) - else: - assert not p[1] - p[0] = [] - - p[0].extend(p[3]) - - def p_DefinitionsEmpty(self, p): - """ - Definitions : - """ - p[0] = [] - - def p_Definition(self, p): - """ - Definition : CallbackOrInterfaceOrMixin - | Namespace - | Partial - | Dictionary - | Exception - | Enum - | Typedef - | IncludesStatement - """ - p[0] = p[1] - assert p[1] # We might not have implemented something ... - - def p_CallbackOrInterfaceOrMixinCallback(self, p): - """ - CallbackOrInterfaceOrMixin : CALLBACK CallbackRestOrInterface - """ - if p[2].isInterface(): - assert isinstance(p[2], IDLInterface) - p[2].setCallback(True) - - p[0] = p[2] - - def p_CallbackOrInterfaceOrMixinInterfaceOrMixin(self, p): - """ - CallbackOrInterfaceOrMixin : INTERFACE InterfaceOrMixin - """ - p[0] = p[2] - - def p_CallbackRestOrInterface(self, p): - """ - CallbackRestOrInterface : CallbackRest - | CallbackConstructorRest - | CallbackInterface - """ - assert p[1] - p[0] = p[1] - - def handleNonPartialObject( - self, location, identifier, constructor, constructorArgs, nonPartialArgs - ): - """ - This handles non-partial objects (interfaces, namespaces and - dictionaries) by checking for an existing partial object, and promoting - it to non-partial as needed. The return value is the non-partial - object. - - constructorArgs are all the args for the constructor except the last - one: isKnownNonPartial. - - nonPartialArgs are the args for the setNonPartial call. - """ - # The name of the class starts with "IDL", so strip that off. - # Also, starts with a capital letter after that, so nix that - # as well. - prettyname = constructor.__name__[3:].lower() - - try: - existingObj = self.globalScope()._lookupIdentifier(identifier) - if existingObj: - if not isinstance(existingObj, constructor): - raise WebIDLError( - "%s has the same name as " - "non-%s object" % (prettyname.capitalize(), prettyname), - [location, existingObj.location], - ) - existingObj.setNonPartial(*nonPartialArgs) - return existingObj - except Exception as ex: - if isinstance(ex, WebIDLError): - raise ex - pass - - # True for isKnownNonPartial - return constructor(*(constructorArgs + [True])) - - def p_InterfaceOrMixin(self, p): - """ - InterfaceOrMixin : InterfaceRest - | MixinRest - """ - p[0] = p[1] - - def p_CallbackInterface(self, p): - """ - CallbackInterface : INTERFACE InterfaceRest - """ - p[0] = p[2] - - def p_InterfaceRest(self, p): - """ - InterfaceRest : IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON - """ - location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(location, p[1]) - members = p[4] - parent = p[2] - - p[0] = self.handleNonPartialObject( - location, - identifier, - IDLInterface, - [location, self.globalScope(), identifier, parent, members], - [location, parent, members], - ) - - def p_InterfaceForwardDecl(self, p): - """ - InterfaceRest : IDENTIFIER SEMICOLON - """ - location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(location, p[1]) - - try: - if self.globalScope()._lookupIdentifier(identifier): - p[0] = self.globalScope()._lookupIdentifier(identifier) - if not isinstance(p[0], IDLExternalInterface): - raise WebIDLError( - "Name collision between external " - "interface declaration for identifier " - "%s and %s" % (identifier.name, p[0]), - [location, p[0].location], - ) - return - except Exception as ex: - if isinstance(ex, WebIDLError): - raise ex - pass - - p[0] = IDLExternalInterface(location, self.globalScope(), identifier) - - def p_MixinRest(self, p): - """ - MixinRest : MIXIN IDENTIFIER LBRACE MixinMembers RBRACE SEMICOLON - """ - location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) - members = p[4] - - p[0] = self.handleNonPartialObject( - location, - identifier, - IDLInterfaceMixin, - [location, self.globalScope(), identifier, members], - [location, members], - ) - - def p_Namespace(self, p): - """ - Namespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON - """ - location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) - members = p[4] - - p[0] = self.handleNonPartialObject( - location, - identifier, - IDLNamespace, - [location, self.globalScope(), identifier, members], - [location, None, members], - ) - - def p_Partial(self, p): - """ - Partial : PARTIAL PartialDefinition - """ - p[0] = p[2] - - def p_PartialDefinitionInterface(self, p): - """ - PartialDefinition : INTERFACE PartialInterfaceOrPartialMixin - """ - p[0] = p[2] - - def p_PartialDefinition(self, p): - """ - PartialDefinition : PartialNamespace - | PartialDictionary - """ - p[0] = p[1] - - def handlePartialObject( - self, - location, - identifier, - nonPartialConstructor, - nonPartialConstructorArgs, - partialConstructorArgs, - ): - """ - This handles partial objects (interfaces, namespaces and dictionaries) - by checking for an existing non-partial object, and adding ourselves to - it as needed. The return value is our partial object. We use - IDLPartialInterfaceOrNamespace for partial interfaces or namespaces, - and IDLPartialDictionary for partial dictionaries. - - nonPartialConstructorArgs are all the args for the non-partial - constructor except the last two: members and isKnownNonPartial. - - partialConstructorArgs are the arguments for the partial object - constructor, except the last one (the non-partial object). - """ - # The name of the class starts with "IDL", so strip that off. - # Also, starts with a capital letter after that, so nix that - # as well. - prettyname = nonPartialConstructor.__name__[3:].lower() - - nonPartialObject = None - try: - nonPartialObject = self.globalScope()._lookupIdentifier(identifier) - if nonPartialObject: - if not isinstance(nonPartialObject, nonPartialConstructor): - raise WebIDLError( - "Partial %s has the same name as " - "non-%s object" % (prettyname, prettyname), - [location, nonPartialObject.location], - ) - except Exception as ex: - if isinstance(ex, WebIDLError): - raise ex - pass - - if not nonPartialObject: - nonPartialObject = nonPartialConstructor( - # No members, False for isKnownNonPartial - *(nonPartialConstructorArgs), - members=[], - isKnownNonPartial=False - ) - - partialObject = None - if isinstance(nonPartialObject, IDLDictionary): - partialObject = IDLPartialDictionary( - *(partialConstructorArgs + [nonPartialObject]) - ) - elif isinstance( - nonPartialObject, (IDLInterface, IDLInterfaceMixin, IDLNamespace) - ): - partialObject = IDLPartialInterfaceOrNamespace( - *(partialConstructorArgs + [nonPartialObject]) - ) - else: - raise WebIDLError( - "Unknown partial object type %s" % type(partialObject), [location] - ) - - return partialObject - - def p_PartialInterfaceOrPartialMixin(self, p): - """ - PartialInterfaceOrPartialMixin : PartialInterfaceRest - | PartialMixinRest - """ - p[0] = p[1] - - def p_PartialInterfaceRest(self, p): - """ - PartialInterfaceRest : IDENTIFIER LBRACE PartialInterfaceMembers RBRACE SEMICOLON - """ - location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(location, p[1]) - members = p[3] - - p[0] = self.handlePartialObject( - location, - identifier, - IDLInterface, - [location, self.globalScope(), identifier, None], - [location, identifier, members], - ) - - def p_PartialMixinRest(self, p): - """ - PartialMixinRest : MIXIN IDENTIFIER LBRACE MixinMembers RBRACE SEMICOLON - """ - location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) - members = p[4] - - p[0] = self.handlePartialObject( - location, - identifier, - IDLInterfaceMixin, - [location, self.globalScope(), identifier], - [location, identifier, members], - ) - - def p_PartialNamespace(self, p): - """ - PartialNamespace : NAMESPACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON - """ - location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) - members = p[4] - - p[0] = self.handlePartialObject( - location, - identifier, - IDLNamespace, - [location, self.globalScope(), identifier], - [location, identifier, members], - ) - - def p_PartialDictionary(self, p): - """ - PartialDictionary : DICTIONARY IDENTIFIER LBRACE DictionaryMembers RBRACE SEMICOLON - """ - location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) - members = p[4] - - p[0] = self.handlePartialObject( - location, - identifier, - IDLDictionary, - [location, self.globalScope(), identifier], - [location, identifier, members], - ) - - def p_Inheritance(self, p): - """ - Inheritance : COLON ScopedName - """ - p[0] = IDLIdentifierPlaceholder(self.getLocation(p, 2), p[2]) - - def p_InheritanceEmpty(self, p): - """ - Inheritance : - """ - pass - - def p_InterfaceMembers(self, p): - """ - InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers - """ - p[0] = [p[2]] - - assert not p[1] or p[2] - p[2].addExtendedAttributes(p[1]) - - p[0].extend(p[3]) - - def p_InterfaceMembersEmpty(self, p): - """ - InterfaceMembers : - """ - p[0] = [] - - def p_InterfaceMember(self, p): - """ - InterfaceMember : PartialInterfaceMember - | Constructor - """ - p[0] = p[1] - - def p_Constructor(self, p): - """ - Constructor : CONSTRUCTOR LPAREN ArgumentList RPAREN SEMICOLON - """ - p[0] = IDLConstructor(self.getLocation(p, 1), p[3], "constructor") - - def p_PartialInterfaceMembers(self, p): - """ - PartialInterfaceMembers : ExtendedAttributeList PartialInterfaceMember PartialInterfaceMembers - """ - p[0] = [p[2]] - - assert not p[1] or p[2] - p[2].addExtendedAttributes(p[1]) - - p[0].extend(p[3]) - - def p_PartialInterfaceMembersEmpty(self, p): - """ - PartialInterfaceMembers : - """ - p[0] = [] - - def p_PartialInterfaceMember(self, p): - """ - PartialInterfaceMember : Const - | AttributeOrOperationOrMaplikeOrSetlikeOrIterable - """ - p[0] = p[1] - - def p_MixinMembersEmpty(self, p): - """ - MixinMembers : - """ - p[0] = [] - - def p_MixinMembers(self, p): - """ - MixinMembers : ExtendedAttributeList MixinMember MixinMembers - """ - p[0] = [p[2]] - - assert not p[1] or p[2] - p[2].addExtendedAttributes(p[1]) - - p[0].extend(p[3]) - - def p_MixinMember(self, p): - """ - MixinMember : Const - | Attribute - | Operation - """ - p[0] = p[1] - - def p_Dictionary(self, p): - """ - Dictionary : DICTIONARY IDENTIFIER Inheritance LBRACE DictionaryMembers RBRACE SEMICOLON - """ - location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) - members = p[5] - p[0] = IDLDictionary(location, self.globalScope(), identifier, p[3], members) - - def p_DictionaryMembers(self, p): - """ - DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers - | - """ - if len(p) == 1: - # We're at the end of the list - p[0] = [] - return - p[2].addExtendedAttributes(p[1]) - p[0] = [p[2]] - p[0].extend(p[3]) - - def p_DictionaryMemberRequired(self, p): - """ - DictionaryMember : REQUIRED TypeWithExtendedAttributes IDENTIFIER SEMICOLON - """ - # These quack a lot like required arguments, so just treat them that way. - t = p[2] - assert isinstance(t, IDLType) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3]) - - p[0] = IDLArgument( - self.getLocation(p, 3), - identifier, - t, - optional=False, - defaultValue=None, - variadic=False, - dictionaryMember=True, - ) - - def p_DictionaryMember(self, p): - """ - DictionaryMember : Type IDENTIFIER Default SEMICOLON - """ - # These quack a lot like optional arguments, so just treat them that way. - t = p[1] - assert isinstance(t, IDLType) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) - defaultValue = p[3] - - # Any attributes that precede this may apply to the type, so - # we configure the argument to forward type attributes down instead of producing - # a parse error - p[0] = IDLArgument( - self.getLocation(p, 2), - identifier, - t, - optional=True, - defaultValue=defaultValue, - variadic=False, - dictionaryMember=True, - allowTypeAttributes=True, - ) - - def p_Default(self, p): - """ - Default : EQUALS DefaultValue - | - """ - if len(p) > 1: - p[0] = p[2] - else: - p[0] = None - - def p_DefaultValue(self, p): - """ - DefaultValue : ConstValue - | LBRACKET RBRACKET - | LBRACE RBRACE - """ - if len(p) == 2: - p[0] = p[1] - else: - assert len(p) == 3 # Must be [] or {} - if p[1] == "[": - p[0] = IDLEmptySequenceValue(self.getLocation(p, 1)) - else: - assert p[1] == "{" - p[0] = IDLDefaultDictionaryValue(self.getLocation(p, 1)) - - def p_DefaultValueNull(self, p): - """ - DefaultValue : NULL - """ - p[0] = IDLNullValue(self.getLocation(p, 1)) - - def p_DefaultValueUndefined(self, p): - """ - DefaultValue : UNDEFINED - """ - p[0] = IDLUndefinedValue(self.getLocation(p, 1)) - - def p_Exception(self, p): - """ - Exception : EXCEPTION IDENTIFIER Inheritance LBRACE ExceptionMembers RBRACE SEMICOLON - """ - pass - - def p_Enum(self, p): - """ - Enum : ENUM IDENTIFIER LBRACE EnumValueList RBRACE SEMICOLON - """ - location = self.getLocation(p, 1) - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) - - values = p[4] - assert values - p[0] = IDLEnum(location, self.globalScope(), identifier, values) - - def p_EnumValueList(self, p): - """ - EnumValueList : STRING EnumValueListComma - """ - p[0] = [p[1]] - p[0].extend(p[2]) - - def p_EnumValueListComma(self, p): - """ - EnumValueListComma : COMMA EnumValueListString - """ - p[0] = p[2] - - def p_EnumValueListCommaEmpty(self, p): - """ - EnumValueListComma : - """ - p[0] = [] - - def p_EnumValueListString(self, p): - """ - EnumValueListString : STRING EnumValueListComma - """ - p[0] = [p[1]] - p[0].extend(p[2]) - - def p_EnumValueListStringEmpty(self, p): - """ - EnumValueListString : - """ - p[0] = [] - - def p_CallbackRest(self, p): - """ - CallbackRest : IDENTIFIER EQUALS Type LPAREN ArgumentList RPAREN SEMICOLON - """ - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) - p[0] = IDLCallback( - self.getLocation(p, 1), - self.globalScope(), - identifier, - p[3], - p[5], - isConstructor=False, - ) - - def p_CallbackConstructorRest(self, p): - """ - CallbackConstructorRest : CONSTRUCTOR IDENTIFIER EQUALS Type LPAREN ArgumentList RPAREN SEMICOLON - """ - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2]) - p[0] = IDLCallback( - self.getLocation(p, 2), - self.globalScope(), - identifier, - p[4], - p[6], - isConstructor=True, - ) - - def p_ExceptionMembers(self, p): - """ - ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers - | - """ - pass - - def p_Typedef(self, p): - """ - Typedef : TYPEDEF TypeWithExtendedAttributes IDENTIFIER SEMICOLON - """ - typedef = IDLTypedef(self.getLocation(p, 1), self.globalScope(), p[2], p[3]) - p[0] = typedef - - def p_IncludesStatement(self, p): - """ - IncludesStatement : ScopedName INCLUDES ScopedName SEMICOLON - """ - assert p[2] == "includes" - interface = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1]) - mixin = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3]) - p[0] = IDLIncludesStatement(self.getLocation(p, 1), interface, mixin) - - def p_Const(self, p): - """ - Const : CONST ConstType IDENTIFIER EQUALS ConstValue SEMICOLON - """ - location = self.getLocation(p, 1) - type = p[2] - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3]) - value = p[5] - p[0] = IDLConst(location, identifier, type, value) - - def p_ConstValueBoolean(self, p): - """ - ConstValue : BooleanLiteral - """ - location = self.getLocation(p, 1) - booleanType = BuiltinTypes[IDLBuiltinType.Types.boolean] - p[0] = IDLValue(location, booleanType, p[1]) - - def p_ConstValueInteger(self, p): - """ - ConstValue : INTEGER - """ - location = self.getLocation(p, 1) - - # We don't know ahead of time what type the integer literal is. - # Determine the smallest type it could possibly fit in and use that. - integerType = matchIntegerValueToType(p[1]) - if integerType is None: - raise WebIDLError("Integer literal out of range", [location]) - - p[0] = IDLValue(location, integerType, p[1]) - - def p_ConstValueFloat(self, p): - """ - ConstValue : FLOATLITERAL - """ - location = self.getLocation(p, 1) - p[0] = IDLValue( - location, BuiltinTypes[IDLBuiltinType.Types.unrestricted_float], p[1] - ) - - def p_ConstValueString(self, p): - """ - ConstValue : STRING - """ - location = self.getLocation(p, 1) - stringType = BuiltinTypes[IDLBuiltinType.Types.domstring] - p[0] = IDLValue(location, stringType, p[1]) - - def p_BooleanLiteralTrue(self, p): - """ - BooleanLiteral : TRUE - """ - p[0] = True - - def p_BooleanLiteralFalse(self, p): - """ - BooleanLiteral : FALSE - """ - p[0] = False - - def p_AttributeOrOperationOrMaplikeOrSetlikeOrIterable(self, p): - """ - AttributeOrOperationOrMaplikeOrSetlikeOrIterable : Attribute - | Maplike - | Setlike - | Iterable - | AsyncIterable - | Operation - """ - p[0] = p[1] - - def p_Iterable(self, p): - """ - Iterable : ITERABLE LT TypeWithExtendedAttributes GT SEMICOLON - | ITERABLE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON - """ - location = self.getLocation(p, 2) - identifier = IDLUnresolvedIdentifier( - location, "__iterable", allowDoubleUnderscore=True - ) - if len(p) > 6: - keyType = p[3] - valueType = p[5] - else: - keyType = None - valueType = p[3] - - p[0] = IDLIterable(location, identifier, keyType, valueType, self.globalScope()) - - def p_AsyncIterable(self, p): - """ - AsyncIterable : ASYNC ITERABLE LT TypeWithExtendedAttributes GT SEMICOLON - | ASYNC ITERABLE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON - | ASYNC ITERABLE LT TypeWithExtendedAttributes GT LPAREN ArgumentList RPAREN SEMICOLON - | ASYNC ITERABLE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT LPAREN ArgumentList RPAREN SEMICOLON - """ - location = self.getLocation(p, 2) - identifier = IDLUnresolvedIdentifier( - location, "__iterable", allowDoubleUnderscore=True - ) - if len(p) == 12: - keyType = p[4] - valueType = p[6] - argList = p[9] - elif len(p) == 10: - keyType = None - valueType = p[4] - argList = p[7] - elif len(p) == 9: - keyType = p[4] - valueType = p[6] - argList = [] - else: - keyType = None - valueType = p[4] - argList = [] - - p[0] = IDLAsyncIterable( - location, identifier, keyType, valueType, argList, self.globalScope() - ) - - def p_Setlike(self, p): - """ - Setlike : ReadOnly SETLIKE LT TypeWithExtendedAttributes GT SEMICOLON - """ - readonly = p[1] - maplikeOrSetlikeType = p[2] - location = self.getLocation(p, 2) - identifier = IDLUnresolvedIdentifier( - location, "__setlike", allowDoubleUnderscore=True - ) - keyType = p[4] - valueType = keyType - p[0] = IDLMaplikeOrSetlike( - location, identifier, maplikeOrSetlikeType, readonly, keyType, valueType - ) - - def p_Maplike(self, p): - """ - Maplike : ReadOnly MAPLIKE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON - """ - readonly = p[1] - maplikeOrSetlikeType = p[2] - location = self.getLocation(p, 2) - identifier = IDLUnresolvedIdentifier( - location, "__maplike", allowDoubleUnderscore=True - ) - keyType = p[4] - valueType = p[6] - p[0] = IDLMaplikeOrSetlike( - location, identifier, maplikeOrSetlikeType, readonly, keyType, valueType - ) - - def p_AttributeWithQualifier(self, p): - """ - Attribute : Qualifier AttributeRest - """ - static = IDLInterfaceMember.Special.Static in p[1] - stringifier = IDLInterfaceMember.Special.Stringifier in p[1] - (location, identifier, type, readonly) = p[2] - p[0] = IDLAttribute( - location, identifier, type, readonly, static=static, stringifier=stringifier - ) - - def p_AttributeInherited(self, p): - """ - Attribute : INHERIT AttributeRest - """ - (location, identifier, type, readonly) = p[2] - p[0] = IDLAttribute(location, identifier, type, readonly, inherit=True) - - def p_Attribute(self, p): - """ - Attribute : AttributeRest - """ - (location, identifier, type, readonly) = p[1] - p[0] = IDLAttribute(location, identifier, type, readonly, inherit=False) - - def p_AttributeRest(self, p): - """ - AttributeRest : ReadOnly ATTRIBUTE TypeWithExtendedAttributes AttributeName SEMICOLON - """ - location = self.getLocation(p, 2) - readonly = p[1] - t = p[3] - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 4), p[4]) - p[0] = (location, identifier, t, readonly) - - def p_ReadOnly(self, p): - """ - ReadOnly : READONLY - """ - p[0] = True - - def p_ReadOnlyEmpty(self, p): - """ - ReadOnly : - """ - p[0] = False - - def p_Operation(self, p): - """ - Operation : Qualifiers OperationRest - """ - qualifiers = p[1] - - # Disallow duplicates in the qualifier set - if not len(set(qualifiers)) == len(qualifiers): - raise WebIDLError( - "Duplicate qualifiers are not allowed", [self.getLocation(p, 1)] - ) - - static = IDLInterfaceMember.Special.Static in p[1] - # If static is there that's all that's allowed. This is disallowed - # by the parser, so we can assert here. - assert not static or len(qualifiers) == 1 - - stringifier = IDLInterfaceMember.Special.Stringifier in p[1] - # If stringifier is there that's all that's allowed. This is disallowed - # by the parser, so we can assert here. - assert not stringifier or len(qualifiers) == 1 - - getter = True if IDLMethod.Special.Getter in p[1] else False - setter = True if IDLMethod.Special.Setter in p[1] else False - deleter = True if IDLMethod.Special.Deleter in p[1] else False - legacycaller = True if IDLMethod.Special.LegacyCaller in p[1] else False - - if getter or deleter: - if setter: - raise WebIDLError( - "getter and deleter are incompatible with setter", - [self.getLocation(p, 1)], - ) - - (returnType, identifier, arguments) = p[2] - - assert isinstance(returnType, IDLType) - - specialType = IDLMethod.NamedOrIndexed.Neither - - if getter or deleter: - if len(arguments) != 1: - raise WebIDLError( - "%s has wrong number of arguments" - % ("getter" if getter else "deleter"), - [self.getLocation(p, 2)], - ) - argType = arguments[0].type - if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]: - specialType = IDLMethod.NamedOrIndexed.Named - elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]: - specialType = IDLMethod.NamedOrIndexed.Indexed - if deleter: - raise WebIDLError( - "There is no such thing as an indexed deleter.", - [self.getLocation(p, 1)], - ) - else: - raise WebIDLError( - "%s has wrong argument type (must be DOMString or UnsignedLong)" - % ("getter" if getter else "deleter"), - [arguments[0].location], - ) - if arguments[0].optional or arguments[0].variadic: - raise WebIDLError( - "%s cannot have %s argument" - % ( - "getter" if getter else "deleter", - "optional" if arguments[0].optional else "variadic", - ), - [arguments[0].location], - ) - if getter: - if returnType.isUndefined(): - raise WebIDLError( - "getter cannot have undefined return type", [self.getLocation(p, 2)] - ) - if setter: - if len(arguments) != 2: - raise WebIDLError( - "setter has wrong number of arguments", [self.getLocation(p, 2)] - ) - argType = arguments[0].type - if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]: - specialType = IDLMethod.NamedOrIndexed.Named - elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]: - specialType = IDLMethod.NamedOrIndexed.Indexed - else: - raise WebIDLError( - "settter has wrong argument type (must be DOMString or UnsignedLong)", - [arguments[0].location], - ) - if arguments[0].optional or arguments[0].variadic: - raise WebIDLError( - "setter cannot have %s argument" - % ("optional" if arguments[0].optional else "variadic"), - [arguments[0].location], - ) - if arguments[1].optional or arguments[1].variadic: - raise WebIDLError( - "setter cannot have %s argument" - % ("optional" if arguments[1].optional else "variadic"), - [arguments[1].location], - ) - - if stringifier: - if len(arguments) != 0: - raise WebIDLError( - "stringifier has wrong number of arguments", - [self.getLocation(p, 2)], - ) - if not returnType.isDOMString(): - raise WebIDLError( - "stringifier must have DOMString return type", - [self.getLocation(p, 2)], - ) - - # identifier might be None. This is only permitted for special methods. - if not identifier: - if ( - not getter - and not setter - and not deleter - and not legacycaller - and not stringifier - ): - raise WebIDLError( - "Identifier required for non-special methods", - [self.getLocation(p, 2)], - ) - - location = BuiltinLocation("<auto-generated-identifier>") - identifier = IDLUnresolvedIdentifier( - location, - "__%s%s%s%s%s%s" - % ( - "named" - if specialType == IDLMethod.NamedOrIndexed.Named - else "indexed" - if specialType == IDLMethod.NamedOrIndexed.Indexed - else "", - "getter" if getter else "", - "setter" if setter else "", - "deleter" if deleter else "", - "legacycaller" if legacycaller else "", - "stringifier" if stringifier else "", - ), - allowDoubleUnderscore=True, - ) - - method = IDLMethod( - self.getLocation(p, 2), - identifier, - returnType, - arguments, - static=static, - getter=getter, - setter=setter, - deleter=deleter, - specialType=specialType, - legacycaller=legacycaller, - stringifier=stringifier, - ) - p[0] = method - - def p_Stringifier(self, p): - """ - Operation : STRINGIFIER SEMICOLON - """ - identifier = IDLUnresolvedIdentifier( - BuiltinLocation("<auto-generated-identifier>"), - "__stringifier", - allowDoubleUnderscore=True, - ) - method = IDLMethod( - self.getLocation(p, 1), - identifier, - returnType=BuiltinTypes[IDLBuiltinType.Types.domstring], - arguments=[], - stringifier=True, - ) - p[0] = method - - def p_QualifierStatic(self, p): - """ - Qualifier : STATIC - """ - p[0] = [IDLInterfaceMember.Special.Static] - - def p_QualifierStringifier(self, p): - """ - Qualifier : STRINGIFIER - """ - p[0] = [IDLInterfaceMember.Special.Stringifier] - - def p_Qualifiers(self, p): - """ - Qualifiers : Qualifier - | Specials - """ - p[0] = p[1] - - def p_Specials(self, p): - """ - Specials : Special Specials - """ - p[0] = [p[1]] - p[0].extend(p[2]) - - def p_SpecialsEmpty(self, p): - """ - Specials : - """ - p[0] = [] - - def p_SpecialGetter(self, p): - """ - Special : GETTER - """ - p[0] = IDLMethod.Special.Getter - - def p_SpecialSetter(self, p): - """ - Special : SETTER - """ - p[0] = IDLMethod.Special.Setter - - def p_SpecialDeleter(self, p): - """ - Special : DELETER - """ - p[0] = IDLMethod.Special.Deleter - - def p_SpecialLegacyCaller(self, p): - """ - Special : LEGACYCALLER - """ - p[0] = IDLMethod.Special.LegacyCaller - - def p_OperationRest(self, p): - """ - OperationRest : Type OptionalIdentifier LPAREN ArgumentList RPAREN SEMICOLON - """ - p[0] = (p[1], p[2], p[4]) - - def p_OptionalIdentifier(self, p): - """ - OptionalIdentifier : IDENTIFIER - """ - p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) - - def p_OptionalIdentifierEmpty(self, p): - """ - OptionalIdentifier : - """ - pass - - def p_ArgumentList(self, p): - """ - ArgumentList : Argument Arguments - """ - p[0] = [p[1]] if p[1] else [] - p[0].extend(p[2]) - - def p_ArgumentListEmpty(self, p): - """ - ArgumentList : - """ - p[0] = [] - - def p_Arguments(self, p): - """ - Arguments : COMMA Argument Arguments - """ - p[0] = [p[2]] if p[2] else [] - p[0].extend(p[3]) - - def p_ArgumentsEmpty(self, p): - """ - Arguments : - """ - p[0] = [] - - def p_Argument(self, p): - """ - Argument : ExtendedAttributeList ArgumentRest - """ - p[0] = p[2] - p[0].addExtendedAttributes(p[1]) - - def p_ArgumentRestOptional(self, p): - """ - ArgumentRest : OPTIONAL TypeWithExtendedAttributes ArgumentName Default - """ - t = p[2] - assert isinstance(t, IDLType) - # Arg names can be reserved identifiers - identifier = IDLUnresolvedIdentifier( - self.getLocation(p, 3), p[3], allowForbidden=True - ) - - defaultValue = p[4] - - # We can't test t.isAny() here and give it a default value as needed, - # since at this point t is not a fully resolved type yet (e.g. it might - # be a typedef). We'll handle the 'any' case in IDLArgument.complete. - - p[0] = IDLArgument( - self.getLocation(p, 3), identifier, t, True, defaultValue, False - ) - - def p_ArgumentRest(self, p): - """ - ArgumentRest : Type Ellipsis ArgumentName - """ - t = p[1] - assert isinstance(t, IDLType) - # Arg names can be reserved identifiers - identifier = IDLUnresolvedIdentifier( - self.getLocation(p, 3), p[3], allowForbidden=True - ) - - variadic = p[2] - - # We can't test t.isAny() here and give it a default value as needed, - # since at this point t is not a fully resolved type yet (e.g. it might - # be a typedef). We'll handle the 'any' case in IDLArgument.complete. - - # variadic implies optional - # Any attributes that precede this may apply to the type, so - # we configure the argument to forward type attributes down instead of producing - # a parse error - p[0] = IDLArgument( - self.getLocation(p, 3), - identifier, - t, - variadic, - None, - variadic, - allowTypeAttributes=True, - ) - - def p_ArgumentName(self, p): - """ - ArgumentName : IDENTIFIER - | ArgumentNameKeyword - """ - p[0] = p[1] - - def p_ArgumentNameKeyword(self, p): - """ - ArgumentNameKeyword : ASYNC - | ATTRIBUTE - | CALLBACK - | CONST - | CONSTRUCTOR - | DELETER - | DICTIONARY - | ENUM - | EXCEPTION - | GETTER - | INCLUDES - | INHERIT - | INTERFACE - | ITERABLE - | LEGACYCALLER - | MAPLIKE - | MIXIN - | NAMESPACE - | PARTIAL - | READONLY - | REQUIRED - | SERIALIZER - | SETLIKE - | SETTER - | STATIC - | STRINGIFIER - | TYPEDEF - | UNRESTRICTED - """ - p[0] = p[1] - - def p_AttributeName(self, p): - """ - AttributeName : IDENTIFIER - | AttributeNameKeyword - """ - p[0] = p[1] - - def p_AttributeNameKeyword(self, p): - """ - AttributeNameKeyword : ASYNC - | REQUIRED - """ - p[0] = p[1] - - def p_Ellipsis(self, p): - """ - Ellipsis : ELLIPSIS - """ - p[0] = True - - def p_EllipsisEmpty(self, p): - """ - Ellipsis : - """ - p[0] = False - - def p_ExceptionMember(self, p): - """ - ExceptionMember : Const - | ExceptionField - """ - pass - - def p_ExceptionField(self, p): - """ - ExceptionField : Type IDENTIFIER SEMICOLON - """ - pass - - def p_ExtendedAttributeList(self, p): - """ - ExtendedAttributeList : LBRACKET ExtendedAttribute ExtendedAttributes RBRACKET - """ - p[0] = [p[2]] - if p[3]: - p[0].extend(p[3]) - - def p_ExtendedAttributeListEmpty(self, p): - """ - ExtendedAttributeList : - """ - p[0] = [] - - def p_ExtendedAttribute(self, p): - """ - ExtendedAttribute : ExtendedAttributeNoArgs - | ExtendedAttributeArgList - | ExtendedAttributeIdent - | ExtendedAttributeWildcard - | ExtendedAttributeNamedArgList - | ExtendedAttributeIdentList - """ - p[0] = IDLExtendedAttribute(self.getLocation(p, 1), p[1]) - - def p_ExtendedAttributeEmpty(self, p): - """ - ExtendedAttribute : - """ - pass - - def p_ExtendedAttributes(self, p): - """ - ExtendedAttributes : COMMA ExtendedAttribute ExtendedAttributes - """ - p[0] = [p[2]] if p[2] else [] - p[0].extend(p[3]) - - def p_ExtendedAttributesEmpty(self, p): - """ - ExtendedAttributes : - """ - p[0] = [] - - def p_Other(self, p): - """ - Other : INTEGER - | FLOATLITERAL - | IDENTIFIER - | STRING - | OTHER - | ELLIPSIS - | COLON - | SCOPE - | SEMICOLON - | LT - | EQUALS - | GT - | QUESTIONMARK - | ASTERISK - | DOMSTRING - | BYTESTRING - | USVSTRING - | UTF8STRING - | JSSTRING - | PROMISE - | ANY - | BOOLEAN - | BYTE - | DOUBLE - | FALSE - | FLOAT - | LONG - | NULL - | OBJECT - | OCTET - | OR - | OPTIONAL - | RECORD - | SEQUENCE - | SHORT - | SYMBOL - | TRUE - | UNSIGNED - | UNDEFINED - | ArgumentNameKeyword - """ - pass - - def p_OtherOrComma(self, p): - """ - OtherOrComma : Other - | COMMA - """ - pass - - def p_TypeSingleType(self, p): - """ - Type : SingleType - """ - p[0] = p[1] - - def p_TypeUnionType(self, p): - """ - Type : UnionType Null - """ - p[0] = self.handleNullable(p[1], p[2]) - - def p_TypeWithExtendedAttributes(self, p): - """ - TypeWithExtendedAttributes : ExtendedAttributeList Type - """ - p[0] = p[2].withExtendedAttributes(p[1]) - - def p_SingleTypeDistinguishableType(self, p): - """ - SingleType : DistinguishableType - """ - p[0] = p[1] - - def p_SingleTypeAnyType(self, p): - """ - SingleType : ANY - """ - p[0] = BuiltinTypes[IDLBuiltinType.Types.any] - - def p_SingleTypePromiseType(self, p): - """ - SingleType : PROMISE LT Type GT - """ - p[0] = IDLPromiseType(self.getLocation(p, 1), p[3]) - - def p_UnionType(self, p): - """ - UnionType : LPAREN UnionMemberType OR UnionMemberType UnionMemberTypes RPAREN - """ - types = [p[2], p[4]] - types.extend(p[5]) - p[0] = IDLUnionType(self.getLocation(p, 1), types) - - def p_UnionMemberTypeDistinguishableType(self, p): - """ - UnionMemberType : ExtendedAttributeList DistinguishableType - """ - p[0] = p[2].withExtendedAttributes(p[1]) - - def p_UnionMemberType(self, p): - """ - UnionMemberType : UnionType Null - """ - p[0] = self.handleNullable(p[1], p[2]) - - def p_UnionMemberTypes(self, p): - """ - UnionMemberTypes : OR UnionMemberType UnionMemberTypes - """ - p[0] = [p[2]] - p[0].extend(p[3]) - - def p_UnionMemberTypesEmpty(self, p): - """ - UnionMemberTypes : - """ - p[0] = [] - - def p_DistinguishableType(self, p): - """ - DistinguishableType : PrimitiveType Null - | ARRAYBUFFER Null - | READABLESTREAM Null - | OBJECT Null - | UNDEFINED Null - """ - if p[1] == "object": - type = BuiltinTypes[IDLBuiltinType.Types.object] - elif p[1] == "ArrayBuffer": - type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer] - elif p[1] == "ReadableStream": - type = BuiltinTypes[IDLBuiltinType.Types.ReadableStream] - elif p[1] == "undefined": - type = BuiltinTypes[IDLBuiltinType.Types.undefined] - else: - type = BuiltinTypes[p[1]] - - p[0] = self.handleNullable(type, p[2]) - - def p_DistinguishableTypeStringType(self, p): - """ - DistinguishableType : StringType Null - """ - p[0] = self.handleNullable(p[1], p[2]) - - def p_DistinguishableTypeSequenceType(self, p): - """ - DistinguishableType : SEQUENCE LT TypeWithExtendedAttributes GT Null - """ - innerType = p[3] - type = IDLSequenceType(self.getLocation(p, 1), innerType) - p[0] = self.handleNullable(type, p[5]) - - def p_DistinguishableTypeRecordType(self, p): - """ - DistinguishableType : RECORD LT StringType COMMA TypeWithExtendedAttributes GT Null - """ - keyType = p[3] - valueType = p[5] - type = IDLRecordType(self.getLocation(p, 1), keyType, valueType) - p[0] = self.handleNullable(type, p[7]) - - def p_DistinguishableTypeObservableArrayType(self, p): - """ - DistinguishableType : OBSERVABLEARRAY LT TypeWithExtendedAttributes GT Null - """ - innerType = p[3] - type = IDLObservableArrayType(self.getLocation(p, 1), innerType) - p[0] = self.handleNullable(type, p[5]) - - def p_DistinguishableTypeScopedName(self, p): - """ - DistinguishableType : ScopedName Null - """ - assert isinstance(p[1], IDLUnresolvedIdentifier) - - if p[1].name == "Promise": - raise WebIDLError( - "Promise used without saying what it's " "parametrized over", - [self.getLocation(p, 1)], - ) - - type = None - - try: - if self.globalScope()._lookupIdentifier(p[1]): - obj = self.globalScope()._lookupIdentifier(p[1]) - assert not obj.isType() - if obj.isTypedef(): - type = IDLTypedefType( - self.getLocation(p, 1), obj.innerType, obj.identifier.name - ) - elif obj.isCallback() and not obj.isInterface(): - type = IDLCallbackType(obj.location, obj) - else: - type = IDLWrapperType(self.getLocation(p, 1), p[1]) - p[0] = self.handleNullable(type, p[2]) - return - except: - pass - - type = IDLUnresolvedType(self.getLocation(p, 1), p[1]) - p[0] = self.handleNullable(type, p[2]) - - def p_ConstType(self, p): - """ - ConstType : PrimitiveType - """ - p[0] = BuiltinTypes[p[1]] - - def p_ConstTypeIdentifier(self, p): - """ - ConstType : IDENTIFIER - """ - identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) - - p[0] = IDLUnresolvedType(self.getLocation(p, 1), identifier) - - def p_PrimitiveTypeUint(self, p): - """ - PrimitiveType : UnsignedIntegerType - """ - p[0] = p[1] - - def p_PrimitiveTypeBoolean(self, p): - """ - PrimitiveType : BOOLEAN - """ - p[0] = IDLBuiltinType.Types.boolean - - def p_PrimitiveTypeByte(self, p): - """ - PrimitiveType : BYTE - """ - p[0] = IDLBuiltinType.Types.byte - - def p_PrimitiveTypeOctet(self, p): - """ - PrimitiveType : OCTET - """ - p[0] = IDLBuiltinType.Types.octet - - def p_PrimitiveTypeFloat(self, p): - """ - PrimitiveType : FLOAT - """ - p[0] = IDLBuiltinType.Types.float - - def p_PrimitiveTypeUnrestictedFloat(self, p): - """ - PrimitiveType : UNRESTRICTED FLOAT - """ - p[0] = IDLBuiltinType.Types.unrestricted_float - - def p_PrimitiveTypeDouble(self, p): - """ - PrimitiveType : DOUBLE - """ - p[0] = IDLBuiltinType.Types.double - - def p_PrimitiveTypeUnrestictedDouble(self, p): - """ - PrimitiveType : UNRESTRICTED DOUBLE - """ - p[0] = IDLBuiltinType.Types.unrestricted_double - - def p_StringType(self, p): - """ - StringType : BuiltinStringType - """ - p[0] = BuiltinTypes[p[1]] - - def p_BuiltinStringTypeDOMString(self, p): - """ - BuiltinStringType : DOMSTRING - """ - p[0] = IDLBuiltinType.Types.domstring - - def p_BuiltinStringTypeBytestring(self, p): - """ - BuiltinStringType : BYTESTRING - """ - p[0] = IDLBuiltinType.Types.bytestring - - def p_BuiltinStringTypeUSVString(self, p): - """ - BuiltinStringType : USVSTRING - """ - p[0] = IDLBuiltinType.Types.usvstring - - def p_BuiltinStringTypeUTF8String(self, p): - """ - BuiltinStringType : UTF8STRING - """ - p[0] = IDLBuiltinType.Types.utf8string - - def p_BuiltinStringTypeJSString(self, p): - """ - BuiltinStringType : JSSTRING - """ - p[0] = IDLBuiltinType.Types.jsstring - - def p_UnsignedIntegerTypeUnsigned(self, p): - """ - UnsignedIntegerType : UNSIGNED IntegerType - """ - # Adding one to a given signed integer type gets you the unsigned type: - p[0] = p[2] + 1 - - def p_UnsignedIntegerType(self, p): - """ - UnsignedIntegerType : IntegerType - """ - p[0] = p[1] - - def p_IntegerTypeShort(self, p): - """ - IntegerType : SHORT - """ - p[0] = IDLBuiltinType.Types.short - - def p_IntegerTypeLong(self, p): - """ - IntegerType : LONG OptionalLong - """ - if p[2]: - p[0] = IDLBuiltinType.Types.long_long - else: - p[0] = IDLBuiltinType.Types.long - - def p_OptionalLong(self, p): - """ - OptionalLong : LONG - """ - p[0] = True - - def p_OptionalLongEmpty(self, p): - """ - OptionalLong : - """ - p[0] = False - - def p_Null(self, p): - """ - Null : QUESTIONMARK - | - """ - if len(p) > 1: - p[0] = self.getLocation(p, 1) - else: - p[0] = None - - def p_ScopedName(self, p): - """ - ScopedName : AbsoluteScopedName - | RelativeScopedName - """ - p[0] = p[1] - - def p_AbsoluteScopedName(self, p): - """ - AbsoluteScopedName : SCOPE IDENTIFIER ScopedNameParts - """ - assert False - pass - - def p_RelativeScopedName(self, p): - """ - RelativeScopedName : IDENTIFIER ScopedNameParts - """ - assert not p[2] # Not implemented! - - p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1]) - - def p_ScopedNameParts(self, p): - """ - ScopedNameParts : SCOPE IDENTIFIER ScopedNameParts - """ - assert False - pass - - def p_ScopedNamePartsEmpty(self, p): - """ - ScopedNameParts : - """ - p[0] = None - - def p_ExtendedAttributeNoArgs(self, p): - """ - ExtendedAttributeNoArgs : IDENTIFIER - """ - p[0] = (p[1],) - - def p_ExtendedAttributeArgList(self, p): - """ - ExtendedAttributeArgList : IDENTIFIER LPAREN ArgumentList RPAREN - """ - p[0] = (p[1], p[3]) - - def p_ExtendedAttributeIdent(self, p): - """ - ExtendedAttributeIdent : IDENTIFIER EQUALS STRING - | IDENTIFIER EQUALS IDENTIFIER - """ - p[0] = (p[1], p[3]) - - def p_ExtendedAttributeWildcard(self, p): - """ - ExtendedAttributeWildcard : IDENTIFIER EQUALS ASTERISK - """ - p[0] = (p[1], p[3]) - - def p_ExtendedAttributeNamedArgList(self, p): - """ - ExtendedAttributeNamedArgList : IDENTIFIER EQUALS IDENTIFIER LPAREN ArgumentList RPAREN - """ - p[0] = (p[1], p[3], p[5]) - - def p_ExtendedAttributeIdentList(self, p): - """ - ExtendedAttributeIdentList : IDENTIFIER EQUALS LPAREN IdentifierList RPAREN - """ - p[0] = (p[1], p[4]) - - def p_IdentifierList(self, p): - """ - IdentifierList : IDENTIFIER Identifiers - """ - idents = list(p[2]) - # This is only used for identifier-list-valued extended attributes, and if - # we're going to restrict to IDENTIFIER here we should at least allow - # escaping with leading '_' as usual for identifiers. - ident = p[1] - if ident[0] == "_": - ident = ident[1:] - idents.insert(0, ident) - p[0] = idents - - def p_IdentifiersList(self, p): - """ - Identifiers : COMMA IDENTIFIER Identifiers - """ - idents = list(p[3]) - # This is only used for identifier-list-valued extended attributes, and if - # we're going to restrict to IDENTIFIER here we should at least allow - # escaping with leading '_' as usual for identifiers. - ident = p[2] - if ident[0] == "_": - ident = ident[1:] - idents.insert(0, ident) - p[0] = idents - - def p_IdentifiersEmpty(self, p): - """ - Identifiers : - """ - p[0] = [] - - def p_error(self, p): - if not p: - raise WebIDLError( - "Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both", - [self._filename], - ) - else: - raise WebIDLError( - "invalid syntax", - [Location(self.lexer, p.lineno, p.lexpos, self._filename)], - ) - - def __init__(self, outputdir="", lexer=None, use_builtin_readable_stream=True): - Tokenizer.__init__(self, outputdir, lexer, use_builtin_readable_stream) - - logger = SqueakyCleanLogger() - try: - self.parser = yacc.yacc( - module=self, - outputdir=outputdir, - errorlog=logger, - debug=False, - write_tables=False, - # Pickling the grammar is a speedup in - # some cases (older Python?) but a - # significant slowdown in others. - # We're not pickling for now, until it - # becomes a speedup again. - # , picklefile='WebIDLGrammar.pkl' - ) - finally: - logger.reportGrammarErrors() - - self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None) - self._installBuiltins(self._globalScope) - self._productions = [] - - self._filename = "<builtin>" - self.lexer.input(Parser._builtins) - self._filename = None - - self.parser.parse(lexer=self.lexer, tracking=True) - - def _installBuiltins(self, scope): - assert isinstance(scope, IDLScope) - - # range omits the last value. - for x in range( - IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1 - ): - builtin = BuiltinTypes[x] - name = builtin.name - typedef = IDLTypedef( - BuiltinLocation("<builtin type>"), scope, builtin, name - ) - - @staticmethod - def handleNullable(type, questionMarkLocation): - if questionMarkLocation is not None: - type = IDLNullableType(questionMarkLocation, type) - - return type - - def parse(self, t, filename=None): - self.lexer.input(t) - - # for tok in iter(self.lexer.token, None): - # print tok - - self._filename = filename - self._productions.extend(self.parser.parse(lexer=self.lexer, tracking=True)) - self._filename = None - - def finish(self): - # If we have interfaces that are iterable, create their - # iterator interfaces and add them to the productions array. - interfaceStatements = [] - for p in self._productions: - if isinstance(p, IDLInterface): - interfaceStatements.append(p) - - for iface in interfaceStatements: - iterable = None - # We haven't run finish() on the interface yet, so we don't know - # whether our interface is maplike/setlike/iterable or not. This - # means we have to loop through the members to see if we have an - # iterable member. - for m in iface.members: - if isinstance(m, (IDLIterable, IDLAsyncIterable)): - iterable = m - break - if iterable and (iterable.isPairIterator() or iterable.isAsyncIterable()): - - def simpleExtendedAttr(str): - return IDLExtendedAttribute(iface.location, (str,)) - - if isinstance(iterable, IDLAsyncIterable): - nextReturnType = IDLPromiseType( - iterable.location, BuiltinTypes[IDLBuiltinType.Types.any] - ) - else: - nextReturnType = BuiltinTypes[IDLBuiltinType.Types.object] - nextMethod = IDLMethod( - iterable.location, - IDLUnresolvedIdentifier(iterable.location, "next"), - nextReturnType, - [], - ) - nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")]) - - methods = [nextMethod] - - if iterable.getExtendedAttribute("GenerateReturnMethod"): - assert isinstance(iterable, IDLAsyncIterable) - - returnMethod = IDLMethod( - iterable.location, - IDLUnresolvedIdentifier(iterable.location, "return"), - IDLPromiseType( - iterable.location, BuiltinTypes[IDLBuiltinType.Types.any] - ), - [ - IDLArgument( - iterable.location, - IDLUnresolvedIdentifier( - BuiltinLocation("<auto-generated-identifier>"), - "value", - ), - BuiltinTypes[IDLBuiltinType.Types.any], - optional=True, - ), - ], - ) - returnMethod.addExtendedAttributes([simpleExtendedAttr("Throws")]) - methods.append(returnMethod) - - if iterable.isIterable(): - itr_suffix = "Iterator" - else: - itr_suffix = "AsyncIterator" - itr_ident = IDLUnresolvedIdentifier( - iface.location, iface.identifier.name + itr_suffix - ) - if iterable.isIterable(): - classNameOverride = iface.identifier.name + " Iterator" - elif iterable.isAsyncIterable(): - classNameOverride = iface.identifier.name + " AsyncIterator" - itr_iface = IDLInterface( - iface.location, - self.globalScope(), - itr_ident, - None, - methods, - isKnownNonPartial=True, - classNameOverride=classNameOverride, - ) - itr_iface.addExtendedAttributes( - [simpleExtendedAttr("LegacyNoInterfaceObject")] - ) - # Make sure the exposure set for the iterator interface is the - # same as the exposure set for the iterable interface, because - # we're going to generate methods on the iterable that return - # instances of the iterator. - itr_iface._exposureGlobalNames = set(iface._exposureGlobalNames) - # Always append generated iterable interfaces after the - # interface they're a member of, otherwise nativeType generation - # won't work correctly. - if iterable.isIterable(): - itr_iface.iterableInterface = iface - else: - itr_iface.asyncIterableInterface = iface - self._productions.append(itr_iface) - iterable.iteratorType = IDLWrapperType(iface.location, itr_iface) - - # Make sure we finish IDLIncludesStatements before we finish the - # IDLInterfaces. - # XXX khuey hates this bit and wants to nuke it from orbit. - includesStatements = [ - p for p in self._productions if isinstance(p, IDLIncludesStatement) - ] - otherStatements = [ - p for p in self._productions if not isinstance(p, IDLIncludesStatement) - ] - for production in includesStatements: - production.finish(self.globalScope()) - for production in otherStatements: - production.finish(self.globalScope()) - - # Do any post-finish validation we need to do - for production in self._productions: - production.validate() - - # De-duplicate self._productions, without modifying its order. - seen = set() - result = [] - for p in self._productions: - if p not in seen: - seen.add(p) - result.append(p) - return result - - def reset(self): - return Parser(lexer=self.lexer) - - # Builtin IDL defined by WebIDL - _builtins = """ - typedef (ArrayBufferView or ArrayBuffer) BufferSource; - """ - - -def main(): - # Parse arguments. - from optparse import OptionParser - - usageString = "usage: %prog [options] files" - o = OptionParser(usage=usageString) - o.add_option( - "--cachedir", - dest="cachedir", - default=None, - help="Directory in which to cache lex/parse tables.", - ) - o.add_option( - "--verbose-errors", - action="store_true", - default=False, - help="When an error happens, display the Python traceback.", - ) - (options, args) = o.parse_args() - - if len(args) < 1: - o.error(usageString) - - fileList = args - baseDir = os.getcwd() - - # Parse the WebIDL. - parser = Parser(options.cachedir) - try: - for filename in fileList: - fullPath = os.path.normpath(os.path.join(baseDir, filename)) - f = open(fullPath, "rb") - lines = f.readlines() - f.close() - print(fullPath) - parser.parse("".join(lines), fullPath) - parser.finish() - except WebIDLError as e: - if options.verbose_errors: - traceback.print_exc() - else: - print(e) - - -if __name__ == "__main__": - main() |