aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/bindings/codegen/parser/WebIDL.py
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/bindings/codegen/parser/WebIDL.py')
-rw-r--r--components/script/dom/bindings/codegen/parser/WebIDL.py3021
1 files changed, 1935 insertions, 1086 deletions
diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py
index dc9fa036141..d74278c3e0c 100644
--- a/components/script/dom/bindings/codegen/parser/WebIDL.py
+++ b/components/script/dom/bindings/codegen/parser/WebIDL.py
@@ -4,13 +4,15 @@
""" A WebIDL parser. """
+
from ply import lex, yacc
import re
import os
import traceback
import math
import string
-from collections import defaultdict
+from collections import defaultdict, OrderedDict
+from itertools import chain
# Machinery
@@ -40,32 +42,22 @@ def parseInt(literal):
return value * sign
-# Magic for creating enums
-def M_add_class_attribs(attribs, start):
- def foo(name, bases, dict_):
- for v, k in enumerate(attribs):
- dict_[k] = start + v
- assert 'length' not in dict_
- dict_['length'] = start + len(attribs)
- return type(name, bases, dict_)
- return foo
-
-
def enum(*names, **kw):
- if len(kw) == 1:
- base = kw['base'].__class__
- start = base.length
- else:
- assert len(kw) == 0
- base = object
- start = 0
-
- class Foo(base):
- __metaclass__ = M_add_class_attribs(names, start)
-
+ 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
- return Foo()
+
+ if "base" not in kw:
+ return Foo(names)
+ return Foo(chain(list(kw["base"].attrs.keys()), names))
class WebIDLError(Exception):
@@ -132,6 +124,9 @@ class BuiltinLocation(object):
return (isinstance(other, BuiltinLocation) and
self.msg == other.msg)
+ def __hash__(self):
+ return hash(self.msg)
+
def filename(self):
return '<builtin>'
@@ -162,6 +157,9 @@ class IDLObject(object):
def isNamespace(self):
return False
+ def isInterfaceMixin(self):
+ return False
+
def isEnum(self):
return False
@@ -241,15 +239,19 @@ class IDLScope(IDLObject):
# A mapping from global name to the set of global interfaces
# that have that global name.
self.globalNameMapping = defaultdict(set)
- self.primaryGlobalAttr = None
- self.primaryGlobalName = None
def __str__(self):
return self.QName()
def QName(self):
- if self._name:
- return self._name.QName() + "::"
+ # 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):
@@ -305,8 +307,8 @@ class IDLScope(IDLObject):
# because we need to merge overloads of NamedConstructors and we need to
# detect conflicts in those across interfaces. See also the comment in
# IDLInterface.addExtendedAttributes for "NamedConstructor".
- if (originalObject.tag == IDLInterfaceMember.Tags.Method and
- newObject.tag == IDLInterfaceMember.Tags.Method):
+ if (isinstance(originalObject, IDLMethod) and
+ isinstance(newObject, IDLMethod)):
return originalObject.addOverload(newObject)
# Default to throwing, derived classes can override.
@@ -316,7 +318,7 @@ class IDLScope(IDLObject):
newObject.location)
raise WebIDLError(
- "Multiple unresolvable definitions of identifier '%s' in scope '%s%s"
+ "Multiple unresolvable definitions of identifier '%s' in scope '%s'%s"
% (identifier.name, str(self), conflictdesc), [])
def _lookupIdentifier(self, identifier):
@@ -327,6 +329,13 @@ class IDLScope(IDLObject):
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):
@@ -367,8 +376,6 @@ class IDLUnresolvedIdentifier(IDLObject):
[location])
if name[0] == '_' and not allowDoubleUnderscore:
name = name[1:]
- # TODO: Bug 872377, Restore "toJSON" to below list.
- # We sometimes need custom serialization, so allow toJSON for now.
if (name in ["constructor", "toString"] and
not allowForbidden):
raise WebIDLError("Cannot use reserved identifier '%s'" % (name),
@@ -409,48 +416,11 @@ class IDLObjectWithIdentifier(IDLObject):
if parentScope:
self.resolve(parentScope)
- self.treatNullAs = "Default"
-
def resolve(self, parentScope):
assert isinstance(parentScope, IDLScope)
assert isinstance(self.identifier, IDLUnresolvedIdentifier)
self.identifier.resolve(parentScope, self)
- def checkForStringHandlingExtendedAttributes(self, attrs,
- isDictionaryMember=False,
- isOptional=False):
- """
- A helper function to deal with TreatNullAs. Returns the list
- of attrs it didn't handle itself.
- """
- assert isinstance(self, IDLArgument) or isinstance(self, IDLAttribute)
- unhandledAttrs = list()
- for attr in attrs:
- if not attr.hasValue():
- unhandledAttrs.append(attr)
- continue
-
- identifier = attr.identifier()
- value = attr.value()
- if identifier == "TreatNullAs":
- if not self.type.isDOMString() or self.type.nullable():
- raise WebIDLError("[TreatNullAs] is only allowed on "
- "arguments or attributes whose type is "
- "DOMString",
- [self.location])
- if isDictionaryMember:
- raise WebIDLError("[TreatNullAs] is not allowed for "
- "dictionary members", [self.location])
- if value != 'EmptyString':
- raise WebIDLError("[TreatNullAs] must take the identifier "
- "'EmptyString', not '%s'" % value,
- [self.location])
- self.treatNullAs = value
- else:
- unhandledAttrs.append(attr)
-
- return unhandledAttrs
-
class IDLObjectWithScope(IDLObjectWithIdentifier, IDLScope):
def __init__(self, location, parentScope, identifier):
@@ -496,8 +466,17 @@ class IDLExposureMixins():
raise WebIDLError("Unknown [Exposed] value %s" % globalName,
[self._location])
- if len(self._exposureGlobalNames) == 0:
- self._exposureGlobalNames.add(scope.primaryGlobalName)
+ # 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):
+ 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)
@@ -505,18 +484,14 @@ class IDLExposureMixins():
def isExposedInWindow(self):
return 'Window' in self.exposureSet
- def isExposedOnMainThread(self):
- return (self.isExposedInWindow() or
- self.isExposedInSystemGlobals())
-
def isExposedInAnyWorker(self):
return len(self.getWorkerExposureSet()) > 0
def isExposedInWorkerDebugger(self):
return len(self.getWorkerDebuggerExposureSet()) > 0
- def isExposedInSystemGlobals(self):
- return 'BackstagePass' in self.exposureSet
+ def isExposedInAnyWorklet(self):
+ return len(self.getWorkletExposureSet()) > 0
def isExposedInSomeButNotAllWorkers(self):
"""
@@ -534,22 +509,24 @@ class IDLExposureMixins():
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, IDLExposureMixins):
+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)
- IDLExposureMixins.__init__(self, location)
IDLObjectWithIdentifier.resolve(self, parentScope)
def finish(self, scope):
- IDLExposureMixins.finish(self, scope)
pass
def validate(self):
@@ -564,11 +541,11 @@ class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
def isInterface(self):
return True
- def isConsequential(self):
- return False
-
def addExtendedAttributes(self, attrs):
- assert len(attrs) == 0
+ 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
@@ -579,16 +556,41 @@ class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
def isJSImplemented(self):
return False
- def isProbablyShortLivingObject(self):
- return False
-
- def isNavigatorProperty(self):
+ 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)
@@ -602,13 +604,13 @@ class IDLPartialInterfaceOrNamespace(IDLObject):
self._haveSecureContextExtendedAttribute = False
self._nonPartialInterfaceOrNamespace = nonPartialInterfaceOrNamespace
self._finished = False
- nonPartialInterfaceOrNamespace.addPartialInterface(self)
+ nonPartialInterfaceOrNamespace.addPartial(self)
def addExtendedAttributes(self, attrs):
for attr in attrs:
identifier = attr.identifier()
- if identifier in ["Constructor", "NamedConstructor"]:
+ if identifier == "NamedConstructor":
self.propagatedExtendedAttrs.append(attr)
elif identifier == "SecureContext":
self._haveSecureContextExtendedAttribute = True
@@ -673,36 +675,225 @@ def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
for name in nameSet:
exposureSet.update(globalScope.globalNameMapping[name])
-
-class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
- def __init__(self, location, parentScope, name, parent, members,
- isKnownNonPartial):
+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, toStringTag):
assert isKnownNonPartial or not parent
assert isKnownNonPartial or len(members) == 0
self.parent = None
self._callback = False
- self._finished = False
- self.members = []
self.maplikeOrSetlikeOrIterable = None
- self._partialInterfaces = []
- self._extendedAttrDict = {}
# namedConstructors needs deterministic ordering because bindings code
# outputs the constructs in the order that namedConstructors enumerates
# them.
self.namedConstructors = list()
- self.implementedInterfaces = set()
- self._consequential = False
- self._isKnownNonPartial = False
+ self.legacyWindowAliases = []
+ self.includedMixins = set()
# self.interfacesBasedOnSelf is the set of interfaces that inherit from
- # self or have self as a consequential interface, including self itself.
+ # self, including self itself.
# Used for distinguishability checking.
self.interfacesBasedOnSelf = set([self])
- # self.interfacesImplementingSelf is the set of interfaces that directly
- # have self as a consequential interface
- self.interfacesImplementingSelf = set()
self._hasChildInterfaces = False
self._isOnGlobalProtoChain = False
# Tracking of the number of reserved slots we need for our
@@ -713,9 +904,14 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
# 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
+ # True if we have cross-origin members.
+ self.hasCrossOriginMembers = False
+ # True if some descendant (including ourselves) has cross-origin members
+ self.hasDescendantWithCrossOriginMembers = False
- IDLObjectWithScope.__init__(self, location, parentScope, name)
- IDLExposureMixins.__init__(self, location)
+ self.toStringTag = toStringTag
+
+ IDLInterfaceOrInterfaceMixinOrNamespace.__init__(self, location, parentScope, name)
if isKnownNonPartial:
self.setNonPartial(location, parent, members)
@@ -735,37 +931,23 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
def isIteratorInterface(self):
return self.iterableInterface is not None
- 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 finish(self, scope):
if self._finished:
return
self._finished = True
- if not self._isKnownNonPartial:
- raise WebIDLError("Interface %s does not have a non-partial "
- "declaration" % self.identifier.name,
- [self.location])
-
- IDLExposureMixins.finish(self, scope)
+ IDLInterfaceOrInterfaceMixinOrNamespace.finish(self, scope)
- # Now go ahead and merge in our partial interfaces.
- for partial in self._partialInterfaces:
- partial.finish(scope)
- self.addExtendedAttributes(partial.propagatedExtendedAttrs)
- self.members.extend(partial.members)
+ if len(self.legacyWindowAliases) > 0:
+ if not self.hasInterfaceObject():
+ raise WebIDLError("Interface %s unexpectedly has [LegacyWindowAlias] "
+ "and [NoInterfaceObject] 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
@@ -788,19 +970,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
# our required methods in Codegen. Generate members now.
self.maplikeOrSetlikeOrIterable.expand(self.members, self.isJSImplemented())
- # Now that we've merged in our partial interfaces, 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 that defined them"
- # and "partial interfaces default to interface they're a partial for"
- # rules from the spec.
- for m in self.members:
- # If m, or the partial interface 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)
-
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):
@@ -809,7 +978,11 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
(self.identifier.name,
self.parent.identifier.name),
[self.location])
- assert not parent or isinstance(parent, IDLInterface)
+ 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
@@ -835,10 +1008,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
self.totalMembersInSlots = self.parent.totalMembersInSlots
- # Interfaces with [Global] or [PrimaryGlobal] must not
- # have anything inherit from them
- if (self.parent.getExtendedAttribute("Global") or
- self.parent.getExtendedAttribute("PrimaryGlobal")):
+ # 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.
@@ -854,10 +1025,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
self.parent.identifier.name),
[self.location, self.parent.location])
- # Callbacks must not inherit from non-callbacks or inherit from
- # anything that has consequential interfaces.
+ # Callbacks must not inherit from non-callbacks.
# XXXbz Can non-callbacks inherit from callbacks? Spec issue pending.
- # XXXbz Can callbacks have consequential interfaces? Spec issue pending
if self.isCallback():
if not self.parent.isCallback():
raise WebIDLError("Callback interface %s inheriting from "
@@ -894,48 +1063,40 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
self.parent.identifier.name),
[self.location, self.parent.location])
- for iface in self.implementedInterfaces:
- iface.finish(scope)
+ for mixin in self.includedMixins:
+ mixin.finish(scope)
cycleInGraph = self.findInterfaceLoopPoint(self)
if cycleInGraph:
- raise WebIDLError("Interface %s has itself as ancestor or "
- "implemented interface" % self.identifier.name,
- [self.location, cycleInGraph.location])
-
- if self.isCallback():
- # "implements" should have made sure we have no
- # consequential interfaces.
- assert len(self.getConsequentialInterfaces()) == 0
- # And that we're not consequential.
- assert not self.isConsequential()
-
- # Now resolve() and finish() our members before importing the
- # ones from our implemented interfaces.
-
- # 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)
+ raise WebIDLError(
+ "Interface %s has itself as ancestor" % self.identifier.name,
+ [self.location, cycleInGraph.location])
- 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 member has larger exposure set "
- "than the interface itself",
- [member.location, self.location])
+ self.finishMembers(scope)
ctor = self.ctor()
if ctor is not None:
- assert len(ctor._exposureGlobalNames) == 0
+ if not self.hasInterfaceObject():
+ raise WebIDLError(
+ "Can't have both a constructor and [NoInterfaceObject]",
+ [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)
- ctor.finish(scope)
+ # 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.namedConstructors:
+ if self.globalNames:
+ raise WebIDLError(
+ "Can't have both a named constructor and [Global]",
+ [self.location, ctor.location])
assert len(ctor._exposureGlobalNames) == 0
ctor._exposureGlobalNames.update(self._exposureGlobalNames)
ctor.finish(scope)
@@ -945,41 +1106,16 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
# admixed.
self.originalMembers = list(self.members)
- # Import everything from our consequential interfaces into
- # self.members. Sort our consequential interfaces by name
- # just so we have a consistent order.
- for iface in sorted(self.getConsequentialInterfaces(),
- cmp=cmp,
+ for mixin in sorted(self.includedMixins,
key=lambda x: x.identifier.name):
- # Flag the interface as being someone's consequential interface
- iface.setIsConsequentialInterfaceOf(self)
- # Verify that we're not exposed somewhere where iface is not exposed
- if not self.exposureSet.issubset(iface.exposureSet):
- raise WebIDLError("Interface %s is exposed in globals where its "
- "consequential interface %s is not exposed." %
- (self.identifier.name, iface.identifier.name),
- [self.location, iface.location])
-
- # If we have a maplike or setlike, and the consequential interface
- # also does, throw an error.
- if iface.maplikeOrSetlikeOrIterable and self.maplikeOrSetlikeOrIterable:
- raise WebIDLError("Maplike/setlike/iterable interface %s cannot have "
- "maplike/setlike/iterable interface %s as a "
- "consequential interface" %
- (self.identifier.name,
- iface.identifier.name),
- [self.maplikeOrSetlikeOrIterable.location,
- iface.maplikeOrSetlikeOrIterable.location])
- additionalMembers = iface.originalMembers
- for additionalMember in additionalMembers:
+ for mixinMember in mixin.members:
for member in self.members:
- if additionalMember.identifier.name == member.identifier.name:
+ if mixinMember.identifier.name == member.identifier.name:
raise WebIDLError(
- "Multiple definitions of %s on %s coming from 'implements' statements" %
+ "Multiple definitions of %s on %s coming from 'includes' statements" %
(member.identifier.name, self),
- [additionalMember.location, member.location])
- self.members.extend(additionalMembers)
- iface.interfacesImplementingSelf.add(self)
+ [mixinMember.location, member.location])
+ self.members.extend(mixin.members)
for ancestor in self.getInheritedInterfaces():
ancestor.interfacesBasedOnSelf.add(self)
@@ -992,8 +1128,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
ancestor.identifier.name),
[self.maplikeOrSetlikeOrIterable.location,
ancestor.maplikeOrSetlikeOrIterable.location])
- for ancestorConsequential in ancestor.getConsequentialInterfaces():
- ancestorConsequential.interfacesBasedOnSelf.add(self)
# Deal with interfaces marked [Unforgeable], now that we have our full
# member list, except unforgeables pulled in from parents. We want to
@@ -1009,10 +1143,9 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
[self.location])
for m in self.members:
- if ((m.isMethod() and m.isJsonifier()) or
- m.identifier.name == "toJSON"):
+ if m.identifier.name == "toJSON":
raise WebIDLError("Unforgeable interface %s has a "
- "jsonifier so we won't be able to add "
+ "toJSON so we won't be able to add "
"one ourselves" % self.identifier.name,
[self.location, m.location])
@@ -1028,6 +1161,21 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
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.
@@ -1036,6 +1184,12 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
(member.getExtendedAttribute("StoreInSlot") or
member.getExtendedAttribute("Cached"))) 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] "
+ "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
@@ -1044,13 +1198,12 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
self._ownMembersInSlots += 1
if self.parent:
- # Make sure we don't shadow any of the [Unforgeable] attributes on
- # our ancestor interfaces. We don't have to worry about
- # consequential interfaces 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.
+ # Make sure we don't shadow any of the [Unforgeable] 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.isUnforgeable()):
@@ -1087,10 +1240,13 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
testInterface = testInterface.parent
# Ensure that there's at most one of each {named,indexed}
- # {getter,setter,creator,deleter}, at most one stringifier,
+ # {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.
+ # 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():
@@ -1100,21 +1256,16 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
memberType = "getters"
elif member.isSetter():
memberType = "setters"
- elif member.isCreator():
- memberType = "creators"
elif member.isDeleter():
memberType = "deleters"
elif member.isStringifier():
memberType = "stringifiers"
- elif member.isJsonifier():
- memberType = "jsonifiers"
elif member.isLegacycaller():
memberType = "legacycallers"
else:
continue
- if (memberType != "stringifiers" and memberType != "legacycallers" and
- memberType != "jsonifiers"):
+ if (memberType != "stringifiers" and memberType != "legacycallers"):
if member.isNamed():
memberType = "named " + memberType
else:
@@ -1147,8 +1298,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
ancestor = ancestor.parent
if self._isOnGlobalProtoChain:
- # Make sure we have no named setters, creators, or deleters
- for memberType in ["setter", "creator", "deleter"]:
+ # 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" %
@@ -1172,16 +1323,21 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
parent = parent.parent
def validate(self):
- # We don't support consequential unforgeable interfaces. Need to check
- # this here, because in finish() an interface might not know yet that
- # it's consequential.
- if self.getExtendedAttribute("Unforgeable") and self.isConsequential():
- raise WebIDLError(
- "%s is an unforgeable consequential interface" %
- self.identifier.name,
- [self.location] +
- list(i.location for i in
- (self.interfacesBasedOnSelf - {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("Unforgeable") and self.hasChildInterfaces():
@@ -1192,6 +1348,12 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
self.identifier.name,
locations)
+ ctor = self.ctor()
+ if ctor is not None:
+ ctor.validate()
+ for namedCtor in self.namedConstructors:
+ namedCtor.validate()
+
indexedGetter = None
hasLengthAttribute = False
for member in self.members:
@@ -1278,23 +1440,22 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
raise WebIDLError("[Alias] must not be used on an "
"[Unforgeable] operation",
[member.location])
- for m in self.members:
- if m.identifier.name == alias:
- raise WebIDLError("[Alias=%s] has same name as "
- "interface member" % alias,
- [member.location, m.location])
- if m.isMethod() and m != member and alias in m.aliases:
- raise WebIDLError("duplicate [Alias=%s] definitions" %
- alias,
- [member.location, m.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, unless they're navigator properties.
+ # 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() and
- not self.isNavigatorProperty()):
+ not self.hasInterfaceObject()):
raise WebIDLError("Interface with no interface object is "
"exposed conditionally",
[self.location])
@@ -1308,7 +1469,7 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
if not indexedGetter:
raise WebIDLError("Interface with value iterator does not "
"support indexed properties",
- [self.location])
+ [self.location, iterableDecl.location])
if iterableDecl.valueType != indexedGetter.signatures()[0][0]:
raise WebIDLError("Iterable type does not match indexed "
@@ -1319,7 +1480,7 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
if not hasLengthAttribute:
raise WebIDLError('Interface with value iterator does not '
'have an integer-typed "length" attribute',
- [self.location])
+ [self.location, iterableDecl.location])
else:
assert iterableDecl.isPairIterator()
if indexedGetter:
@@ -1328,15 +1489,10 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
[self.location, iterableDecl.location,
indexedGetter.location])
- def isExternal(self):
- return False
-
- def setIsConsequentialInterfaceOf(self, other):
- self._consequential = True
- self.interfacesBasedOnSelf.add(other)
-
- def isConsequential(self):
- return self._consequential
+ 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
@@ -1352,8 +1508,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
not self.isJSImplemented() and
# Not inheriting from another interface
not self.parent and
- # No consequential interfaces
- len(self.getConsequentialInterfaces()) == 0 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
@@ -1381,9 +1535,9 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
return (not self.isCallback() and not self.isNamespace()
and self.getUserData('hasConcreteDescendant', False))
- def addImplementedInterface(self, implementedInterface):
- assert(isinstance(implementedInterface, IDLInterface))
- self.implementedInterfaces.add(implementedInterface)
+ def addIncludedMixin(self, includedMixin):
+ assert(isinstance(includedMixin, IDLInterfaceMixin))
+ self.includedMixins.add(includedMixin)
def getInheritedInterfaces(self):
"""
@@ -1398,27 +1552,10 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
parentInterfaces.insert(0, self.parent)
return parentInterfaces
- def getConsequentialInterfaces(self):
- assert(self._finished)
- # The interfaces we implement directly
- consequentialInterfaces = set(self.implementedInterfaces)
-
- # And their inherited interfaces
- for iface in self.implementedInterfaces:
- consequentialInterfaces |= set(iface.getInheritedInterfaces())
-
- # And now collect up the consequential interfaces of all of those
- temp = set()
- for iface in consequentialInterfaces:
- temp |= iface.getConsequentialInterfaces()
-
- return consequentialInterfaces | temp
-
def findInterfaceLoopPoint(self, otherInterface):
"""
- Finds an interface, amongst our ancestors and consequential interfaces,
- that inherits from otherInterface or implements otherInterface
- directly. If there is no such interface, returns None.
+ 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:
@@ -1426,37 +1563,13 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
loopPoint = self.parent.findInterfaceLoopPoint(otherInterface)
if loopPoint:
return loopPoint
- if otherInterface in self.implementedInterfaces:
- return self
- for iface in self.implementedInterfaces:
- loopPoint = iface.findInterfaceLoopPoint(otherInterface)
- if loopPoint:
- return loopPoint
return None
- def getExtendedAttribute(self, name):
- return self._extendedAttrDict.get(name, None)
-
def setNonPartial(self, location, parent, members):
assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
- if self._isKnownNonPartial:
- raise WebIDLError("Two non-partial definitions for the "
- "same %s" %
- ("interface" if self.isInterface()
- else "namespace"),
- [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
+ IDLInterfaceOrInterfaceMixinOrNamespace.setNonPartial(self, location, members)
assert not self.parent
self.parent = parent
- # Put the new members at the beginning
- self.members = members + self.members
-
- def addPartialInterface(self, partial):
- assert self.identifier.name == partial.identifier.name
- self._partialInterfaces.append(partial)
def getJSImplementation(self):
classId = self.getExtendedAttribute("JSImplementation")
@@ -1469,47 +1582,14 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
def isJSImplemented(self):
return bool(self.getJSImplementation())
- def isProbablyShortLivingObject(self):
+ def hasProbablyShortLivingWrapper(self):
current = self
while current:
- if current.getExtendedAttribute("ProbablyShortLivingObject"):
+ if current.getExtendedAttribute("ProbablyShortLivingWrapper"):
return True
current = current.parent
return False
- def isNavigatorProperty(self):
- naviProp = self.getExtendedAttribute("NavigatorProperty")
- if not naviProp:
- return False
- assert len(naviProp) == 1
- assert isinstance(naviProp, list)
- assert len(naviProp[0]) != 0
- return True
-
- def getNavigatorProperty(self):
- naviProp = self.getExtendedAttribute("NavigatorProperty")
- if not naviProp:
- return None
- assert len(naviProp) == 1
- assert isinstance(naviProp, list)
- assert len(naviProp[0]) != 0
- conditionExtendedAttributes = self._extendedAttrDict.viewkeys() & IDLInterfaceOrNamespace.conditionExtendedAttributes
- attr = IDLAttribute(self.location,
- IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), naviProp[0]),
- IDLUnresolvedType(self.location, IDLUnresolvedIdentifier(self.location, self.identifier.name)),
- True,
- extendedAttrDict={ a: self._extendedAttrDict[a] for a in conditionExtendedAttributes },
- navigatorObjectGetter=True)
- attr._exposureGlobalNames = self._exposureGlobalNames
- # We're abusing Constant a little bit here, because we need Cached. The
- # getter will create a new object every time, but we're never going to
- # clear the cached value.
- extendedAttrs = [ IDLExtendedAttribute(self.location, ("Throws", )),
- IDLExtendedAttribute(self.location, ("Cached", )),
- IDLExtendedAttribute(self.location, ("Constant", )) ]
- attr.addExtendedAttributes(extendedAttrs)
- return attr
-
def hasChildInterfaces(self):
return self._hasChildInterfaces
@@ -1518,7 +1598,7 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
def _getDependentObjects(self):
deps = set(self.members)
- deps.update(self.implementedInterfaces)
+ deps.update(self.includedMixins)
if self.parent:
deps.add(self.parent)
return deps
@@ -1526,18 +1606,19 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
def hasMembersInSlots(self):
return self._ownMembersInSlots != 0
- conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn",
- "SecureContext",
- "CheckAnyPermissions",
- "CheckAllPermissions" ]
+ conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func",
+ "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):
+ isKnownNonPartial, classNameOverride=None,
+ toStringTag=None):
IDLInterfaceOrNamespace.__init__(self, location, parentScope, name,
- parent, members, isKnownNonPartial)
+ parent, members, isKnownNonPartial,
+ toStringTag)
+ self.classNameOverride = classNameOverride
def __str__(self):
return "Interface '%s'" % self.identifier.name
@@ -1545,6 +1626,11 @@ class IDLInterface(IDLInterfaceOrNamespace):
def isInterface(self):
return True
+ def getClassName(self):
+ if self.classNameOverride:
+ return self.classNameOverride
+ return self.identifier.name
+
def addExtendedAttributes(self, attrs):
for attr in attrs:
identifier = attr.identifier()
@@ -1561,86 +1647,46 @@ class IDLInterface(IDLInterfaceOrNamespace):
raise WebIDLError("[NoInterfaceObject] must take no arguments",
[attr.location])
- if self.ctor():
- raise WebIDLError("Constructor and NoInterfaceObject are incompatible",
- [self.location])
-
self._noInterfaceObject = True
- elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor":
- if identifier == "Constructor" and not self.hasInterfaceObject():
- raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
- [self.location])
-
- if identifier == "NamedConstructor" and not attr.hasValue():
+ elif identifier == "NamedConstructor":
+ if not attr.hasValue():
raise WebIDLError("NamedConstructor must either take an identifier or take a named argument list",
[attr.location])
- if identifier == "ChromeConstructor" and not self.hasInterfaceObject():
- raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
- [self.location])
args = attr.args() if attr.hasArgs() else []
- if self.identifier.name == "Promise":
- promiseType = BuiltinTypes[IDLBuiltinType.Types.any]
- else:
- promiseType = None
- retType = IDLWrapperType(self.location, self, promiseType)
+ retType = IDLWrapperType(self.location, self)
- if identifier == "Constructor" or identifier == "ChromeConstructor":
- name = "constructor"
- allowForbidden = True
- else:
- name = attr.value()
- allowForbidden = False
-
- methodIdentifier = IDLUnresolvedIdentifier(self.location, name,
- allowForbidden=allowForbidden)
-
- method = IDLMethod(self.location, methodIdentifier, retType,
- args, static=True)
- # Constructors are always NewObject and are always
- # assumed to be able to throw (since there's no way to
- # indicate otherwise) and never have any other
- # extended attributes.
+ 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, ("NewObject",)),
- IDLExtendedAttribute(self.location, ("Throws",))])
- if identifier == "ChromeConstructor":
- method.addExtendedAttributes(
- [IDLExtendedAttribute(self.location, ("ChromeOnly",))])
-
- if identifier == "Constructor" or identifier == "ChromeConstructor":
- method.resolve(self)
- else:
- # We need to detect conflicts for NamedConstructors across
- # interfaces. We first call resolve on the parentScope,
- # which will merge all NamedConstructors 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 NamedConstructor 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
- # NamedConstructors.
- newMethod = self.parentScope.lookupIdentifier(method.identifier)
- if newMethod == method:
- self.namedConstructors.append(method)
- elif newMethod not in self.namedConstructors:
- raise WebIDLError("NamedConstructor conflicts with a NamedConstructor of a different interface",
- [method.location, newMethod.location])
- elif (identifier == "ArrayClass"):
- if not attr.noArguments():
- raise WebIDLError("[ArrayClass] must take no arguments",
- [attr.location])
- if self.parent:
- raise WebIDLError("[ArrayClass] must not be specified on "
- "an interface with inherited interfaces",
- [attr.location, self.location])
+ [IDLExtendedAttribute(self.location, ("Throws",))])
+
+ # We need to detect conflicts for NamedConstructors across
+ # interfaces. We first call resolve on the parentScope,
+ # which will merge all NamedConstructors 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 NamedConstructor 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
+ # NamedConstructors.
+ newMethod = self.parentScope.lookupIdentifier(method.identifier)
+ if newMethod == method:
+ self.namedConstructors.append(method)
+ elif newMethod not in self.namedConstructors:
+ raise WebIDLError("NamedConstructor conflicts with a "
+ "NamedConstructor of a different interface",
+ [method.location, newMethod.location])
elif (identifier == "ExceptionClass"):
if not attr.noArguments():
raise WebIDLError("[ExceptionClass] must take no arguments",
@@ -1656,24 +1702,21 @@ class IDLInterface(IDLInterfaceOrNamespace):
self.globalNames = attr.args()
else:
self.globalNames = [self.identifier.name]
- self.parentScope.globalNames.update(self.globalNames)
- for globalName in self.globalNames:
- self.parentScope.globalNameMapping[globalName].add(self.identifier.name)
+ self.parentScope.addIfaceGlobalNames(self.identifier.name,
+ self.globalNames)
self._isOnGlobalProtoChain = True
- elif identifier == "PrimaryGlobal":
- if not attr.noArguments():
- raise WebIDLError("[PrimaryGlobal] must take no arguments",
+ 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])
- if self.parentScope.primaryGlobalAttr is not None:
- raise WebIDLError(
- "[PrimaryGlobal] specified twice",
- [attr.location,
- self.parentScope.primaryGlobalAttr.location])
- self.parentScope.primaryGlobalAttr = attr
- self.parentScope.primaryGlobalName = self.identifier.name
- self.parentScope.globalNames.add(self.identifier.name)
- self.parentScope.globalNameMapping[self.identifier.name].add(self.identifier.name)
- self._isOnGlobalProtoChain = True
+ 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,
@@ -1690,11 +1733,12 @@ class IDLInterface(IDLInterfaceOrNamespace):
identifier == "OverrideBuiltins" or
identifier == "ChromeOnly" or
identifier == "Unforgeable" or
- identifier == "UnsafeInPrerendering" or
identifier == "LegacyEventInit" or
- identifier == "ProbablyShortLivingObject" or
+ identifier == "ProbablyShortLivingWrapper" or
identifier == "LegacyUnenumerableNamedProperties" or
- identifier == "NonOrdinaryGetPrototypeOf" or
+ identifier == "RunConstructorInCallerCompartment" or
+ identifier == "WantsEventListenerHooks" or
+ identifier == "Serializable" or
identifier == "Abstract" or
identifier == "Inline"):
# Known extended attributes that do not take values
@@ -1707,13 +1751,17 @@ class IDLInterface(IDLInterfaceOrNamespace):
elif (identifier == "Pref" or
identifier == "JSImplementation" or
identifier == "HeaderFile" or
- identifier == "NavigatorProperty" or
identifier == "Func" 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])
@@ -1721,11 +1769,36 @@ class IDLInterface(IDLInterfaceOrNamespace):
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)
+ None, members, isKnownNonPartial,
+ toStringTag=None)
def __str__(self):
return "Namespace '%s'" % self.identifier.name
@@ -1750,10 +1823,18 @@ class IDLNamespace(IDLInterfaceOrNamespace):
if not attr.hasValue():
raise WebIDLError("[%s] must have a value" % identifier,
[attr.location])
- elif identifier == "ProtoObjectHack":
+ 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"):
+ # 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,
@@ -1762,6 +1843,9 @@ class IDLNamespace(IDLInterfaceOrNamespace):
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):
@@ -1772,6 +1856,10 @@ class IDLDictionary(IDLObjectWithScope):
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)
@@ -1808,6 +1896,11 @@ class IDLDictionary(IDLObjectWithScope):
# 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():
@@ -1815,7 +1908,7 @@ class IDLDictionary(IDLObjectWithScope):
assert member.type.isComplete()
# Members of a dictionary are sorted in lexicographic order
- self.members.sort(cmp=cmp, key=lambda x: x.identifier.name)
+ self.members.sort(key=lambda x: x.identifier.name)
inheritedMembers = []
ancestor = self.parent
@@ -1853,7 +1946,7 @@ class IDLDictionary(IDLObjectWithScope):
if (memberType.nullable() or
memberType.isSequence() or
- memberType.isMozMap()):
+ memberType.isRecord()):
return typeContainsDictionary(memberType.inner, dictionary)
if memberType.isDictionary():
@@ -1900,8 +1993,34 @@ class IDLDictionary(IDLObjectWithScope):
self.identifier.name,
[member.location] + locations)
+ def getExtendedAttribute(self, name):
+ return self._extendedAttrDict.get(name, None)
+
def addExtendedAttributes(self, attrs):
- assert len(attrs) == 0
+ 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
+ else:
+ raise WebIDLError("[%s] extended attribute not allowed on "
+ "dictionaries" % identifier,
+ [attr.location])
+
+ self._extendedAttrDict[identifier] = True
def _getDependentObjects(self):
deps = set(self.members)
@@ -1909,6 +2028,10 @@ class IDLDictionary(IDLObjectWithScope):
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):
@@ -1935,7 +2058,10 @@ class IDLEnum(IDLObjectWithIdentifier):
return True
def addExtendedAttributes(self, attrs):
- assert len(attrs) == 0
+ 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()
@@ -1964,8 +2090,9 @@ class IDLType(IDLObject):
'domstring',
'bytestring',
'usvstring',
+ 'utf8string',
+ 'jsstring',
'object',
- 'date',
'void',
# Funny stuff
'interface',
@@ -1974,16 +2101,25 @@ class IDLType(IDLObject):
'callback',
'union',
'sequence',
- 'mozmap'
+ 'record',
+ 'promise',
)
def __init__(self, location, name):
IDLObject.__init__(self, location)
self.name = name
self.builtin = False
+ self.treatNullAsEmpty = False
+ self._clamp = False
+ self._enforceRange = False
+ self._allowShared = False
+ self._extendedAttrDict = {}
def __eq__(self, other):
- return other and self.builtin == other.builtin and self.name == other.name
+ return (other and self.builtin == other.builtin and self.name == other.name and
+ self._clamp == other.hasClamp() and self._enforceRange == other.hasEnforceRange() and
+ self.treatNullAsEmpty == other.treatNullAsEmpty and
+ self._allowShared == other.hasAllowShared())
def __ne__(self, other):
return not self == other
@@ -1991,6 +2127,14 @@ class IDLType(IDLObject):
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
@@ -2018,27 +2162,36 @@ class IDLType(IDLObject):
def isUSVString(self):
return False
+ def isUTF8String(self):
+ return False
+
+ def isJSString(self):
+ return False
+
def isVoid(self):
return self.name == "Void"
def isSequence(self):
return False
- def isMozMap(self):
+ def isRecord(self):
return False
- def isArrayBuffer(self):
+ def isReadableStream(self):
return False
- def isArrayBufferView(self):
+ def isArrayBuffer(self):
return False
- def isSharedArrayBuffer(self):
+ 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
@@ -2054,12 +2207,9 @@ class IDLType(IDLObject):
def isSpiderMonkeyInterface(self):
""" Returns a boolean indicating whether this type is an 'interface'
- type that is implemented in Spidermonkey. At the moment, this
- only returns true for the types from the TypedArray spec. """
- return self.isInterface() and (self.isArrayBuffer() or
- self.isArrayBufferView() or
- self.isSharedArrayBuffer() or
- self.isTypedArray())
+ type that is implemented in SpiderMonkey. """
+ return self.isInterface() and (self.isBufferSource() or
+ self.isReadableStream())
def isDictionary(self):
return False
@@ -2070,9 +2220,6 @@ class IDLType(IDLObject):
def isAny(self):
return self.tag() == IDLType.Tags.any
- def isDate(self):
- return self.tag() == IDLType.Tags.date
-
def isObject(self):
return self.tag() == IDLType.Tags.object
@@ -2092,9 +2239,18 @@ class IDLType(IDLObject):
# Should only call this on float types
assert self.isFloat()
- def isSerializable(self):
+ def isJSONType(self):
return False
+ 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!
@@ -2106,8 +2262,14 @@ class IDLType(IDLObject):
assert self.tag() == IDLType.Tags.callback
return self.nullable() and self.inner.callback._treatNonObjectAsNull
- def addExtendedAttributes(self, attrs):
- assert len(attrs) == 0
+ 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
@@ -2128,9 +2290,9 @@ class IDLUnresolvedType(IDLType):
Unresolved types are interface types
"""
- def __init__(self, location, name, promiseInnerType=None):
+ def __init__(self, location, name, attrs=[]):
IDLType.__init__(self, location, name)
- self._promiseInnerType = promiseInnerType
+ self.extraTypeAttributes = attrs
def isComplete(self):
return False
@@ -2145,30 +2307,30 @@ class IDLUnresolvedType(IDLType):
assert obj
if obj.isType():
- print obj
+ print(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)
+ 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)
- if self._promiseInnerType and not self._promiseInnerType.isComplete():
- self._promiseInnerType = self._promiseInnerType.complete(scope)
-
name = self.name.resolve(scope, None)
- return IDLWrapperType(self.location, obj, self._promiseInnerType)
+ 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 IDLParameterizedType(IDLType):
+class IDLParametrizedType(IDLType):
def __init__(self, location, name, innerType):
IDLType.__init__(self, location, name)
self.builtin = False
@@ -2191,22 +2353,25 @@ class IDLParameterizedType(IDLType):
return self.inner._getDependentObjects()
-class IDLNullableType(IDLParameterizedType):
+class IDLNullableType(IDLParametrizedType):
def __init__(self, location, innerType):
assert not innerType.isVoid()
assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any]
- name = innerType.name
- if innerType.isComplete():
- name += "OrNull"
- IDLParameterizedType.__init__(self, location, name, innerType)
+ IDLParametrizedType.__init__(self, location, None, innerType)
def __eq__(self, other):
return isinstance(other, IDLNullableType) and self.inner == other.inner
+ def __hash__(self):
+ return hash(self.inner)
+
def __str__(self):
return self.inner.__str__() + "OrNull"
+ def prettyName(self):
+ return self.inner.prettyName() + "?"
+
def nullable(self):
return True
@@ -2234,6 +2399,12 @@ class IDLNullableType(IDLParameterizedType):
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()
@@ -2249,8 +2420,11 @@ class IDLNullableType(IDLParameterizedType):
def isSequence(self):
return self.inner.isSequence()
- def isMozMap(self):
- return self.inner.isMozMap()
+ def isRecord(self):
+ return self.inner.isRecord()
+
+ def isReadableStream(self):
+ return self.inner.isReadableStream()
def isArrayBuffer(self):
return self.inner.isArrayBuffer()
@@ -2258,9 +2432,6 @@ class IDLNullableType(IDLParameterizedType):
def isArrayBufferView(self):
return self.inner.isArrayBufferView()
- def isSharedArrayBuffer(self):
- return self.inner.isSharedArrayBuffer()
-
def isTypedArray(self):
return self.inner.isTypedArray()
@@ -2271,7 +2442,9 @@ class IDLNullableType(IDLParameterizedType):
return self.inner.isInterface()
def isPromise(self):
- return self.inner.isPromise()
+ # There is no such thing as a nullable Promise.
+ assert not self.inner.isPromise()
+ return False
def isCallbackInterface(self):
return self.inner.isCallbackInterface()
@@ -2285,14 +2458,29 @@ class IDLNullableType(IDLParameterizedType):
def isUnion(self):
return self.inner.isUnion()
- def isSerializable(self):
- return self.inner.isSerializable()
+ def isJSONType(self):
+ return self.inner.isJSONType()
+
+ 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):
- self.inner = self.inner.complete(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",
@@ -2302,23 +2490,36 @@ class IDLNullableType(IDLParameterizedType):
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.treatNullAsEmpty:
+ raise WebIDLError("[TreatNullAs] not allowed on a nullable DOMString",
+ [self.location, self.inner.location])
self.name = self.inner.name + "OrNull"
return self
def isDistinguishableFrom(self, other):
- if (other.nullable() or (other.isUnion() and other.hasNullableType) or
- other.isDictionary()):
+ 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(IDLParameterizedType):
+
+class IDLSequenceType(IDLParametrizedType):
def __init__(self, location, parameterType):
assert not parameterType.isVoid()
- IDLParameterizedType.__init__(self, location, parameterType.name, parameterType)
+ 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():
@@ -2327,9 +2528,15 @@ class IDLSequenceType(IDLParameterizedType):
def __eq__(self, other):
return isinstance(other, IDLSequenceType) and self.inner == other.inner
+ def __hash__(self):
+ return hash(self.inner)
+
def __str__(self):
return self.inner.__str__() + "Sequence"
+ def prettyName(self):
+ return "sequence<%s>" % self.inner.prettyName()
+
def nullable(self):
return False
@@ -2348,6 +2555,12 @@ class IDLSequenceType(IDLParameterizedType):
def isUSVString(self):
return False
+ def isUTF8String(self):
+ return False
+
+ def isJSString(self):
+ return False
+
def isVoid(self):
return False
@@ -2363,8 +2576,8 @@ class IDLSequenceType(IDLParameterizedType):
def isEnum(self):
return False
- def isSerializable(self):
- return self.inner.isSerializable()
+ def isJSONType(self):
+ return self.inner.isJSONType()
def tag(self):
return IDLType.Tags.sequence
@@ -2381,36 +2594,45 @@ class IDLSequenceType(IDLParameterizedType):
# Just forward to the union; it'll deal
return other.isDistinguishableFrom(self)
return (other.isPrimitive() or other.isString() or other.isEnum() or
- other.isDate() or other.isInterface() or
- other.isDictionary() or
- other.isCallback() or other.isMozMap())
+ other.isInterface() or other.isDictionary() or
+ other.isCallback() or other.isRecord())
-class IDLMozMapType(IDLParameterizedType):
- def __init__(self, location, parameterType):
- assert not parameterType.isVoid()
+class IDLRecordType(IDLParametrizedType):
+ def __init__(self, location, keyType, valueType):
+ assert keyType.isString()
+ assert keyType.isComplete()
+ assert not valueType.isVoid()
+
+ IDLParametrizedType.__init__(self, location, valueType.name, valueType)
+ self.keyType = keyType
- IDLParameterizedType.__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 + "MozMap"
+ self.name = self.keyType.name + self.inner.name + "Record"
def __eq__(self, other):
- return isinstance(other, IDLMozMapType) and self.inner == other.inner
+ return isinstance(other, IDLRecordType) and self.inner == other.inner
def __str__(self):
- return self.inner.__str__() + "MozMap"
+ return self.keyType.__str__() + self.inner.__str__() + "Record"
- def isMozMap(self):
+ 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.mozmap
+ return IDLType.Tags.record
def complete(self, scope):
self.inner = self.inner.complete(scope)
- self.name = self.inner.name + "MozMap"
+ self.name = self.keyType.name + self.inner.name + "Record"
return self
def unroll(self):
@@ -2426,7 +2648,7 @@ class IDLMozMapType(IDLParameterizedType):
# Just forward to the union; it'll deal
return other.isDistinguishableFrom(self)
return (other.isPrimitive() or other.isString() or other.isEnum() or
- other.isDate() or other.isNonCallbackInterface() or other.isSequence())
+ other.isNonCallbackInterface() or other.isSequence())
def isExposedInAllOf(self, exposureSet):
return self.inner.unroll().isExposedInAllOf(exposureSet)
@@ -2448,14 +2670,17 @@ class IDLUnionType(IDLType):
assert self.isComplete()
return self.name.__hash__()
+ def prettyName(self):
+ return "(" + " or ".join(m.prettyName() for m in self.memberTypes) + ")"
+
def isVoid(self):
return False
def isUnion(self):
return True
- def isSerializable(self):
- return all(m.isSerializable() for m in self.memberTypes)
+ def isJSONType(self):
+ return all(m.isJSONType() for m in self.memberTypes)
def includesRestrictedFloat(self):
return any(t.includesRestrictedFloat() for t in self.memberTypes)
@@ -2479,6 +2704,9 @@ class IDLUnionType(IDLType):
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):
@@ -2602,14 +2830,26 @@ class IDLTypedefType(IDLType):
def isUSVString(self):
return self.inner.isUSVString()
+ def isUTF8String(self):
+ return self.inner.isUTF8String()
+
+ def isJSString(self):
+ return self.inner.isJSString()
+
def isVoid(self):
return self.inner.isVoid()
+ def isJSONType(self):
+ return self.inner.isJSONType()
+
def isSequence(self):
return self.inner.isSequence()
- def isMozMap(self):
- return self.inner.isMozMap()
+ def isRecord(self):
+ return self.inner.isRecord()
+
+ def isReadableStream(self):
+ return self.inner.isReadableStream()
def isDictionary(self):
return self.inner.isDictionary()
@@ -2620,9 +2860,6 @@ class IDLTypedefType(IDLType):
def isArrayBufferView(self):
return self.inner.isArrayBufferView()
- def isSharedArrayBuffer(self):
- return self.inner.isSharedArrayBuffer()
-
def isTypedArray(self):
return self.inner.isTypedArray()
@@ -2658,12 +2895,17 @@ class IDLTypedefType(IDLType):
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)
- self.innerType = innerType
def __str__(self):
return "Typedef %s %s" % (self.identifier.name, self.innerType)
@@ -2679,26 +2921,30 @@ class IDLTypedef(IDLObjectWithIdentifier):
return True
def addExtendedAttributes(self, attrs):
- assert len(attrs) == 0
+ 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, promiseInnerType=None):
+ def __init__(self, location, inner):
IDLType.__init__(self, location, inner.identifier.name)
self.inner = inner
self._identifier = inner.identifier
self.builtin = False
- assert not promiseInnerType or inner.identifier.name == "Promise"
- self._promiseInnerType = promiseInnerType
def __eq__(self, other):
return (isinstance(other, IDLWrapperType) and
self._identifier == other._identifier and
self.builtin == other.builtin)
+ def __hash__(self):
+ return hash((self._identifier, self.builtin))
+
def __str__(self):
return str(self.name) + " (Wrapper)"
@@ -2720,6 +2966,12 @@ class IDLWrapperType(IDLType):
def isUSVString(self):
return False
+ def isUTF8String(self):
+ return False
+
+ def isJSString(self):
+ return False
+
def isVoid(self):
return False
@@ -2742,23 +2994,25 @@ class IDLWrapperType(IDLType):
def isEnum(self):
return isinstance(self.inner, IDLEnum)
- def isPromise(self):
- return (isinstance(self.inner, IDLInterface) and
- self.inner.identifier.name == "Promise")
-
- def promiseInnerType(self):
- assert self.isPromise()
- return self._promiseInnerType
-
- def isSerializable(self):
+ def isJSONType(self):
if self.isInterface():
if self.inner.isExternal():
return False
- return any(m.isMethod() and m.isJsonifier() for m in self.inner.members)
+ 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():
- return all(m.type.isSerializable() for m in self.inner.members)
+ 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])
@@ -2781,8 +3035,6 @@ class IDLWrapperType(IDLType):
assert False
def isDistinguishableFrom(self, other):
- if self.isPromise():
- return False
if other.isPromise():
return False
if other.isUnion():
@@ -2792,11 +3044,11 @@ class IDLWrapperType(IDLType):
if self.isEnum():
return (other.isPrimitive() or other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate())
+ other.isSequence() or other.isRecord())
if self.isDictionary() and other.nullable():
return False
if (other.isPrimitive() or other.isString() or other.isEnum() or
- other.isDate() or other.isSequence()):
+ other.isSequence()):
return True
if self.isDictionary():
return other.isNonCallbackInterface()
@@ -2814,7 +3066,7 @@ class IDLWrapperType(IDLType):
(self.isNonCallbackInterface() or
other.isNonCallbackInterface()))
if (other.isDictionary() or other.isCallback() or
- other.isMozMap()):
+ other.isRecord()):
return self.isNonCallbackInterface()
# Not much else |other| can be
@@ -2826,13 +3078,10 @@ class IDLWrapperType(IDLType):
return True
iface = self.inner
if iface.isExternal():
- # Let's say true, though ideally we'd only do this when
- # exposureSet contains the primary global's name.
+ # 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
- if (self.isPromise() and
- # Check the internal type
- not self.promiseInnerType().unroll().isExposedInAllOf(exposureSet)):
- return False
return iface.exposureSet.issuperset(exposureSet)
def _getDependentObjects(self):
@@ -2860,6 +3109,48 @@ class IDLWrapperType(IDLType):
return set()
+class IDLPromiseType(IDLParametrizedType):
+ def __init__(self, location, innerType):
+ IDLParametrizedType.__init__(self, location, "Promise", innerType)
+
+ 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):
+ 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(
@@ -2884,13 +3175,13 @@ class IDLBuiltinType(IDLType):
'domstring',
'bytestring',
'usvstring',
+ 'utf8string',
+ 'jsstring',
'object',
- 'date',
'void',
# Funny stuff
'ArrayBuffer',
'ArrayBufferView',
- 'SharedArrayBuffer',
'Int8Array',
'Uint8Array',
'Uint8ClampedArray',
@@ -2899,7 +3190,8 @@ class IDLBuiltinType(IDLType):
'Int32Array',
'Uint32Array',
'Float32Array',
- 'Float64Array'
+ 'Float64Array',
+ 'ReadableStream',
)
TagLookup = {
@@ -2920,12 +3212,12 @@ class IDLBuiltinType(IDLType):
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.date: IDLType.Tags.date,
Types.void: IDLType.Tags.void,
Types.ArrayBuffer: IDLType.Tags.interface,
Types.ArrayBufferView: IDLType.Tags.interface,
- Types.SharedArrayBuffer: IDLType.Tags.interface,
Types.Int8Array: IDLType.Tags.interface,
Types.Uint8Array: IDLType.Tags.interface,
Types.Uint8ClampedArray: IDLType.Tags.interface,
@@ -2934,13 +3226,129 @@ class IDLBuiltinType(IDLType):
Types.Int32Array: IDLType.Tags.interface,
Types.Uint32Array: IDLType.Tags.interface,
Types.Float32Array: IDLType.Tags.interface,
- Types.Float64Array: 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.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.void: "void",
+ 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):
+ def __init__(self, location, name, type, clamp=False, enforceRange=False, treatNullAsEmpty=False,
+ allowShared=False, attrLocation=[]):
+ """
+ The mutually exclusive clamp/enforceRange/treatNullAsEmpty/allowShared arguments are used
+ to create instances of this type with the appropriate attributes attached. Use .clamped(),
+ .rangeEnforced(), .withTreatNullAs() 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._withTreatNullAs = 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 treatNullAsEmpty:
+ self.treatNullAsEmpty = True
+ self.name = "NullIsEmpty" + self.name
+ self._extendedAttrDict["TreatNullAs"] = ["EmptyString"]
+ elif treatNullAsEmpty:
+ raise WebIDLError("Non-string types cannot be [TreatNullAs]", 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 __eq__(self, other):
+ return other and self.location == other.location and self.name == other.name and self._typeTag == other._typeTag
+
+ def __hash__(self):
+ return hash((self.location, self.name, self._typeTag))
+
+ 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 withTreatNullAs(self, attrLocation):
+ if not self._withTreatNullAs:
+ self._withTreatNullAs = IDLBuiltinType(self.location, self.name,
+ self._typeTag, treatNullAsEmpty=True,
+ attrLocation=attrLocation)
+ return self._withTreatNullAs
+
+ 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
@@ -2954,7 +3362,9 @@ class IDLBuiltinType(IDLType):
def isString(self):
return (self._typeTag == IDLBuiltinType.Types.domstring or
self._typeTag == IDLBuiltinType.Types.bytestring or
- self._typeTag == IDLBuiltinType.Types.usvstring)
+ 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
@@ -2965,6 +3375,12 @@ class IDLBuiltinType(IDLType):
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
@@ -2974,21 +3390,21 @@ class IDLBuiltinType(IDLType):
def isArrayBufferView(self):
return self._typeTag == IDLBuiltinType.Types.ArrayBufferView
- def isSharedArrayBuffer(self):
- return self._typeTag == IDLBuiltinType.Types.SharedArrayBuffer
-
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.isSharedArrayBuffer() or
- self.isTypedArray())
+ self.isTypedArray() or
+ self.isReadableStream())
def isNonCallbackInterface(self):
# All the interfaces we can be are non-callback
@@ -3005,8 +3421,8 @@ class IDLBuiltinType(IDLType):
return (self._typeTag == IDLBuiltinType.Types.unrestricted_float or
self._typeTag == IDLBuiltinType.Types.unrestricted_double)
- def isSerializable(self):
- return self.isPrimitive() or self.isString() or self.isDate()
+ def isJSONType(self):
+ return self.isPrimitive() or self.isString() or self.isObject()
def includesRestrictedFloat(self):
return self.isFloat() and not self.isUnrestricted()
@@ -3024,27 +3440,22 @@ class IDLBuiltinType(IDLType):
return (other.isNumeric() or other.isString() or other.isEnum() or
other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate())
+ other.isSequence() or other.isRecord())
if self.isNumeric():
return (other.isBoolean() or other.isString() or other.isEnum() or
other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate())
+ other.isSequence() or other.isRecord())
if self.isString():
return (other.isPrimitive() or other.isInterface() or
other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate())
+ other.isSequence() or other.isRecord())
if self.isAny():
# Can't tell "any" apart from anything
return False
if self.isObject():
return other.isPrimitive() or other.isString() or other.isEnum()
- if self.isDate():
- return (other.isPrimitive() or other.isString() or other.isEnum() or
- other.isInterface() or other.isCallback() or
- other.isDictionary() or other.isSequence() or
- other.isMozMap())
if self.isVoid():
return not other.isVoid()
# Not much else we could be!
@@ -3052,12 +3463,12 @@ class IDLBuiltinType(IDLType):
# Like interfaces, but we know we're not a callback
return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isMozMap() or other.isDate() 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.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) 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
@@ -3071,6 +3482,54 @@ class IDLBuiltinType(IDLType):
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 == "TreatNullAs":
+ if not (self.isDOMString() or self.isUTF8String()):
+ raise WebIDLError("[TreatNullAs] only allowed on DOMStrings and UTF8Strings",
+ [self.location, attribute.location])
+ assert not self.nullable()
+ if not attribute.hasValue():
+ raise WebIDLError("[TreatNullAs] must take an identifier argument",
+ [attribute.location])
+ value = attribute.value()
+ if value != 'EmptyString':
+ raise WebIDLError("[TreatNullAs] must take the identifier "
+ "'EmptyString', not '%s'" % value,
+ [attribute.location])
+ ret = self.withTreatNullAs([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",
@@ -3123,12 +3582,15 @@ BuiltinTypes = {
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.date:
- IDLBuiltinType(BuiltinLocation("<builtin type>"), "Date",
- IDLBuiltinType.Types.date),
IDLBuiltinType.Types.void:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Void",
IDLBuiltinType.Types.void),
@@ -3138,9 +3600,6 @@ BuiltinTypes = {
IDLBuiltinType.Types.ArrayBufferView:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "ArrayBufferView",
IDLBuiltinType.Types.ArrayBufferView),
- IDLBuiltinType.Types.SharedArrayBuffer:
- IDLBuiltinType(BuiltinLocation("<builtin type>"), "SharedArrayBuffer",
- IDLBuiltinType.Types.SharedArrayBuffer),
IDLBuiltinType.Types.Int8Array:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int8Array",
IDLBuiltinType.Types.Int8Array),
@@ -3167,7 +3626,10 @@ BuiltinTypes = {
IDLBuiltinType.Types.Float32Array),
IDLBuiltinType.Types.Float64Array:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float64Array",
- IDLBuiltinType.Types.Float64Array)
+ IDLBuiltinType.Types.Float64Array),
+ IDLBuiltinType.Types.ReadableStream:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "ReadableStream",
+ IDLBuiltinType.Types.ReadableStream),
}
@@ -3184,7 +3646,7 @@ integerTypeSizes = {
def matchIntegerValueToType(value):
- for type, extremes in integerTypeSizes.items():
+ for type, extremes in list(integerTypeSizes.items()):
(min, max) = extremes
if value <= max and value >= min:
return BuiltinTypes[type]
@@ -3263,7 +3725,7 @@ class IDLValue(IDLObject):
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():
+ if self.value not in list(enum.values()):
raise WebIDLError("'%s' is not a valid default value for enum %s"
% (self.value, enum.identifier.name),
[location, enum.location])
@@ -3282,8 +3744,13 @@ class IDLValue(IDLObject):
# extra normalization step.
assert self.type.isDOMString()
return self
- elif self.type.isString() and type.isByteString():
- # Allow ByteStrings to use a default value like DOMString.
+ elif self.type.isDOMString() and type.treatNullAsEmpty:
+ # TreatNullAsEmpty is a different type for resolution reasons,
+ # however once you have a value it doesn't matter
+ 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.
@@ -3312,8 +3779,6 @@ class IDLNullValue(IDLObject):
def coerceToType(self, type, location):
if (not isinstance(type, IDLNullableType) and
not (type.isUnion() and type.hasNullableType) and
- not (type.isUnion() and type.hasDictionaryType()) and
- not type.isDictionary() and
not type.isAny()):
raise WebIDLError("Cannot coerce null value to type %s." % type,
[location])
@@ -3362,6 +3827,35 @@ class IDLEmptySequenceValue(IDLObject):
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)
@@ -3437,10 +3931,6 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
return self._extendedAttrDict.get(name, None)
def finish(self, scope):
- # We better be exposed _somewhere_.
- if (len(self._exposureGlobalNames) == 0):
- print self.identifier.name
- assert len(self._exposureGlobalNames) != 0
IDLExposureMixins.finish(self, scope)
def validate(self):
@@ -3456,6 +3946,14 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
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":
@@ -3483,6 +3981,11 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
[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):
@@ -3527,8 +4030,10 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
(member.identifier.name,
self.maplikeOrSetlikeOrIterableType),
[self.location, member.location])
- # Check that there are no disallowed non-method members
- if (isAncestor or (member.isAttr() or member.isConst()) and
+ # 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." %
@@ -3601,6 +4106,11 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
if isIteratorAlias:
method.addExtendedAttributes(
[IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))])
+ # Methods generated for iterables should be enumerable, but the ones for
+ # maplike/setlike should not be.
+ if not self.isIterable():
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("NonEnumerable",))])
members.append(method)
def resolve(self, parentScope):
@@ -3724,12 +4234,17 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
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))
+ sizeAttr = IDLAttribute(self.location,
+ IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
+ BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
+ True,
+ maplikeOrSetlike=self)
+ # This should be non-enumerable.
+ sizeAttr.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("NonEnumerable",))])
+ members.append(sizeAttr)
self.reserved_ro_names = ["size"]
+ self.disallowedMemberNames.append("size")
# object entries()
self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
@@ -3820,6 +4335,9 @@ class IDLConst(IDLInterfaceMember):
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
@@ -3860,7 +4378,9 @@ class IDLConst(IDLInterfaceMember):
elif (identifier == "Pref" or
identifier == "ChromeOnly" or
identifier == "Func" or
- identifier == "SecureContext"):
+ identifier == "SecureContext" or
+ identifier == "NonEnumerable" or
+ identifier == "NeedsWindowsUndef"):
# Known attributes that we don't need to do anything with here
pass
else:
@@ -3875,7 +4395,7 @@ class IDLConst(IDLInterfaceMember):
class IDLAttribute(IDLInterfaceMember):
def __init__(self, location, identifier, type, readonly, inherit=False,
static=False, stringifier=False, maplikeOrSetlike=None,
- extendedAttrDict=None, navigatorObjectGetter=False):
+ extendedAttrDict=None):
IDLInterfaceMember.__init__(self, location, identifier,
IDLInterfaceMember.Tags.Attr,
extendedAttrDict=extendedAttrDict)
@@ -3888,14 +4408,12 @@ class IDLAttribute(IDLInterfaceMember):
self.lenientThis = False
self._unforgeable = False
self.stringifier = stringifier
- self.enforceRange = False
- self.clamp = False
self.slotIndices = None
assert maplikeOrSetlike is None or isinstance(maplikeOrSetlike, IDLMaplikeOrSetlike)
self.maplikeOrSetlike = maplikeOrSetlike
self.dependsOn = "Everything"
self.affects = "Everything"
- self.navigatorObjectGetter = navigatorObjectGetter
+ self.bindingAliases = []
if static and identifier.name == "prototype":
raise WebIDLError("The identifier of a static attribute must not be 'prototype'",
@@ -3925,14 +4443,18 @@ class IDLAttribute(IDLInterfaceMember):
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.treatNullAsEmpty):
+ 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.isMozMap() and not self.getExtendedAttribute("Cached"):
- raise WebIDLError("A non-cached attribute cannot be of a MozMap "
+ 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:
@@ -3948,25 +4470,30 @@ class IDLAttribute(IDLInterfaceMember):
"one of its member types's member "
"types, and so on) is a sequence "
"type", [self.location, f.location])
- if f.isMozMap():
+ 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 MozMap "
+ "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"):
+ 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])
+
def validate(self):
def typeContainsChromeOnlyDictionaryMember(type):
if (type.nullable() or
type.isSequence() or
- type.isMozMap()):
+ type.isRecord()):
return typeContainsChromeOnlyDictionaryMember(type.inner)
if type.isUnion():
@@ -4012,27 +4539,42 @@ class IDLAttribute(IDLInterfaceMember):
[self.location, location])
if self.getExtendedAttribute("Frozen"):
if (not self.type.isSequence() and not self.type.isDictionary() and
- not self.type.isMozMap()):
+ not self.type.isRecord()):
raise WebIDLError("[Frozen] is only allowed on "
"sequence-valued, dictionary-valued, and "
- "MozMap-valued attributes",
+ "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" and self.readonly:
+ if ((identifier == "SetterThrows" or identifier == "SetterCanOOM" or
+ identifier == "SetterNeedsSubjectPrincipal")
+ and self.readonly):
raise WebIDLError("Readonly attributes must not be flagged as "
- "[SetterThrows]",
+ "[%s]" % identifier,
[self.location])
- elif (((identifier == "Throws" or identifier == "GetterThrows") and
+ 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")))):
+ self.getExtendedAttribute("GetterThrows") or
+ self.getExtendedAttribute("CanOOM") or
+ self.getExtendedAttribute("GetterCanOOM")))):
raise WebIDLError("Throwing things can't be [StoreInSlot]",
[attr.location])
elif identifier == "LenientThis":
@@ -4066,6 +4608,10 @@ class IDLAttribute(IDLInterfaceMember):
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])
@@ -4083,6 +4629,10 @@ class IDLAttribute(IDLInterfaceMember):
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])
@@ -4097,6 +4647,10 @@ class IDLAttribute(IDLInterfaceMember):
if not self.readonly:
raise WebIDLError("[LenientSetter] is only allowed on readonly "
"attributes", [attr.location, self.location])
+ if self.type.isPromise():
+ raise WebIDLError("[LenientSetter] is not allowed on "
+ "Promise-typed attributes",
+ [attr.location, self.location])
if self.isStatic():
raise WebIDLError("[LenientSetter] is only allowed on non-static "
"attributes", [attr.location, self.location])
@@ -4116,16 +4670,6 @@ class IDLAttribute(IDLInterfaceMember):
raise WebIDLError("[LenientFloat] used on an attribute with a "
"non-restricted-float type",
[attr.location, self.location])
- elif identifier == "EnforceRange":
- if self.readonly:
- raise WebIDLError("[EnforceRange] used on a readonly attribute",
- [attr.location, self.location])
- self.enforceRange = True
- elif identifier == "Clamp":
- if self.readonly:
- raise WebIDLError("[Clamp] used on a readonly attribute",
- [attr.location, self.location])
- self.clamp = True
elif identifier == "StoreInSlot":
if self.getExtendedAttribute("Cached"):
raise WebIDLError("[StoreInSlot] and [Cached] must not be "
@@ -4138,7 +4682,7 @@ class IDLAttribute(IDLInterfaceMember):
[attr.location, self.location])
elif (identifier == "CrossOriginReadable" or
identifier == "CrossOriginWritable"):
- if not attr.noArguments() and identifier == "CrossOriginReadable":
+ if not attr.noArguments():
raise WebIDLError("[%s] must take no arguments" % identifier,
[attr.location])
if self.isStatic():
@@ -4191,18 +4735,30 @@ class IDLAttribute(IDLInterfaceMember):
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 == "SecureContext" or
identifier == "Frozen" or
identifier == "NewObject" or
- identifier == "UnsafeInPrerendering" or
- identifier == "BinaryName"):
+ 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:
@@ -4215,10 +4771,6 @@ class IDLAttribute(IDLInterfaceMember):
self.type.resolveType(parentScope)
IDLObjectWithIdentifier.resolve(self, parentScope)
- def addExtendedAttributes(self, attrs):
- attrs = self.checkForStringHandlingExtendedAttributes(attrs)
- IDLInterfaceMember.addExtendedAttributes(self, attrs)
-
def hasLenientThis(self):
return self.lenientThis
@@ -4236,9 +4788,43 @@ class IDLAttribute(IDLInterfaceMember):
def _getDependentObjects(self):
return set([self.type])
+ def expand(self, members):
+ assert self.stringifier
+ if not self.type.isDOMString() and not self.type.isUSVString():
+ raise WebIDLError("The type of a stringifer attribute must be "
+ "either DOMString or USVString",
+ [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 list(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):
+ 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)
@@ -4249,41 +4835,25 @@ class IDLArgument(IDLObjectWithIdentifier):
self.variadic = variadic
self.dictionaryMember = dictionaryMember
self._isComplete = False
- self.enforceRange = False
- self.clamp = False
self._allowTreatNonCallableAsNull = False
self._extendedAttrDict = {}
+ self.allowTypeAttributes = allowTypeAttributes
assert not variadic or optional
assert not variadic or not defaultValue
def addExtendedAttributes(self, attrs):
- attrs = self.checkForStringHandlingExtendedAttributes(
- attrs,
- isDictionaryMember=self.dictionaryMember,
- isOptional=self.optional)
for attribute in attrs:
identifier = attribute.identifier()
- if identifier == "Clamp":
- if not attribute.noArguments():
- raise WebIDLError("[Clamp] must take no arguments",
- [attribute.location])
- if self.enforceRange:
- raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
- [self.location])
- self.clamp = True
- elif identifier == "EnforceRange":
- if not attribute.noArguments():
- raise WebIDLError("[EnforceRange] must take no arguments",
- [attribute.location])
- if self.clamp:
- raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
- [self.location])
- self.enforceRange = True
+ if self.allowTypeAttributes and (identifier == "EnforceRange" or identifier == "Clamp" or
+ identifier == "TreatNullAs" or identifier == "AllowShared"):
+ self.type = self.type.withExtendedAttributes([attribute])
elif identifier == "TreatNonCallableAsNull":
self._allowTreatNonCallableAsNull = True
elif (self.dictionaryMember and
- (identifier == "ChromeOnly" or identifier == "Func")):
+ (identifier == "ChromeOnly" or
+ identifier == "Func" or
+ identifier == "Pref")):
if not self.optional:
raise WebIDLError("[%s] must not be used on a required "
"dictionary member" % identifier,
@@ -4315,13 +4885,7 @@ class IDLArgument(IDLObjectWithIdentifier):
assert not isinstance(type.name, IDLUnresolvedIdentifier)
self.type = type
- if ((self.type.isDictionary() or
- self.type.isUnion() and self.type.unroll().hasDictionaryType()) and
- self.optional and not self.defaultValue and not self.variadic):
- # Default optional non-variadic dictionaries to null,
- # for simplicity, so the codegen doesn't have to special-case this.
- self.defaultValue = IDLNullValue(self.location)
- elif self.type.isAny():
+ if self.type.isAny():
assert (self.defaultValue is None or
isinstance(self.defaultValue, IDLNullValue))
# optional 'any' values always have a default value
@@ -4330,6 +4894,8 @@ class IDLArgument(IDLObjectWithIdentifier):
# codegen doesn't have to special-case this.
self.defaultValue = IDLUndefinedValue(self.location)
+ if self.dictionaryMember and self.type.treatNullAsEmpty:
+ raise WebIDLError("Dictionary members cannot be [TreatNullAs]", [self.location])
# Now do the coercing thing; this needs to happen after the
# above creation of a default value.
if self.defaultValue:
@@ -4351,7 +4917,7 @@ class IDLArgument(IDLObjectWithIdentifier):
class IDLCallback(IDLObjectWithScope):
- def __init__(self, location, parentScope, identifier, returnType, arguments):
+ def __init__(self, location, parentScope, identifier, returnType, arguments, isConstructor):
assert isinstance(returnType, IDLType)
self._returnType = returnType
@@ -4366,10 +4932,15 @@ class IDLCallback(IDLObjectWithScope):
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)]
@@ -4402,7 +4973,16 @@ class IDLCallback(IDLObjectWithScope):
if attr.identifier() == "TreatNonCallableAsNull":
self._treatNonCallableAsNull = True
elif attr.identifier() == "TreatNonObjectAsNull":
+ if self._isConstructor:
+ raise WebIDLError("[TreatNonObjectAsNull] 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:
@@ -4414,6 +4994,9 @@ class IDLCallback(IDLObjectWithScope):
def _getDependentObjects(self):
return set([self._returnType] + self._arguments)
+ def isRunScriptBoundary(self):
+ return self._isRunScriptBoundary;
+
class IDLCallbackType(IDLType):
def __init__(self, location, callback):
@@ -4433,8 +5016,7 @@ class IDLCallbackType(IDLType):
# 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.isDate() or
- other.isSequence())
+ other.isNonCallbackInterface() or other.isSequence())
def _getDependentObjects(self):
return self.callback._getDependentObjects()
@@ -4460,13 +5042,15 @@ class IDLMethodOverload:
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',
- 'Creator',
'Deleter',
'LegacyCaller',
base=IDLInterfaceMember.Special
@@ -4479,10 +5063,11 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
)
def __init__(self, location, identifier, returnType, arguments,
- static=False, getter=False, setter=False, creator=False,
+ static=False, getter=False, setter=False,
deleter=False, specialType=NamedOrIndexed.Neither,
- legacycaller=False, stringifier=False, jsonifier=False,
- maplikeOrSetlikeOrIterable=None):
+ 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)
@@ -4500,18 +5085,16 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
self._getter = getter
assert isinstance(setter, bool)
self._setter = setter
- assert isinstance(creator, bool)
- self._creator = creator
assert isinstance(deleter, bool)
self._deleter = deleter
assert isinstance(legacycaller, bool)
self._legacycaller = legacycaller
assert isinstance(stringifier, bool)
self._stringifier = stringifier
- assert isinstance(jsonifier, bool)
- self._jsonifier = jsonifier
assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase)
self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable
+ self._htmlConstructor = False
+ self.underlyingAttr = underlyingAttr
self._specialType = specialType
self._unforgeable = False
self.dependsOn = "Everything"
@@ -4538,7 +5121,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
assert not arguments[0].optional and not arguments[0].variadic
assert not self._getter or not overload.returnType.isVoid()
- if self._setter or self._creator:
+ if self._setter:
assert len(self._overloads) == 1
arguments = self._overloads[0].arguments
assert len(arguments) == 2
@@ -4551,13 +5134,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
assert len(self._overloads) == 1
overload = self._overloads[0]
assert len(overload.arguments) == 0
- assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring]
-
- if self._jsonifier:
- assert len(self._overloads) == 1
- overload = self._overloads[0]
- assert len(overload.arguments) == 0
- assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.object]
+ if not self.underlyingAttr:
+ assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring]
def isStatic(self):
return self._static
@@ -4571,9 +5149,6 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def isSetter(self):
return self._setter
- def isCreator(self):
- return self._creator
-
def isDeleter(self):
return self._deleter
@@ -4593,8 +5168,11 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def isStringifier(self):
return self._stringifier
- def isJsonifier(self):
- return self._jsonifier
+ def isToJSON(self):
+ return self.identifier.name == "toJSON"
+
+ def isDefaultToJSON(self):
+ return self.isToJSON() and self.getExtendedAttribute("Default")
def isMaplikeOrSetlikeOrIterableMethod(self):
"""
@@ -4606,11 +5184,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def isSpecial(self):
return (self.isGetter() or
self.isSetter() or
- self.isCreator() or
self.isDeleter() or
self.isLegacycaller() or
- self.isStringifier() or
- self.isJsonifier())
+ self.isStringifier())
+
+ def isHTMLConstructor(self):
+ return self._htmlConstructor
def hasOverloads(self):
return self._hasOverloads
@@ -4637,10 +5216,25 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def addOverload(self, method):
assert len(method._overloads) == 1
- if self._extendedAttrDict != method ._extendedAttrDict:
- raise WebIDLError("Extended attributes differ on different "
- "overloads of %s" % method.identifier,
- [self.location, method.location])
+ 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)
@@ -4659,14 +5253,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
assert not method.isGetter()
assert not self.isSetter()
assert not method.isSetter()
- assert not self.isCreator()
- assert not method.isCreator()
assert not self.isDeleter()
assert not method.isDeleter()
assert not self.isStringifier()
assert not method.isStringifier()
- assert not self.isJsonifier()
- assert not method.isJsonifier()
+ assert not self.isHTMLConstructor()
+ assert not method.isHTMLConstructor()
return self
@@ -4735,7 +5327,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
assert argument.type.isComplete()
if ((argument.type.isDictionary() and
- argument.type.inner.canBeEmpty())or
+ argument.type.unroll().inner.canBeEmpty()) or
(argument.type.isUnion() and
argument.type.unroll().hasPossiblyEmptyDictionaryType())):
# Optional dictionaries and unions containing optional
@@ -4743,19 +5335,33 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
# optional arguments must be optional.
if (not argument.optional and
all(arg.optional for arg in arguments[idx+1:])):
- raise WebIDLError("Dictionary argument or union "
- "argument containing a dictionary "
- "not followed by a required argument "
+ 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])
- # An argument cannot be a Nullable Dictionary
- if argument.type.nullable():
- raise WebIDLError("An argument cannot be a nullable "
- "dictionary or nullable union "
- "containing a dictionary",
+ 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",
@@ -4786,6 +5392,19 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
" 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
@@ -4831,13 +5450,14 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def handleExtendedAttribute(self, attr):
identifier = attr.identifier()
- if identifier == "GetterThrows":
+ 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 "
- "[GetterThrows]",
- [attr.location, self.location])
- elif identifier == "SetterThrows":
- raise WebIDLError("Methods must not be flagged as "
- "[SetterThrows]",
+ "[%s]" % identifier,
[attr.location, self.location])
elif identifier == "Unforgeable":
if self.isStatic():
@@ -4858,12 +5478,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
[attr.location, self.location])
elif identifier == "LenientFloat":
# This is called before we've done overload resolution
- assert len(self.signatures()) == 1
- sig = self.signatures()[0]
- if not sig[0].isVoid():
+ overloads = self._overloads
+ assert len(overloads) == 1
+ if not overloads[0].returnType.isVoid():
raise WebIDLError("[LenientFloat] used on a non-void method",
[attr.location, self.location])
- if not any(arg.type.includesRestrictedFloat() for arg in sig[1]):
+ if not overloads[0].includesRestrictedFloatArgument():
raise WebIDLError("[LenientFloat] used on an operation with no "
"restricted float type arguments",
[attr.location, self.location])
@@ -4875,6 +5495,10 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
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",
@@ -4909,16 +5533,42 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
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 == "UnsafeInPrerendering" or
identifier == "Pref" or
identifier == "Deprecated" or
identifier == "Func" or
identifier == "SecureContext" or
identifier == "BinaryName" or
- identifier == "StaticClassOverride"):
+ identifier == "NeedsSubjectPrincipal" or
+ identifier == "NeedsCallerType" or
+ identifier == "StaticClassOverride" or
+ identifier == "NonEnumerable" or
+ identifier == "Unexposed"):
# Known attributes that we don't need to do anything with here
pass
else:
@@ -4939,49 +5589,110 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
return deps
-class IDLImplementsStatement(IDLObject):
- def __init__(self, location, implementor, implementee):
+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 == "Pref"):
+ 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 named constructors.
+ 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.implementor = implementor
- self.implementee = implementee
+ self.interface = interface
+ self.mixin = mixin
self._finished = False
def finish(self, scope):
if self._finished:
return
- assert(isinstance(self.implementor, IDLIdentifierPlaceholder))
- assert(isinstance(self.implementee, IDLIdentifierPlaceholder))
- implementor = self.implementor.finish(scope)
- implementee = self.implementee.finish(scope)
- # NOTE: we depend on not setting self.implementor and
- # self.implementee here to keep track of the original
+ 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(implementor, IDLInterface):
- raise WebIDLError("Left-hand side of 'implements' is not an "
- "interface",
- [self.implementor.location])
- if implementor.isCallback():
- raise WebIDLError("Left-hand side of 'implements' is a callback "
- "interface",
- [self.implementor.location])
- if not isinstance(implementee, IDLInterface):
- raise WebIDLError("Right-hand side of 'implements' is not an "
+ if not isinstance(interface, IDLInterface):
+ raise WebIDLError("Left-hand side of 'includes' is not an "
"interface",
- [self.implementee.location])
- if implementee.isCallback():
- raise WebIDLError("Right-hand side of 'implements' is a callback "
+ [self.interface.location, interface.location])
+ if interface.isCallback():
+ raise WebIDLError("Left-hand side of 'includes' is a callback "
"interface",
- [self.implementee.location])
- implementor.addImplementedInterface(implementee)
- self.implementor = implementor
- self.implementee = implementee
+ [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):
- assert len(attrs) == 0
-
+ 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):
"""
@@ -5028,6 +5739,7 @@ class Tokenizer(object):
"FLOATLITERAL",
"IDENTIFIER",
"STRING",
+ "COMMENTS",
"WHITESPACE",
"OTHER"
]
@@ -5051,7 +5763,7 @@ class Tokenizer(object):
return t
def t_IDENTIFIER(self, t):
- r'[A-Z_a-z][0-9A-Z_a-z-]*'
+ r'[_-]?[A-Za-z][0-9A-Z_a-z-]*'
t.type = self.keywords.get(t.value, 'IDENTIFIER')
return t
@@ -5060,8 +5772,12 @@ class Tokenizer(object):
t.value = t.value[1:-1]
return t
+ def t_COMMENTS(self, t):
+ r'(\/\*(.|\n)*?\*\/)|(\/\/.*)'
+ pass
+
def t_WHITESPACE(self, t):
- r'[\t\n\r ]+|[\t\n\r ]*((//[^\n]*|/\*.*?\*/)[\t\n\r ]*)+'
+ r'[\t\n\r ]+'
pass
def t_ELLIPSIS(self, t):
@@ -5075,22 +5791,21 @@ class Tokenizer(object):
return t
keywords = {
- "module": "MODULE",
"interface": "INTERFACE",
"partial": "PARTIAL",
+ "mixin": "MIXIN",
"dictionary": "DICTIONARY",
"exception": "EXCEPTION",
"enum": "ENUM",
"callback": "CALLBACK",
"typedef": "TYPEDEF",
- "implements": "IMPLEMENTS",
+ "includes": "INCLUDES",
"const": "CONST",
"null": "NULL",
"true": "TRUE",
"false": "FALSE",
"serializer": "SERIALIZER",
"stringifier": "STRINGIFIER",
- "jsonifier": "JSONIFIER",
"unrestricted": "UNRESTRICTED",
"attribute": "ATTRIBUTE",
"readonly": "READONLY",
@@ -5098,16 +5813,16 @@ class Tokenizer(object):
"static": "STATIC",
"getter": "GETTER",
"setter": "SETTER",
- "creator": "CREATOR",
"deleter": "DELETER",
"legacycaller": "LEGACYCALLER",
"optional": "OPTIONAL",
"...": "ELLIPSIS",
"::": "SCOPE",
- "Date": "DATE",
"DOMString": "DOMSTRING",
"ByteString": "BYTESTRING",
"USVString": "USVSTRING",
+ "JSString": "JSSTRING",
+ "UTF8String": "UTF8STRING",
"any": "ANY",
"boolean": "BOOLEAN",
"byte": "BYTE",
@@ -5119,7 +5834,7 @@ class Tokenizer(object):
"Promise": "PROMISE",
"required": "REQUIRED",
"sequence": "SEQUENCE",
- "MozMap": "MOZMAP",
+ "record": "RECORD",
"short": "SHORT",
"unsigned": "UNSIGNED",
"void": "VOID",
@@ -5137,15 +5852,18 @@ class Tokenizer(object):
"<": "LT",
">": "GT",
"ArrayBuffer": "ARRAYBUFFER",
- "SharedArrayBuffer": "SHAREDARRAYBUFFER",
"or": "OR",
"maplike": "MAPLIKE",
"setlike": "SETLIKE",
"iterable": "ITERABLE",
- "namespace": "NAMESPACE"
+ "namespace": "NAMESPACE",
+ "ReadableStream": "READABLESTREAM",
+ "constructor": "CONSTRUCTOR",
+ "symbol": "SYMBOL",
+ "async": "ASYNC",
}
- tokens.extend(keywords.values())
+ tokens.extend(list(keywords.values()))
def t_error(self, t):
raise WebIDLError("Unrecognized Input",
@@ -5154,23 +5872,21 @@ class Tokenizer(object):
lexpos=self.lexer.lexpos,
filename=self.filename)])
- def __init__(self, outputdir, lexer=None):
+ def __init__(self, lexer=None):
if lexer:
self.lexer = lexer
else:
- self.lexer = lex.lex(object=self,
- outputdir=outputdir,
- lextab='webidllex',
- reflags=re.DOTALL)
+ self.lexer = lex.lex(object=self)
class SqueakyCleanLogger(object):
errorWhitelist = [
- # Web IDL defines the WHITESPACE token, but doesn't actually
+ # Web IDL defines the WHITESPACE and COMMENTS 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",
+ "Token 'COMMENTS' defined, but not used",
+ # And that means we have unused tokens
+ "There are 2 unused tokens",
# Web IDL defines a OtherOrComma rule that's only used in
# ExtendedAttributeInner, which we don't use yet.
"Rule 'OtherOrComma' defined, but not used",
@@ -5238,21 +5954,21 @@ class Parser(Tokenizer):
def p_Definition(self, p):
"""
- Definition : CallbackOrInterface
+ Definition : CallbackOrInterfaceOrMixin
| Namespace
| Partial
| Dictionary
| Exception
| Enum
| Typedef
- | ImplementsStatement
+ | IncludesStatement
"""
p[0] = p[1]
assert p[1] # We might not have implemented something ...
- def p_CallbackOrInterfaceCallback(self, p):
+ def p_CallbackOrInterfaceOrMixinCallback(self, p):
"""
- CallbackOrInterface : CALLBACK CallbackRestOrInterface
+ CallbackOrInterfaceOrMixin : CALLBACK CallbackRestOrInterface
"""
if p[2].isInterface():
assert isinstance(p[2], IDLInterface)
@@ -5260,16 +5976,17 @@ class Parser(Tokenizer):
p[0] = p[2]
- def p_CallbackOrInterfaceInterface(self, p):
+ def p_CallbackOrInterfaceOrMixinInterfaceOrMixin(self, p):
"""
- CallbackOrInterface : Interface
+ CallbackOrInterfaceOrMixin : INTERFACE InterfaceOrMixin
"""
- p[0] = p[1]
+ p[0] = p[2]
def p_CallbackRestOrInterface(self, p):
"""
CallbackRestOrInterface : CallbackRest
- | Interface
+ | CallbackConstructorRest
+ | CallbackInterface
"""
assert p[1]
p[0] = p[1]
@@ -5277,9 +5994,10 @@ class Parser(Tokenizer):
def handleNonPartialObject(self, location, identifier, constructor,
constructorArgs, nonPartialArgs):
"""
- This handles non-partial objects (interfaces and namespaces) by
- checking for an existing partial object, and promoting it to
- non-partial as needed. The return value is the non-partial object.
+ 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.
@@ -5301,7 +6019,7 @@ class Parser(Tokenizer):
[location, existingObj.location])
existingObj.setNonPartial(*nonPartialArgs)
return existingObj
- except Exception, ex:
+ except Exception as ex:
if isinstance(ex, WebIDLError):
raise ex
pass
@@ -5309,14 +6027,27 @@ class Parser(Tokenizer):
# True for isKnownNonPartial
return constructor(*(constructorArgs + [True]))
- def p_Interface(self, p):
+ def p_InterfaceOrMixin(self, p):
+ """
+ InterfaceOrMixin : InterfaceRest
+ | MixinRest
+ """
+ p[0] = p[1]
+
+ def p_CallbackInterface(self, p):
+ """
+ CallbackInterface : INTERFACE InterfaceRest
"""
- Interface : INTERFACE IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON
+ p[0] = p[2]
+
+ def p_InterfaceRest(self, p):
+ """
+ InterfaceRest : IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON
"""
location = self.getLocation(p, 1)
- identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
- members = p[5]
- parent = p[3]
+ identifier = IDLUnresolvedIdentifier(location, p[1])
+ members = p[4]
+ parent = p[2]
p[0] = self.handleNonPartialObject(
location, identifier, IDLInterface,
@@ -5325,10 +6056,10 @@ class Parser(Tokenizer):
def p_InterfaceForwardDecl(self, p):
"""
- Interface : INTERFACE IDENTIFIER SEMICOLON
+ InterfaceRest : IDENTIFIER SEMICOLON
"""
location = self.getLocation(p, 1)
- identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
+ identifier = IDLUnresolvedIdentifier(location, p[1])
try:
if self.globalScope()._lookupIdentifier(identifier):
@@ -5339,13 +6070,26 @@ class Parser(Tokenizer):
"%s and %s" % (identifier.name, p[0]),
[location, p[0].location])
return
- except Exception, ex:
+ 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
@@ -5365,10 +6109,16 @@ class Parser(Tokenizer):
"""
p[0] = p[2]
+ def p_PartialDefinitionInterface(self, p):
+ """
+ PartialDefinition : INTERFACE PartialInterfaceOrPartialMixin
+ """
+ p[0] = p[2]
+
def p_PartialDefinition(self, p):
"""
- PartialDefinition : PartialInterface
- | PartialNamespace
+ PartialDefinition : PartialNamespace
+ | PartialDictionary
"""
p[0] = p[1]
@@ -5376,17 +6126,17 @@ class Parser(Tokenizer):
nonPartialConstructorArgs,
partialConstructorArgs):
"""
- This handles partial objects (interfaces and namespaces) by checking for
- an existing non-partial object, and adding ourselves to it as needed.
- The return value is our partial object. For now we just use
- IDLPartialInterfaceOrNamespace for partial objects.
+ 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
- IDLPartialInterfaceOrNamespace constructor, except the last one (the
- non-partial object).
+ 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
@@ -5402,7 +6152,7 @@ class Parser(Tokenizer):
"non-%s object" %
(prettyname, prettyname),
[location, nonPartialObject.location])
- except Exception, ex:
+ except Exception as ex:
if isinstance(ex, WebIDLError):
raise ex
pass
@@ -5410,24 +6160,55 @@ class Parser(Tokenizer):
if not nonPartialObject:
nonPartialObject = nonPartialConstructor(
# No members, False for isKnownNonPartial
- *(nonPartialConstructorArgs + [[], False]))
- partialInterface = IDLPartialInterfaceOrNamespace(
- *(partialConstructorArgs + [nonPartialObject]))
- return partialInterface
+ *(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_PartialInterface(self, p):
+ def p_PartialInterfaceOrPartialMixin(self, p):
"""
- PartialInterface : INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+ 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(self.getLocation(p, 2), p[2])
- members = p[4]
+ 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
@@ -5441,6 +6222,19 @@ class Parser(Tokenizer):
[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
@@ -5457,7 +6251,7 @@ class Parser(Tokenizer):
"""
InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
"""
- p[0] = [p[2]] if p[2] else []
+ p[0] = [p[2]]
assert not p[1] or p[2]
p[2].addExtendedAttributes(p[1])
@@ -5472,8 +6266,64 @@ class Parser(Tokenizer):
def p_InterfaceMember(self, p):
"""
- InterfaceMember : Const
- | AttributeOrOperationOrMaplikeOrSetlikeOrIterable
+ 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]
@@ -5495,31 +6345,42 @@ class Parser(Tokenizer):
# We're at the end of the list
p[0] = []
return
- # Add our extended attributes
p[2].addExtendedAttributes(p[1])
p[0] = [p[2]]
p[0].extend(p[3])
- def p_DictionaryMember(self, p):
+ def p_DictionaryMemberRequired(self, p):
"""
- DictionaryMember : Required Type IDENTIFIER Default SEMICOLON
+ DictionaryMember : REQUIRED TypeWithExtendedAttributes IDENTIFIER SEMICOLON
"""
- # These quack a lot like optional arguments, so just treat them that way.
+ # 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])
- defaultValue = p[4]
- optional = not p[1]
-
- if not optional and defaultValue:
- raise WebIDLError("Required dictionary members can't have a default value.",
- [self.getLocation(p, 4)])
p[0] = IDLArgument(self.getLocation(p, 3), identifier, t,
- optional=optional,
- defaultValue=defaultValue, variadic=False,
+ 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
@@ -5534,12 +6395,23 @@ class Parser(Tokenizer):
"""
DefaultValue : ConstValue
| LBRACKET RBRACKET
+ | LBRACE RBRACE
"""
if len(p) == 2:
p[0] = p[1]
else:
- assert len(p) == 3 # Must be []
- p[0] = IDLEmptySequenceValue(self.getLocation(p, 1))
+ 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_Exception(self, p):
"""
@@ -5596,7 +6468,15 @@ class Parser(Tokenizer):
"""
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
p[0] = IDLCallback(self.getLocation(p, 1), self.globalScope(),
- identifier, p[3], p[5])
+ identifier, p[3], p[5], isConstructor=False)
+
+ def p_CallbackConstructorRest(self, p):
+ """
+ CallbackConstructorRest : CONSTRUCTOR IDENTIFIER EQUALS ReturnType 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):
"""
@@ -5607,21 +6487,20 @@ class Parser(Tokenizer):
def p_Typedef(self, p):
"""
- Typedef : TYPEDEF Type IDENTIFIER SEMICOLON
+ Typedef : TYPEDEF TypeWithExtendedAttributes IDENTIFIER SEMICOLON
"""
typedef = IDLTypedef(self.getLocation(p, 1), self.globalScope(),
p[2], p[3])
p[0] = typedef
- def p_ImplementsStatement(self, p):
+ def p_IncludesStatement(self, p):
"""
- ImplementsStatement : ScopedName IMPLEMENTS ScopedName SEMICOLON
+ IncludesStatement : ScopedName INCLUDES ScopedName SEMICOLON
"""
- assert(p[2] == "implements")
- implementor = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1])
- implementee = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3])
- p[0] = IDLImplementsStatement(self.getLocation(p, 1), implementor,
- implementee)
+ 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):
"""
@@ -5670,12 +6549,6 @@ class Parser(Tokenizer):
stringType = BuiltinTypes[IDLBuiltinType.Types.domstring]
p[0] = IDLValue(location, stringType, p[1])
- def p_ConstValueNull(self, p):
- """
- ConstValue : NULL
- """
- p[0] = IDLNullValue(self.getLocation(p, 1))
-
def p_BooleanLiteralTrue(self, p):
"""
BooleanLiteral : TRUE
@@ -5700,8 +6573,8 @@ class Parser(Tokenizer):
def p_Iterable(self, p):
"""
- Iterable : ITERABLE LT Type GT SEMICOLON
- | ITERABLE LT Type COMMA Type GT SEMICOLON
+ Iterable : ITERABLE LT TypeWithExtendedAttributes GT SEMICOLON
+ | ITERABLE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON
"""
location = self.getLocation(p, 2)
identifier = IDLUnresolvedIdentifier(location, "__iterable",
@@ -5717,7 +6590,7 @@ class Parser(Tokenizer):
def p_Setlike(self, p):
"""
- Setlike : ReadOnly SETLIKE LT Type GT SEMICOLON
+ Setlike : ReadOnly SETLIKE LT TypeWithExtendedAttributes GT SEMICOLON
"""
readonly = p[1]
maplikeOrSetlikeType = p[2]
@@ -5731,7 +6604,7 @@ class Parser(Tokenizer):
def p_Maplike(self, p):
"""
- Maplike : ReadOnly MAPLIKE LT Type COMMA Type GT SEMICOLON
+ Maplike : ReadOnly MAPLIKE LT TypeWithExtendedAttributes COMMA TypeWithExtendedAttributes GT SEMICOLON
"""
readonly = p[1]
maplikeOrSetlikeType = p[2]
@@ -5769,7 +6642,7 @@ class Parser(Tokenizer):
def p_AttributeRest(self, p):
"""
- AttributeRest : ReadOnly ATTRIBUTE Type AttributeName SEMICOLON
+ AttributeRest : ReadOnly ATTRIBUTE TypeWithExtendedAttributes AttributeName SEMICOLON
"""
location = self.getLocation(p, 2)
readonly = p[1]
@@ -5812,13 +6685,12 @@ class Parser(Tokenizer):
getter = True if IDLMethod.Special.Getter in p[1] else False
setter = True if IDLMethod.Special.Setter in p[1] else False
- creator = True if IDLMethod.Special.Creator 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 or creator:
- raise WebIDLError("getter and deleter are incompatible with setter and creator",
+ if setter:
+ raise WebIDLError("getter and deleter are incompatible with setter",
[self.getLocation(p, 1)])
(returnType, identifier, arguments) = p[2]
@@ -5837,6 +6709,9 @@ class Parser(Tokenizer):
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"),
@@ -5850,10 +6725,9 @@ class Parser(Tokenizer):
if returnType.isVoid():
raise WebIDLError("getter cannot have void return type",
[self.getLocation(p, 2)])
- if setter or creator:
+ if setter:
if len(arguments) != 2:
- raise WebIDLError("%s has wrong number of arguments" %
- ("setter" if setter else "creator"),
+ raise WebIDLError("setter has wrong number of arguments",
[self.getLocation(p, 2)])
argType = arguments[0].type
if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
@@ -5861,18 +6735,15 @@ class Parser(Tokenizer):
elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
specialType = IDLMethod.NamedOrIndexed.Indexed
else:
- raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" %
- ("setter" if setter else "creator"),
+ 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("%s cannot have %s argument" %
- ("setter" if setter else "creator",
- "optional" if arguments[0].optional else "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("%s cannot have %s argument" %
- ("setter" if setter else "creator",
- "optional" if arguments[1].optional else "variadic"),
+ raise WebIDLError("setter cannot have %s argument" %
+ ("optional" if arguments[1].optional else "variadic"),
[arguments[1].location])
if stringifier:
@@ -5885,7 +6756,7 @@ class Parser(Tokenizer):
# identifier might be None. This is only permitted for special methods.
if not identifier:
- if (not getter and not setter and not creator and
+ 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)])
@@ -5893,19 +6764,18 @@ class Parser(Tokenizer):
location = BuiltinLocation("<auto-generated-identifier>")
identifier = IDLUnresolvedIdentifier(
location,
- "__%s%s%s%s%s%s%s" %
+ "__%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 "",
- "creator" if creator 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, creator=creator,
+ static=static, getter=getter, setter=setter,
deleter=deleter, specialType=specialType,
legacycaller=legacycaller, stringifier=stringifier)
p[0] = method
@@ -5924,19 +6794,6 @@ class Parser(Tokenizer):
stringifier=True)
p[0] = method
- def p_Jsonifier(self, p):
- """
- Operation : JSONIFIER SEMICOLON
- """
- identifier = IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
- "__jsonifier", allowDoubleUnderscore=True)
- method = IDLMethod(self.getLocation(p, 1),
- identifier,
- returnType=BuiltinTypes[IDLBuiltinType.Types.object],
- arguments=[],
- jsonifier=True)
- p[0] = method
-
def p_QualifierStatic(self, p):
"""
Qualifier : STATIC
@@ -5981,12 +6838,6 @@ class Parser(Tokenizer):
"""
p[0] = IDLMethod.Special.Setter
- def p_SpecialCreator(self, p):
- """
- Special : CREATOR
- """
- p[0] = IDLMethod.Special.Creator
-
def p_SpecialDeleter(self, p):
"""
Special : DELETER
@@ -6045,95 +6896,105 @@ class Parser(Tokenizer):
def p_Argument(self, p):
"""
- Argument : ExtendedAttributeList Optional Type Ellipsis ArgumentName Default
+ Argument : ExtendedAttributeList ArgumentRest
"""
- t = p[3]
+ 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)
- identifier = IDLUnresolvedIdentifier(self.getLocation(p, 5), p[5])
+ # Arg names can be reserved identifiers
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3],
+ allowForbidden=True)
- optional = p[2]
- variadic = p[4]
- defaultValue = p[6]
+ defaultValue = p[4]
- if not optional and defaultValue:
- raise WebIDLError("Mandatory arguments can't have a default value.",
- [self.getLocation(p, 6)])
# 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.
- if variadic:
- if optional:
- raise WebIDLError("Variadic arguments should not be marked optional.",
- [self.getLocation(p, 2)])
- optional = variadic
+ p[0] = IDLArgument(self.getLocation(p, 3), identifier, t, True, defaultValue, False)
- p[0] = IDLArgument(self.getLocation(p, 5), identifier, t, optional, defaultValue, variadic)
- p[0].addExtendedAttributes(p[1])
+ 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
- | ATTRIBUTE
- | CALLBACK
- | CONST
- | CREATOR
- | DELETER
- | DICTIONARY
- | ENUM
- | EXCEPTION
- | GETTER
- | IMPLEMENTS
- | INHERIT
- | INTERFACE
- | ITERABLE
- | LEGACYCALLER
- | MAPLIKE
- | PARTIAL
- | REQUIRED
- | SERIALIZER
- | SETLIKE
- | SETTER
- | STATIC
- | STRINGIFIER
- | JSONIFIER
- | TYPEDEF
- | UNRESTRICTED
- | NAMESPACE
+ | ArgumentNameKeyword
"""
p[0] = p[1]
- def p_AttributeName(self, p):
- """
- AttributeName : IDENTIFIER
- | REQUIRED
+ 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_Optional(self, p):
- """
- Optional : OPTIONAL
- """
- p[0] = True
-
- def p_OptionalEmpty(self, p):
- """
- Optional :
- """
- p[0] = False
-
- def p_Required(self, p):
+ def p_AttributeName(self, p):
"""
- Required : REQUIRED
+ AttributeName : IDENTIFIER
+ | AttributeNameKeyword
"""
- p[0] = True
+ p[0] = p[1]
- def p_RequiredEmpty(self, p):
+ def p_AttributeNameKeyword(self, p):
"""
- Required :
+ AttributeNameKeyword : ASYNC
+ | REQUIRED
"""
- p[0] = False
+ p[0] = p[1]
def p_Ellipsis(self, p):
"""
@@ -6218,43 +7079,32 @@ class Parser(Tokenizer):
| EQUALS
| GT
| QUESTIONMARK
- | DATE
| DOMSTRING
| BYTESTRING
| USVSTRING
+ | UTF8STRING
+ | JSSTRING
+ | PROMISE
| ANY
- | ATTRIBUTE
| BOOLEAN
| BYTE
- | LEGACYCALLER
- | CONST
- | CREATOR
- | DELETER
| DOUBLE
- | EXCEPTION
| FALSE
| FLOAT
- | GETTER
- | IMPLEMENTS
- | INHERIT
- | INTERFACE
| LONG
- | MODULE
| NULL
| OBJECT
| OCTET
+ | OR
| OPTIONAL
+ | RECORD
| SEQUENCE
- | MOZMAP
- | SETTER
| SHORT
- | STATIC
- | STRINGIFIER
- | JSONIFIER
+ | SYMBOL
| TRUE
- | TYPEDEF
| UNSIGNED
| VOID
+ | ArgumentNameKeyword
"""
pass
@@ -6277,9 +7127,15 @@ class Parser(Tokenizer):
"""
p[0] = self.handleNullable(p[1], p[2])
- def p_SingleTypeNonAnyType(self, p):
+ def p_TypeWithExtendedAttributes(self, p):
"""
- SingleType : NonAnyType
+ TypeWithExtendedAttributes : ExtendedAttributeList Type
+ """
+ p[0] = p[2].withExtendedAttributes(p[1])
+
+ def p_SingleTypeDistinguishableType(self, p):
+ """
+ SingleType : DistinguishableType
"""
p[0] = p[1]
@@ -6289,6 +7145,14 @@ class Parser(Tokenizer):
"""
p[0] = BuiltinTypes[IDLBuiltinType.Types.any]
+ # Note: Promise<void> is allowed, so we want to parametrize on ReturnType,
+ # not Type. Promise types can't be null, hence no "Null" in there.
+ def p_SingleTypePromiseType(self, p):
+ """
+ SingleType : PROMISE LT ReturnType GT
+ """
+ p[0] = IDLPromiseType(self.getLocation(p, 1), p[3])
+
def p_UnionType(self, p):
"""
UnionType : LPAREN UnionMemberType OR UnionMemberType UnionMemberTypes RPAREN
@@ -6297,11 +7161,11 @@ class Parser(Tokenizer):
types.extend(p[5])
p[0] = IDLUnionType(self.getLocation(p, 1), types)
- def p_UnionMemberTypeNonAnyType(self, p):
+ def p_UnionMemberTypeDistinguishableType(self, p):
"""
- UnionMemberType : NonAnyType
+ UnionMemberType : ExtendedAttributeList DistinguishableType
"""
- p[0] = p[1]
+ p[0] = p[2].withExtendedAttributes(p[1])
def p_UnionMemberType(self, p):
"""
@@ -6322,55 +7186,50 @@ class Parser(Tokenizer):
"""
p[0] = []
- def p_NonAnyType(self, p):
+ def p_DistinguishableType(self, p):
"""
- NonAnyType : PrimitiveOrStringType Null
- | ARRAYBUFFER Null
- | SHAREDARRAYBUFFER Null
- | OBJECT Null
+ DistinguishableType : PrimitiveType Null
+ | ARRAYBUFFER Null
+ | READABLESTREAM Null
+ | OBJECT Null
"""
if p[1] == "object":
type = BuiltinTypes[IDLBuiltinType.Types.object]
elif p[1] == "ArrayBuffer":
type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
- elif p[1] == "SharedArrayBuffer":
- type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer]
+ elif p[1] == "ReadableStream":
+ type = BuiltinTypes[IDLBuiltinType.Types.ReadableStream]
else:
type = BuiltinTypes[p[1]]
p[0] = self.handleNullable(type, p[2])
- def p_NonAnyTypeSequenceType(self, p):
+ def p_DistinguishableTypeStringType(self, p):
"""
- NonAnyType : SEQUENCE LT Type GT Null
+ DistinguishableType : StringType Null
"""
- innerType = p[3]
- type = IDLSequenceType(self.getLocation(p, 1), innerType)
- p[0] = self.handleNullable(type, p[5])
+ p[0] = self.handleNullable(p[1], p[2])
- # Note: Promise<void> is allowed, so we want to parametrize on
- # ReturnType, not Type. Also, we want this to end up picking up
- # the Promise interface for now, hence the games with IDLUnresolvedType.
- def p_NonAnyTypePromiseType(self, p):
+ def p_DistinguishableTypeSequenceType(self, p):
"""
- NonAnyType : PROMISE LT ReturnType GT Null
+ DistinguishableType : SEQUENCE LT TypeWithExtendedAttributes GT Null
"""
innerType = p[3]
- promiseIdent = IDLUnresolvedIdentifier(self.getLocation(p, 1), "Promise")
- type = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3])
+ type = IDLSequenceType(self.getLocation(p, 1), innerType)
p[0] = self.handleNullable(type, p[5])
- def p_NonAnyTypeMozMapType(self, p):
+ def p_DistinguishableTypeRecordType(self, p):
"""
- NonAnyType : MOZMAP LT Type GT Null
+ DistinguishableType : RECORD LT StringType COMMA TypeWithExtendedAttributes GT Null
"""
- innerType = p[3]
- type = IDLMozMapType(self.getLocation(p, 1), innerType)
- p[0] = self.handleNullable(type, p[5])
+ keyType = p[3]
+ valueType = p[5]
+ type = IDLRecordType(self.getLocation(p, 1), keyType, valueType)
+ p[0] = self.handleNullable(type, p[7])
- def p_NonAnyTypeScopedName(self, p):
+ def p_DistinguishableTypeScopedName(self, p):
"""
- NonAnyType : ScopedName Null
+ DistinguishableType : ScopedName Null
"""
assert isinstance(p[1], IDLUnresolvedIdentifier)
@@ -6400,95 +7259,104 @@ class Parser(Tokenizer):
type = IDLUnresolvedType(self.getLocation(p, 1), p[1])
p[0] = self.handleNullable(type, p[2])
- def p_NonAnyTypeDate(self, p):
- """
- NonAnyType : DATE Null
- """
- p[0] = self.handleNullable(BuiltinTypes[IDLBuiltinType.Types.date],
- p[2])
-
def p_ConstType(self, p):
"""
- ConstType : PrimitiveOrStringType Null
+ ConstType : PrimitiveType
"""
- type = BuiltinTypes[p[1]]
- p[0] = self.handleNullable(type, p[2])
+ p[0] = BuiltinTypes[p[1]]
def p_ConstTypeIdentifier(self, p):
"""
- ConstType : IDENTIFIER Null
+ ConstType : IDENTIFIER
"""
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
- type = IDLUnresolvedType(self.getLocation(p, 1), identifier)
- p[0] = self.handleNullable(type, p[2])
+ p[0] = IDLUnresolvedType(self.getLocation(p, 1), identifier)
- def p_PrimitiveOrStringTypeUint(self, p):
+ def p_PrimitiveTypeUint(self, p):
"""
- PrimitiveOrStringType : UnsignedIntegerType
+ PrimitiveType : UnsignedIntegerType
"""
p[0] = p[1]
- def p_PrimitiveOrStringTypeBoolean(self, p):
+ def p_PrimitiveTypeBoolean(self, p):
"""
- PrimitiveOrStringType : BOOLEAN
+ PrimitiveType : BOOLEAN
"""
p[0] = IDLBuiltinType.Types.boolean
- def p_PrimitiveOrStringTypeByte(self, p):
+ def p_PrimitiveTypeByte(self, p):
"""
- PrimitiveOrStringType : BYTE
+ PrimitiveType : BYTE
"""
p[0] = IDLBuiltinType.Types.byte
- def p_PrimitiveOrStringTypeOctet(self, p):
+ def p_PrimitiveTypeOctet(self, p):
"""
- PrimitiveOrStringType : OCTET
+ PrimitiveType : OCTET
"""
p[0] = IDLBuiltinType.Types.octet
- def p_PrimitiveOrStringTypeFloat(self, p):
+ def p_PrimitiveTypeFloat(self, p):
"""
- PrimitiveOrStringType : FLOAT
+ PrimitiveType : FLOAT
"""
p[0] = IDLBuiltinType.Types.float
- def p_PrimitiveOrStringTypeUnrestictedFloat(self, p):
+ def p_PrimitiveTypeUnrestictedFloat(self, p):
"""
- PrimitiveOrStringType : UNRESTRICTED FLOAT
+ PrimitiveType : UNRESTRICTED FLOAT
"""
p[0] = IDLBuiltinType.Types.unrestricted_float
- def p_PrimitiveOrStringTypeDouble(self, p):
+ def p_PrimitiveTypeDouble(self, p):
"""
- PrimitiveOrStringType : DOUBLE
+ PrimitiveType : DOUBLE
"""
p[0] = IDLBuiltinType.Types.double
- def p_PrimitiveOrStringTypeUnrestictedDouble(self, p):
+ def p_PrimitiveTypeUnrestictedDouble(self, p):
"""
- PrimitiveOrStringType : UNRESTRICTED DOUBLE
+ PrimitiveType : UNRESTRICTED DOUBLE
"""
p[0] = IDLBuiltinType.Types.unrestricted_double
- def p_PrimitiveOrStringTypeDOMString(self, p):
+ def p_StringType(self, p):
+ """
+ StringType : BuiltinStringType
+ """
+ p[0] = BuiltinTypes[p[1]]
+
+ def p_BuiltinStringTypeDOMString(self, p):
"""
- PrimitiveOrStringType : DOMSTRING
+ BuiltinStringType : DOMSTRING
"""
p[0] = IDLBuiltinType.Types.domstring
- def p_PrimitiveOrStringTypeBytestring(self, p):
+ def p_BuiltinStringTypeBytestring(self, p):
"""
- PrimitiveOrStringType : BYTESTRING
+ BuiltinStringType : BYTESTRING
"""
p[0] = IDLBuiltinType.Types.bytestring
- def p_PrimitiveOrStringTypeUSVString(self, p):
+ def p_BuiltinStringTypeUSVString(self, p):
"""
- PrimitiveOrStringType : USVSTRING
+ 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
@@ -6622,7 +7490,13 @@ class Parser(Tokenizer):
IdentifierList : IDENTIFIER Identifiers
"""
idents = list(p[2])
- idents.insert(0, p[1])
+ # 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):
@@ -6630,7 +7504,13 @@ class Parser(Tokenizer):
Identifiers : COMMA IDENTIFIER Identifiers
"""
idents = list(p[3])
- idents.insert(0, 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[2]
+ if ident[0] == '_':
+ ident = ident[1:]
+ idents.insert(0, ident)
p[0] = idents
def p_IdentifiersEmpty(self, p):
@@ -6647,36 +7527,16 @@ class Parser(Tokenizer):
raise WebIDLError("invalid syntax", [Location(self.lexer, p.lineno, p.lexpos, self._filename)])
def __init__(self, outputdir='', lexer=None):
- Tokenizer.__init__(self, outputdir, lexer)
+ Tokenizer.__init__(self, lexer)
logger = SqueakyCleanLogger()
try:
- self.parser = yacc.yacc(module=self,
- outputdir=outputdir,
- tabmodule='webidlyacc',
- errorlog=logger,
- debug=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'
- )
+ self.parser = yacc.yacc(module=self, errorlog=logger, debug=False)
finally:
logger.reportGrammarErrors()
self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None)
- # To make our test harness work, pretend like we have a primary global already.
- # Note that we _don't_ set _globalScope.primaryGlobalAttr,
- # so we'll still be able to detect multiple PrimaryGlobal extended attributes.
- self._globalScope.primaryGlobalName = "FakeTestPrimaryGlobal"
- self._globalScope.globalNames.add("FakeTestPrimaryGlobal")
- self._globalScope.globalNameMapping["FakeTestPrimaryGlobal"].add("FakeTestPrimaryGlobal")
- # And we add the special-cased "System" global name, which
- # doesn't have any corresponding interfaces.
- self._globalScope.globalNames.add("System")
- self._globalScope.globalNameMapping["System"].add("BackstagePass")
+
self._installBuiltins(self._globalScope)
self._productions = []
@@ -6689,8 +7549,8 @@ class Parser(Tokenizer):
def _installBuiltins(self, scope):
assert isinstance(scope, IDLScope)
- # xrange omits the last value.
- for x in xrange(IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1):
+ # 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)
@@ -6703,12 +7563,12 @@ class Parser(Tokenizer):
return type
def parse(self, t, filename=None):
- self.lexer.input(t)
+ self._filename = filename
+ self.lexer.input(t.decode(encoding = 'utf-8'))
# 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
@@ -6719,23 +7579,9 @@ class Parser(Tokenizer):
for p in self._productions:
if isinstance(p, IDLInterface):
interfaceStatements.append(p)
- if p.identifier.name == "Navigator":
- navigatorInterface = p
iterableIteratorIface = None
for iface in interfaceStatements:
- navigatorProperty = iface.getNavigatorProperty()
- if navigatorProperty:
- # We're generating a partial interface to add a readonly
- # property to the Navigator interface for every interface
- # annotated with NavigatorProperty.
- partialInterface = IDLPartialInterfaceOrNamespace(
- iface.location,
- IDLUnresolvedIdentifier(iface.location, "Navigator"),
- [ navigatorProperty ],
- navigatorInterface)
- self._productions.append(partialInterface)
-
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
@@ -6755,9 +7601,12 @@ class Parser(Tokenizer):
nextMethod.addExtendedAttributes([simpleExtendedAttr("Throws")])
itr_ident = IDLUnresolvedIdentifier(iface.location,
iface.identifier.name + "Iterator")
+ toStringTag = iface.identifier.name + " Iterator"
itr_iface = IDLInterface(iface.location, self.globalScope(),
itr_ident, None, [nextMethod],
- isKnownNonPartial=True)
+ isKnownNonPartial=True,
+ classNameOverride=toStringTag,
+ toStringTag=toStringTag)
itr_iface.addExtendedAttributes([simpleExtendedAttr("NoInterfaceObject")])
# Make sure the exposure set for the iterator interface is the
# same as the exposure set for the iterable interface, because
@@ -6771,14 +7620,14 @@ class Parser(Tokenizer):
self._productions.append(itr_iface)
iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
- # Then, finish all the IDLImplementsStatements. In particular, we
- # have to make sure we do those before we do the IDLInterfaces.
+ # Make sure we finish IDLIncludesStatements before we finish the
+ # IDLInterfaces.
# XXX khuey hates this bit and wants to nuke it from orbit.
- implementsStatements = [p for p in self._productions if
- isinstance(p, IDLImplementsStatement)]
+ includesStatements = [p for p in self._productions if
+ isinstance(p, IDLIncludesStatement)]
otherStatements = [p for p in self._productions if
- not isinstance(p, IDLImplementsStatement)]
- for production in implementsStatements:
+ not isinstance(p, IDLIncludesStatement)]
+ for production in includesStatements:
production.finish(self.globalScope())
for production in otherStatements:
production.finish(self.globalScope())
@@ -6831,14 +7680,14 @@ def main():
f = open(fullPath, 'rb')
lines = f.readlines()
f.close()
- print fullPath
+ print(fullPath)
parser.parse(''.join(lines), fullPath)
parser.finish()
- except WebIDLError, e:
+ except WebIDLError as e:
if options.verbose_errors:
traceback.print_exc()
else:
- print e
+ print(e)
if __name__ == '__main__':
main()