aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/bindings/codegen/parser/WebIDL.py
diff options
context:
space:
mode:
authorMartin Robinson <mrobinson@igalia.com>2023-06-24 13:38:11 +0200
committerMartin Robinson <mrobinson@igalia.com>2023-06-30 09:51:31 +0200
commit8be014ee46077e78db21c5d73058c35a4ee65fa9 (patch)
tree9bfd0bc11997381d792fd3015add8be9cd7abd70 /components/script/dom/bindings/codegen/parser/WebIDL.py
parent7412e28349237055652a08a2216043d0993a3cea (diff)
downloadservo-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.py9107
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()