aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/bindings/codegen/parser
diff options
context:
space:
mode:
authorKagami Sascha Rosylight <saschanaz@outlook.com>2019-08-16 21:13:29 +0900
committerKagami Sascha Rosylight <saschanaz@outlook.com>2019-08-16 23:17:50 +0900
commit1806b9ede2f7e2b6d621900aa841a535c03a433a (patch)
tree45f158e96d06b20b85f336661c7b9b74ad35c02d /components/script/dom/bindings/codegen/parser
parentff3f3d30c741eb8d73d6a39421cbfc7451ec97ee (diff)
downloadservo-1806b9ede2f7e2b6d621900aa841a535c03a433a.tar.gz
servo-1806b9ede2f7e2b6d621900aa841a535c03a433a.zip
Update WebIDL parser
Diffstat (limited to 'components/script/dom/bindings/codegen/parser')
-rw-r--r--components/script/dom/bindings/codegen/parser/WebIDL.py496
-rw-r--r--components/script/dom/bindings/codegen/parser/abstract.patch2
-rw-r--r--components/script/dom/bindings/codegen/parser/debug.patch2
-rw-r--r--components/script/dom/bindings/codegen/parser/exposed-globals.patch27
-rw-r--r--components/script/dom/bindings/codegen/parser/inline.patch2
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py12
-rw-r--r--components/script/dom/bindings/codegen/parser/tests/test_interfacemixin.py373
-rw-r--r--components/script/dom/bindings/codegen/parser/union-typedef.patch4
-rwxr-xr-xcomponents/script/dom/bindings/codegen/parser/update.sh1
9 files changed, 757 insertions, 162 deletions
diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py
index edfdcf9d745..b934c21db5b 100644
--- a/components/script/dom/bindings/codegen/parser/WebIDL.py
+++ b/components/script/dom/bindings/codegen/parser/WebIDL.py
@@ -314,7 +314,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):
@@ -605,7 +605,7 @@ 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:
@@ -676,30 +676,212 @@ def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
for name in nameSet:
exposureSet.update(globalScope.globalNameMapping[name])
+class IDLInterfaceOrInterfaceMixinOrNamespace(IDLObjectWithScope, IDLExposureMixins):
+ def __init__(self, location, parentScope, name):
+ assert isinstance(parentScope, IDLScope)
+ assert isinstance(name, IDLUnresolvedIdentifier)
+
+ self._finished = False
+ self.members = []
+ self._partials = []
+ self._extendedAttrDict = {}
+ self._isKnownNonPartial = False
+
+ IDLObjectWithScope.__init__(self, location, parentScope, name)
+ IDLExposureMixins.__init__(self, location)
+
+ def finish(self, scope):
+ if not self._isKnownNonPartial:
+ raise WebIDLError("%s does not have a non-partial declaration" %
+ str(self), [self.location])
+
+ IDLExposureMixins.finish(self, scope)
+
+ # Now go ahead and merge in our partials.
+ for partial in self._partials:
+ partial.finish(scope)
+ self.addExtendedAttributes(partial.propagatedExtendedAttrs)
+ self.members.extend(partial.members)
+
+ def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
+ assert isinstance(scope, IDLScope)
+ assert isinstance(originalObject, IDLInterfaceMember)
+ assert isinstance(newObject, IDLInterfaceMember)
+
+ retval = IDLScope.resolveIdentifierConflict(self, scope, identifier,
+ originalObject, newObject)
+
+ # Might be a ctor, which isn't in self.members
+ if newObject in self.members:
+ self.members.remove(newObject)
+ return retval
+
+ def typeName(self):
+ if self.isInterface():
+ return "interface"
+ if self.isNamespace():
+ return "namespace"
+ return "interface mixin"
-class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
+ 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)
+
+ # 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])
+
+
+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 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 to
+ # prevent exposing to PrimaryGlobal by default.
+ 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 isinstance(parentScope, IDLScope)
- assert isinstance(name, IDLUnresolvedIdentifier)
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.legacyWindowAliases = []
self.implementedInterfaces = set()
+ self.includedMixins = set()
self._consequential = False
- self._isKnownNonPartial = False
# self.interfacesBasedOnSelf is the set of interfaces that inherit from
# self or have self as a consequential interface, including self itself.
# Used for distinguishability checking.
@@ -720,8 +902,7 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
self.toStringTag = toStringTag
- IDLObjectWithScope.__init__(self, location, parentScope, name)
- IDLExposureMixins.__init__(self, location)
+ IDLInterfaceOrInterfaceMixinOrNamespace.__init__(self, location, parentScope, name)
if isKnownNonPartial:
self.setNonPartial(location, parent, members)
@@ -741,31 +922,13 @@ 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)
if len(self.legacyWindowAliases) > 0:
if not self.hasInterfaceObject():
@@ -777,12 +940,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
"but not exposed in Window" % self.identifier.name,
[self.location])
- # 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)
-
# Generate maplike/setlike interface members. Since generated members
# need to be treated like regular interface members, do this before
# things like exposure setting.
@@ -804,19 +961,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):
@@ -912,6 +1056,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
for iface in self.implementedInterfaces:
iface.finish(scope)
+ for mixin in self.includedMixins:
+ mixin.finish(scope)
cycleInGraph = self.findInterfaceLoopPoint(self)
if cycleInGraph:
@@ -926,24 +1072,7 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
# 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)
-
- 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:
@@ -996,6 +1125,10 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
self.members.extend(additionalMembers)
iface.interfacesImplementingSelf.add(self)
+ for mixin in sorted(self.includedMixins,
+ key=lambda x: x.identifier.name):
+ self.members.extend(mixin.members)
+
for ancestor in self.getInheritedInterfaces():
ancestor.interfacesBasedOnSelf.add(self)
if (ancestor.maplikeOrSetlikeOrIterable is not None and
@@ -1430,6 +1563,10 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
assert(isinstance(implementedInterface, IDLInterface))
self.implementedInterfaces.add(implementedInterface)
+ def addIncludedMixin(self, includedMixin):
+ assert(isinstance(includedMixin, IDLInterfaceMixin))
+ self.includedMixins.add(includedMixin)
+
def getInheritedInterfaces(self):
"""
Returns a list of the interfaces this interface inherits from
@@ -1478,34 +1615,11 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
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 getPartialInterfaces(self):
- # Don't let people mutate our guts.
- return list(self._partialInterfaces)
def getJSImplementation(self):
classId = self.getExtendedAttribute("JSImplementation")
@@ -1568,6 +1682,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
@@ -3344,7 +3459,7 @@ class IDLBuiltinType(IDLType):
[self.location, attribute.location])
assert not self.nullable()
if not attribute.hasValue():
- raise WebIDLError("[TreatNullAs] must take an identifier argument",
+ raise WebIDLError("[TreatNullAs] must take an identifier argument"
[attribute.location])
value = attribute.value()
if value != 'EmptyString':
@@ -3723,7 +3838,6 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
IDLObjectWithIdentifier.__init__(self, location, None, identifier)
IDLExposureMixins.__init__(self, location)
self.tag = tag
- self.exposed = set()
if extendedAttrDict is None:
self._extendedAttrDict = {}
else:
@@ -3757,16 +3871,12 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
def getExtendedAttribute(self, name):
return self._extendedAttrDict.get(name, None)
- def exposedSet(self):
- return self.exposed
-
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)
- globalNameSetToExposureSet(scope, self._exposureGlobalNames, self.exposed)
def validate(self):
if self.isAttr() or self.isMethod():
@@ -5433,6 +5543,49 @@ class IDLImplementsStatement(IDLObject):
"allowed on implements statements",
[attrs[0].location, self.location])
+class IDLIncludesStatement(IDLObject):
+ def __init__(self, location, interface, mixin):
+ IDLObject.__init__(self, location)
+ self.interface = interface
+ self.mixin = mixin
+ self._finished = False
+
+ def finish(self, scope):
+ if self._finished:
+ return
+ self._finished = True
+ assert(isinstance(self.interface, IDLIdentifierPlaceholder))
+ assert(isinstance(self.mixin, IDLIdentifierPlaceholder))
+ interface = self.interface.finish(scope)
+ mixin = self.mixin.finish(scope)
+ # NOTE: we depend on not setting self.interface and
+ # self.mixin here to keep track of the original
+ # locations.
+ if not isinstance(interface, IDLInterface):
+ raise WebIDLError("Left-hand side of 'includes' is not an "
+ "interface",
+ [self.interface.location])
+ if interface.isCallback():
+ raise WebIDLError("Left-hand side of 'includes' is a callback "
+ "interface",
+ [self.interface.location])
+ if not isinstance(mixin, IDLInterfaceMixin):
+ raise WebIDLError("Right-hand side of 'includes' is not an "
+ "interface mixin",
+ [self.mixin.location])
+ mixin.actualExposureGlobalNames.update(interface._exposureGlobalNames)
+ interface.addIncludedMixin(mixin)
+ self.interface = interface
+ self.mixin = mixin
+
+ def validate(self):
+ pass
+
+ def addExtendedAttributes(self, attrs):
+ if len(attrs) != 0:
+ raise WebIDLError("There are no extended attributes that are "
+ "allowed on includes statements",
+ [attrs[0].location, self.location])
class IDLExtendedAttribute(IDLObject):
"""
@@ -5529,12 +5682,14 @@ class Tokenizer(object):
"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",
@@ -5689,7 +5844,7 @@ class Parser(Tokenizer):
def p_Definition(self, p):
"""
- Definition : CallbackOrInterface
+ Definition : CallbackOrInterfaceOrMixin
| Namespace
| Partial
| Dictionary
@@ -5697,13 +5852,14 @@ class Parser(Tokenizer):
| 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)
@@ -5711,17 +5867,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
| CallbackConstructorRest
- | Interface
+ | CallbackInterface
"""
assert p[1]
p[0] = p[1]
@@ -5762,14 +5918,27 @@ class Parser(Tokenizer):
# True for isKnownNonPartial
return constructor(*(constructorArgs + [True]))
- def p_Interface(self, p):
+ def p_InterfaceOrMixin(self, p):
"""
- Interface : INTERFACE IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON
+ InterfaceOrMixin : InterfaceRest
+ | MixinRest
+ """
+ p[0] = p[1]
+
+ def p_CallbackInterface(self, p):
+ """
+ CallbackInterface : INTERFACE InterfaceRest
+ """
+ p[0] = p[2]
+
+ def p_InterfaceRest(self, p):
+ """
+ InterfaceRest : IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON
"""
location = self.getLocation(p, 1)
- identifier = IDLUnresolvedIdentifier(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,
@@ -5778,10 +5947,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):
@@ -5799,6 +5968,19 @@ class Parser(Tokenizer):
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
@@ -5818,10 +6000,15 @@ 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]
@@ -5864,34 +6051,55 @@ class Parser(Tokenizer):
if not nonPartialObject:
nonPartialObject = nonPartialConstructor(
# No members, False for isKnownNonPartial
- *(nonPartialConstructorArgs + [[], False]))
+ *(nonPartialConstructorArgs), members=[], isKnownNonPartial=False)
partialObject = None
if isinstance(nonPartialObject, IDLDictionary):
partialObject = IDLPartialDictionary(
*(partialConstructorArgs + [nonPartialObject]))
- elif isinstance(nonPartialObject, (IDLInterface, IDLNamespace)):
+ elif isinstance(nonPartialObject, (IDLInterface, IDLInterfaceMixin, IDLNamespace)):
partialObject = IDLPartialInterfaceOrNamespace(
*(partialConstructorArgs + [nonPartialObject]))
else:
raise WebIDLError("Unknown partial object type %s" %
- type(partialObject))
+ type(partialObject),
+ [location])
return partialObject
- def p_PartialInterface(self, p):
+ def p_PartialInterfaceOrPartialMixin(self, p):
+ """
+ PartialInterfaceOrPartialMixin : PartialInterfaceRest
+ | PartialMixinRest
+ """
+ p[0] = p[1]
+
+ def p_PartialInterfaceRest(self, p):
"""
- PartialInterface : INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
+ PartialInterfaceRest : IDENTIFIER LBRACE InterfaceMembers 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
@@ -5934,7 +6142,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])
@@ -5954,6 +6162,32 @@ class Parser(Tokenizer):
"""
p[0] = p[1]
+
+ def p_MixinMembersEmpty(self, p):
+ """
+ MixinMembers :
+ """
+ p[0] = []
+
+ def p_MixinMembers(self, p):
+ """
+ MixinMembers : ExtendedAttributeList MixinMember MixinMembers
+ """
+ p[0] = [p[2]]
+
+ assert not p[1] or p[2]
+ p[2].addExtendedAttributes(p[1])
+
+ p[0].extend(p[3])
+
+ def p_MixinMember(self, p):
+ """
+ MixinMember : Const
+ | Attribute
+ | Operation
+ """
+ p[0] = p[1]
+
def p_Dictionary(self, p):
"""
Dictionary : DICTIONARY IDENTIFIER Inheritance LBRACE DictionaryMembers RBRACE SEMICOLON
@@ -6130,6 +6364,15 @@ class Parser(Tokenizer):
p[0] = IDLImplementsStatement(self.getLocation(p, 1), implementor,
implementee)
+ def p_IncludesStatement(self, p):
+ """
+ IncludesStatement : ScopedName INCLUDES ScopedName SEMICOLON
+ """
+ assert(p[2] == "includes")
+ interface = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1])
+ mixin = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3])
+ p[0] = IDLIncludesStatement(self.getLocation(p, 1), interface, mixin)
+
def p_Const(self, p):
"""
Const : CONST ConstType IDENTIFIER EQUALS ConstValue SEMICOLON
@@ -7260,10 +7503,17 @@ class Parser(Tokenizer):
# XXX khuey hates this bit and wants to nuke it from orbit.
implementsStatements = [p for p in self._productions if
isinstance(p, IDLImplementsStatement)]
+ # Make sure we finish IDLIncludesStatements before we finish the
+ # IDLInterfaces.
+ includesStatements = [p for p in self._productions if
+ isinstance(p, IDLIncludesStatement)]
otherStatements = [p for p in self._productions if
- not isinstance(p, IDLImplementsStatement)]
+ not isinstance(p, (IDLImplementsStatement,
+ IDLIncludesStatement))]
for production in implementsStatements:
production.finish(self.globalScope())
+ for production in includesStatements:
+ production.finish(self.globalScope())
for production in otherStatements:
production.finish(self.globalScope())
diff --git a/components/script/dom/bindings/codegen/parser/abstract.patch b/components/script/dom/bindings/codegen/parser/abstract.patch
index e2db398a051..180e345b61b 100644
--- a/components/script/dom/bindings/codegen/parser/abstract.patch
+++ b/components/script/dom/bindings/codegen/parser/abstract.patch
@@ -1,6 +1,6 @@
--- WebIDL.py
+++ WebIDL.py
-@@ -1768,7 +1768,8 @@ class IDLInterface(IDLInterfaceOrNamespace):
+@@ -1883,7 +1883,8 @@ class IDLInterface(IDLInterfaceOrNamespace):
identifier == "LegacyUnenumerableNamedProperties" or
identifier == "RunConstructorInCallerCompartment" or
identifier == "WantsEventListenerHooks" or
diff --git a/components/script/dom/bindings/codegen/parser/debug.patch b/components/script/dom/bindings/codegen/parser/debug.patch
index f9b3d940399..a4f8739000d 100644
--- a/components/script/dom/bindings/codegen/parser/debug.patch
+++ b/components/script/dom/bindings/codegen/parser/debug.patch
@@ -1,6 +1,6 @@
--- WebIDL.py
+++ WebIDL.py
-@@ -7123,7 +7123,8 @@ class Parser(Tokenizer):
+@@ -7382,7 +7382,8 @@ class Parser(Tokenizer):
self.parser = yacc.yacc(module=self,
outputdir=outputdir,
tabmodule='webidlyacc',
diff --git a/components/script/dom/bindings/codegen/parser/exposed-globals.patch b/components/script/dom/bindings/codegen/parser/exposed-globals.patch
deleted file mode 100644
index 02ad787f5e0..00000000000
--- a/components/script/dom/bindings/codegen/parser/exposed-globals.patch
+++ /dev/null
@@ -1,27 +0,0 @@
---- WebIDL.py
-+++ WebIDL.py
-@@ -3653,6 +3653,7 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
- IDLObjectWithIdentifier.__init__(self, location, None, identifier)
- IDLExposureMixins.__init__(self, location)
- self.tag = tag
-+ self.exposed = set()
- if extendedAttrDict is None:
- self._extendedAttrDict = {}
- else:
-@@ -3686,12 +3687,16 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
- def getExtendedAttribute(self, name):
- return self._extendedAttrDict.get(name, None)
-
-+ def exposedSet(self):
-+ return self.exposed
-+
- 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)
-+ globalNameSetToExposureSet(scope, self._exposureGlobalNames, self.exposed)
-
- def validate(self):
- if self.isAttr() or self.isMethod():
diff --git a/components/script/dom/bindings/codegen/parser/inline.patch b/components/script/dom/bindings/codegen/parser/inline.patch
index 8337effd0f1..46971ce5067 100644
--- a/components/script/dom/bindings/codegen/parser/inline.patch
+++ b/components/script/dom/bindings/codegen/parser/inline.patch
@@ -1,6 +1,6 @@
--- WebIDL.py
+++ WebIDL.py
-@@ -1769,7 +1769,8 @@ class IDLInterface(IDLInterfaceOrNamespace):
+@@ -1884,7 +1884,8 @@ class IDLInterface(IDLInterfaceOrNamespace):
identifier == "RunConstructorInCallerCompartment" or
identifier == "WantsEventListenerHooks" or
identifier == "Serializable" or
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py b/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py
index 066300e8bb4..8420f2ee4e0 100644
--- a/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py
+++ b/components/script/dom/bindings/codegen/parser/tests/test_conditional_dictionary_member.py
@@ -44,8 +44,8 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception as exception:
- pass
+ except Exception as e:
+ exception = e
harness.ok(exception, "Should have thrown.")
harness.check(exception.message,
@@ -70,8 +70,8 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception as exception:
- pass
+ except Exception as e:
+ exception = e
harness.ok(exception, "Should have thrown (2).")
harness.check(exception.message,
@@ -100,8 +100,8 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
- except Exception as exception:
- pass
+ except Exception as e:
+ exception = e
harness.ok(exception, "Should have thrown (3).")
harness.check(exception.message,
diff --git a/components/script/dom/bindings/codegen/parser/tests/test_interfacemixin.py b/components/script/dom/bindings/codegen/parser/tests/test_interfacemixin.py
new file mode 100644
index 00000000000..ae3400d2cdb
--- /dev/null
+++ b/components/script/dom/bindings/codegen/parser/tests/test_interfacemixin.py
@@ -0,0 +1,373 @@
+import WebIDL
+
+def WebIDLTest(parser, harness):
+ parser.parse("interface mixin Foo { };")
+ results = parser.finish()
+ harness.ok(True, "Empty interface mixin parsed without error.")
+ harness.check(len(results), 1, "Should be one production")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterfaceMixin),
+ "Should be an IDLInterfaceMixin")
+ mixin = results[0]
+ harness.check(mixin.identifier.QName(), "::Foo", "Interface mixin has the right QName")
+ harness.check(mixin.identifier.name, "Foo", "Interface mixin has the right name")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface mixin QNameBase {
+ const long foo = 3;
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 1, "Should be one productions")
+ harness.ok(isinstance(results[0], WebIDL.IDLInterfaceMixin),
+ "Should be an IDLInterfaceMixin")
+ harness.check(len(results[0].members), 1, "Expect 1 productions")
+ mixin = results[0]
+ harness.check(mixin.members[0].identifier.QName(), "::QNameBase::foo",
+ "Member has the right QName")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface mixin A {
+ readonly attribute boolean x;
+ void foo();
+ };
+ partial interface mixin A {
+ readonly attribute boolean y;
+ void foo(long arg);
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 2,
+ "Should have two results with partial interface mixin")
+ mixin = results[0]
+ harness.check(len(mixin.members), 3,
+ "Should have three members with partial interface mixin")
+ harness.check(mixin.members[0].identifier.name, "x",
+ "First member should be x with partial interface mixin")
+ harness.check(mixin.members[1].identifier.name, "foo",
+ "Second member should be foo with partial interface mixin")
+ harness.check(len(mixin.members[1].signatures()), 2,
+ "Should have two foo signatures with partial interface mixin")
+ harness.check(mixin.members[2].identifier.name, "y",
+ "Third member should be y with partial interface mixin")
+
+ parser = parser.reset()
+ parser.parse("""
+ partial interface mixin A {
+ readonly attribute boolean y;
+ void foo(long arg);
+ };
+ interface mixin A {
+ readonly attribute boolean x;
+ void foo();
+ };
+ """)
+ results = parser.finish()
+ harness.check(len(results), 2,
+ "Should have two results with reversed partial interface mixin")
+ mixin = results[1]
+ harness.check(len(mixin.members), 3,
+ "Should have three members with reversed partial interface mixin")
+ harness.check(mixin.members[0].identifier.name, "x",
+ "First member should be x with reversed partial interface mixin")
+ harness.check(mixin.members[1].identifier.name, "foo",
+ "Second member should be foo with reversed partial interface mixin")
+ harness.check(len(mixin.members[1].signatures()), 2,
+ "Should have two foo signatures with reversed partial interface mixin")
+ harness.check(mixin.members[2].identifier.name, "y",
+ "Third member should be y with reversed partial interface mixin")
+
+ parser = parser.reset()
+ parser.parse("""
+ interface Interface {};
+ interface mixin Mixin {
+ attribute short x;
+ };
+ Interface includes Mixin;
+ """)
+ results = parser.finish()
+ iface = results[0]
+ harness.check(len(iface.members), 1, "Should merge members from mixins")
+ harness.check(iface.members[0].identifier.name, "x",
+ "Should merge members from mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ readonly attribute boolean x;
+ };
+ interface mixin A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow two non-partial interface mixins with the same name")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ partial interface mixin A {
+ readonly attribute boolean x;
+ };
+ partial interface mixin A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Must have a non-partial interface mixin for a given name")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ boolean x;
+ };
+ partial interface mixin A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between partial interface "
+ "mixin and other object")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ dictionary A {
+ boolean x;
+ };
+ interface mixin A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between interface mixin "
+ "and other object")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ readonly attribute boolean x;
+ };
+ interface A;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow a name collision between external interface "
+ "and interface mixin")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ [SomeRandomAnnotation]
+ interface mixin A {
+ readonly attribute boolean y;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow unknown extended attributes on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ getter double (DOMString propertyName);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow getters on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ setter void (DOMString propertyName, double propertyValue);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow setters on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ deleter void (DOMString propertyName);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow deleters on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ legacycaller double compute(double x);
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow legacycallers on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin A {
+ inherit attribute x;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should not allow inherited attribute on interface mixins")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface Interface {};
+ interface NotMixin {
+ attribute short x;
+ };
+ Interface includes NotMixin;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should fail if the right side does not point an interface mixin")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin NotInterface {};
+ interface mixin Mixin {
+ attribute short x;
+ };
+ NotInterface includes Mixin;
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should fail if the left side does not point an interface")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin Mixin {
+ iterable<DOMString>;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should fail if an interface mixin includes iterable")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin Mixin {
+ setlike<DOMString>;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should fail if an interface mixin includes setlike")
+
+ parser = parser.reset()
+ threw = False
+ try:
+ parser.parse("""
+ interface mixin Mixin {
+ maplike<DOMString, DOMString>;
+ };
+ """)
+ results = parser.finish()
+ except:
+ threw = True
+ harness.ok(threw,
+ "Should fail if an interface mixin includes maplike")
+
+ parser = parser.reset()
+ parser.parse("""
+ [Global] interface Window {};
+ [Global] interface Worker {};
+ [Exposed=Window]
+ interface Base {};
+ interface mixin Mixin {
+ Base returnSelf();
+ };
+ Base includes Mixin;
+ """)
+ results = parser.finish()
+ base = results[2]
+ attr = base.members[0]
+ harness.check(attr.exposureSet, set(["Window"]),
+ "Should expose on globals where the base interfaces are exposed")
+
+ parser = parser.reset()
+ parser.parse("""
+ [Global] interface Window {};
+ [Global] interface Worker {};
+ [Exposed=Window]
+ interface Base {};
+ [Exposed=Window]
+ interface mixin Mixin {
+ attribute short a;
+ };
+ Base includes Mixin;
+ """)
+ results = parser.finish()
+ base = results[2]
+ attr = base.members[0]
+ harness.check(attr.exposureSet, set(["Window"]),
+ "Should follow [Exposed] on interface mixin")
diff --git a/components/script/dom/bindings/codegen/parser/union-typedef.patch b/components/script/dom/bindings/codegen/parser/union-typedef.patch
index ec045300b6d..20efea8e129 100644
--- a/components/script/dom/bindings/codegen/parser/union-typedef.patch
+++ b/components/script/dom/bindings/codegen/parser/union-typedef.patch
@@ -1,8 +1,8 @@
--- WebIDL.py
+++ WebIDL.py
-@@ -2613,10 +2613,18 @@ class IDLUnionType(IDLType):
+@@ -2624,10 +2624,18 @@ class IDLUnionType(IDLType):
return type.name
-
+
for (i, type) in enumerate(self.memberTypes):
- if not type.isComplete():
+ # Exclude typedefs because if given "typedef (B or C) test",
diff --git a/components/script/dom/bindings/codegen/parser/update.sh b/components/script/dom/bindings/codegen/parser/update.sh
index 570b8b4506d..fee9720ab2d 100755
--- a/components/script/dom/bindings/codegen/parser/update.sh
+++ b/components/script/dom/bindings/codegen/parser/update.sh
@@ -4,7 +4,6 @@ patch < debug.patch
patch < callback-location.patch
patch < union-typedef.patch
patch < inline.patch
-patch < exposed-globals.patch
wget https://hg.mozilla.org/mozilla-central/archive/tip.tar.gz/dom/bindings/parser/tests/ -O tests.tar.gz
rm -r tests