Auto merge of #22934 - Manishearth:update-webidl, r=nox

Update WebIDL.py

I'm working on getting [attributes on types landed in upstream webidl](https://bugzilla.mozilla.org/show_bug.cgi?id=1359269). In preparation for that, I'd like to update WebIDL.py and deal with all the conflicts first so that updating for that bug is easier.

(This PR doesn't need to be reviewed and landed right now, I can just roll it into the PR for implementing attributes on types, but I suspect it would be easier to land this first)

Probably should squash before landing, the steps are separated out for ease of review.

r? @nox @jdm

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/22934)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2019-03-01 15:44:43 -05:00 committed by GitHub
commit 27f443fd00
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 201 additions and 76 deletions

View file

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
""" A WebIDL parser. """
@ -248,8 +248,14 @@ class IDLScope(IDLObject):
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):
@ -327,6 +333,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):
@ -504,8 +517,10 @@ class IDLExposureMixins():
return 'Window' in self.exposureSet
def isExposedOnMainThread(self):
return (self.isExposedInWindow() or
self.isExposedInSystemGlobals())
return self.isExposedInWindow()
def isExposedOffMainThread(self):
return len(self.exposureSet - {'Window', 'FakeTestPrimaryGlobal'}) > 0
def isExposedInAnyWorker(self):
return len(self.getWorkerExposureSet()) > 0
@ -516,9 +531,6 @@ class IDLExposureMixins():
def isExposedInAnyWorklet(self):
return len(self.getWorkletExposureSet()) > 0
def isExposedInSystemGlobals(self):
return 'BackstagePass' in self.exposureSet
def isExposedInSomeButNotAllWorkers(self):
"""
Returns true if the Exposed extended attribute for this interface
@ -597,6 +609,34 @@ class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
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)
@ -1322,7 +1362,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
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.
# And SecureContext makes sense for interfaces with no interface object,
@ -1704,9 +1743,8 @@ 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():
@ -1719,8 +1757,8 @@ class IDLInterface(IDLInterfaceOrNamespace):
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.parentScope.addIfaceGlobalNames(self.identifier.name,
[self.identifier.name])
self._isOnGlobalProtoChain = True
elif identifier == "SecureContext":
if not attr.noArguments():
@ -1743,7 +1781,6 @@ class IDLInterface(IDLInterfaceOrNamespace):
identifier == "LegacyUnenumerableNamedProperties" or
identifier == "RunConstructorInCallerCompartment" or
identifier == "WantsEventListenerHooks" or
identifier == "NonOrdinaryGetPrototypeOf" or
identifier == "Abstract" or
identifier == "Inline"):
# Known extended attributes that do not take values
@ -1805,7 +1842,7 @@ class IDLNamespace(IDLInterfaceOrNamespace):
if not attr.noArguments():
raise WebIDLError("[%s] must not have arguments" % identifier,
[attr.location])
elif identifier == "Pref":
elif identifier == "Pref" or identifier == "Func":
# Known extended attributes that take a string value
if not attr.hasValue():
raise WebIDLError("[%s] must have a value" % identifier,
@ -1828,6 +1865,7 @@ class IDLDictionary(IDLObjectWithScope):
self.parent = parent
self._finished = False
self.members = list(members)
self._partialDictionaries = []
IDLObjectWithScope.__init__(self, location, parentScope, name)
@ -1864,6 +1902,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():
@ -1968,6 +2011,9 @@ 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):
@ -4333,7 +4379,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():
@ -4525,7 +4571,7 @@ class IDLArgument(IDLObjectWithIdentifier):
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,
# Default optional non-variadic dictionary arguments to null,
# for simplicity, so the codegen doesn't have to special-case this.
self.defaultValue = IDLNullValue(self.location)
elif self.type.isAny():
@ -5089,6 +5135,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",
@ -5293,7 +5343,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
@ -5518,9 +5568,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.
@ -5610,6 +5661,7 @@ class Parser(Tokenizer):
"""
PartialDefinition : PartialInterface
| PartialNamespace
| PartialDictionary
"""
p[0] = p[1]
@ -5617,17 +5669,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
@ -5652,9 +5704,19 @@ class Parser(Tokenizer):
nonPartialObject = nonPartialConstructor(
# No members, False for isKnownNonPartial
*(nonPartialConstructorArgs + [[], False]))
partialInterface = IDLPartialInterfaceOrNamespace(
*(partialConstructorArgs + [nonPartialObject]))
return partialInterface
partialObject = None
if isinstance(nonPartialObject, IDLDictionary):
partialObject = IDLPartialDictionary(
*(partialConstructorArgs + [nonPartialObject]))
elif isinstance(nonPartialObject, (IDLInterface, IDLNamespace)):
partialObject = IDLPartialInterfaceOrNamespace(
*(partialConstructorArgs + [nonPartialObject]))
else:
raise WebIDLError("Unknown partial object type %s" %
type(partialObject))
return partialObject
def p_PartialInterface(self, p):
"""
@ -5682,6 +5744,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
@ -6894,16 +6969,13 @@ class Parser(Tokenizer):
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._globalScope.addIfaceGlobalNames("FakeTestPrimaryGlobal", ["FakeTestPrimaryGlobal"])
self._installBuiltins(self._globalScope)
self._productions = []

View file

@ -1,12 +1,12 @@
--- WebIDL.py
+++ WebIDL.py
@@ -1744,7 +1744,8 @@
@@ -1786,7 +1786,8 @@ class IDLInterface(IDLInterfaceOrNamespace):
identifier == "ProbablyShortLivingWrapper" or
identifier == "LegacyUnenumerableNamedProperties" or
identifier == "RunConstructorInCallerCompartment" or
identifier == "WantsEventListenerHooks" or
- identifier == "NonOrdinaryGetPrototypeOf"):
+ identifier == "NonOrdinaryGetPrototypeOf" or
- identifier == "WantsEventListenerHooks"):
+ identifier == "WantsEventListenerHooks" or
+ identifier == "Abstract"):
# Known extended attributes that do not take values
if not attr.noArguments():
raise WebIDLError("[%s] must take no arguments" % identifier,
raise WebIDLError("[%s] must take no arguments" % identifier,

View file

@ -1,17 +1,15 @@
diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py
index da32340..81c52b7 100644
--- WebIDL.py
+++ WebIDL.py
@@ -2170,7 +2170,7 @@ class IDLUnresolvedType(IDLType):
@@ -2275,7 +2275,7 @@ class IDLUnresolvedType(IDLType):
return typedefType.complete(scope)
elif obj.isCallback() and not obj.isInterface():
assert self.name.name == obj.identifier.name
- return IDLCallbackType(self.location, obj)
+ return IDLCallbackType(obj.location, obj)
if self._promiseInnerType and not self._promiseInnerType.isComplete():
self._promiseInnerType = self._promiseInnerType.complete(scope)
@@ -6521,7 +6521,7 @@ class Parser(Tokenizer):
name = self.name.resolve(scope, None)
return IDLWrapperType(self.location, obj)
@@ -6688,7 +6688,7 @@ class Parser(Tokenizer):
type = IDLTypedefType(self.getLocation(p, 1), obj.innerType,
obj.identifier.name)
elif obj.isCallback() and not obj.isInterface():
@ -19,4 +17,4 @@ index da32340..81c52b7 100644
+ type = IDLCallbackType(obj.location, obj)
else:
type = IDLWrapperType(self.getLocation(p, 1), p[1])
p[0] = self.handleModifiers(type, p[2])
p[0] = self.handleNullable(type, p[2])

View file

@ -1,6 +1,6 @@
--- WebIDL.py
+++ WebIDL.py
@@ -6823,7 +6823,8 @@ class Parser(Tokenizer):
@@ -6959,7 +6959,8 @@ class Parser(Tokenizer):
self.parser = yacc.yacc(module=self,
outputdir=outputdir,
tabmodule='webidlyacc',

View file

@ -1,9 +1,9 @@
--- WebIDL.py
+++ WebIDL.py
@@ -1695,7 +1695,8 @@ class IDLInterface(IDLInterfaceOrNamespace):
identifier == "ProbablyShortLivingObject" or
@@ -1787,7 +1787,8 @@ class IDLInterface(IDLInterfaceOrNamespace):
identifier == "LegacyUnenumerableNamedProperties" or
identifier == "NonOrdinaryGetPrototypeOf" or
identifier == "RunConstructorInCallerCompartment" or
identifier == "WantsEventListenerHooks" or
- identifier == "Abstract"):
+ identifier == "Abstract" or
+ identifier == "Inline"):

View file

@ -26,6 +26,31 @@ def WebIDLTest(parser, harness):
harness.check(dict2.members[1].identifier.name, "child",
"'a' really comes before 'c'")
# Test partial dictionary.
parser = parser.reset();
parser.parse("""
dictionary A {
long c;
long g;
};
partial dictionary A {
long h;
long d;
};
""")
results = parser.finish()
dict1 = results[0];
harness.check(len(dict1.members), 4, "Dict1 has four members")
harness.check(dict1.members[0].identifier.name, "c",
"c should be first")
harness.check(dict1.members[1].identifier.name, "d",
"d should come after c")
harness.check(dict1.members[2].identifier.name, "g",
"g should come after d")
harness.check(dict1.members[3].identifier.name, "h",
"h should be last")
# Now reset our parser
parser = parser.reset()
threw = False
@ -42,6 +67,24 @@ def WebIDLTest(parser, harness):
harness.ok(threw, "Should not allow name duplication in a dictionary")
# Test no name duplication across normal and partial dictionary.
parser = parser.reset();
threw = False
try:
parser.parse("""
dictionary A {
long prop = 5;
};
partial dictionary A {
long prop;
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Should not allow name duplication across normal and partial dictionary")
# Now reset our parser again
parser = parser.reset()
threw = False

View file

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
def should_throw(parser, harness, message, code):
parser = parser.reset();

View file

@ -1,6 +1,6 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
def should_throw(parser, harness, message, code):
parser = parser.reset();

View file

@ -0,0 +1,12 @@
--- WebIDL.py
+++ WebIDL.py
@@ -4570,8 +4570,7 @@ class IDLArgument(IDLObjectWithIdentifier):
if ((self.type.isDictionary() or
self.type.isUnion() and self.type.unroll().hasDictionaryType()) and
- self.optional and not self.defaultValue and not self.variadic and
- not self.dictionaryMember):
+ self.optional and not self.defaultValue and not self.variadic):
# Default optional non-variadic dictionary arguments to null,
# for simplicity, so the codegen doesn't have to special-case this.
self.defaultValue = IDLNullValue(self.location)

View file

@ -1,22 +1,22 @@
--- WebIDL.py
+++ WebIDL.py
@@ -2481,10 +2481,18 @@ class IDLUnionType(IDLType):
@@ -2613,10 +2613,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",
+ # we want AOrTest, not AOrBOrC
+ if not type.isComplete() and not isinstance(type, IDLTypedefType):
+ self.memberTypes[i] = type.complete(scope)
+
+ self.name = "Or".join(typeName(type) for type in self.memberTypes)
self.memberTypes[i] = type.complete(scope)
self.name = "Or".join(typeName(type) for type in self.memberTypes)
+
+ # We do this again to complete the typedef types
+ for (i, type) in enumerate(self.memberTypes):
if not type.isComplete():
self.memberTypes[i] = type.complete(scope)
- self.name = "Or".join(typeName(type) for type in self.memberTypes)
+ if not type.isComplete():
+ self.memberTypes[i] = type.complete(scope)
+
self.flatMemberTypes = list(self.memberTypes)
i = 0
while i < len(self.flatMemberTypes):

View file

@ -5,6 +5,7 @@ patch < pref-main-thread.patch
patch < callback-location.patch
patch < union-typedef.patch
patch < inline.patch
patch < undo-dictionary-optional.patch
wget https://hg.mozilla.org/mozilla-central/archive/tip.tar.gz/dom/bindings/parser/tests/ -O tests.tar.gz
rm -r tests

View file

@ -1,26 +1,25 @@
--- WebIDL.py
+++ WebIDL.py
@@ -1239,12 +1239,6 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
alias,
[member.location, m.location])
@@ -1362,12 +1362,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
for bindingAlias in member.bindingAliases:
checkDuplicateNames(member, bindingAlias, "BindingAlias")
- if (self.getExtendedAttribute("Pref") and
- self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])):
- raise WebIDLError("[Pref] used on an interface that is not %s-only" %
- self.parentScope.primaryGlobalName,
-
- if self.getExtendedAttribute("Pref") and self.isExposedOffMainThread():
- raise WebIDLError("[Pref] used on an interface that is not "
- "main-thread-only",
- [self.location])
-
# Conditional exposure makes no sense for interfaces with no
# interface object, unless they're navigator properties.
# And SecureContext makes sense for interfaces with no interface object,
@@ -3459,12 +3453,6 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
@@ -3619,11 +3613,6 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
IDLExposureMixins.finish(self, scope)
def validate(self):
- if (self.getExtendedAttribute("Pref") and
- self.exposureSet != set([self._globalScope.primaryGlobalName])):
- if self.getExtendedAttribute("Pref") and self.isExposedOffMainThread():
- raise WebIDLError("[Pref] used on an interface member that is not "
- "%s-only" % self._globalScope.primaryGlobalName,
- "main-thread-only",
- [self.location])
-
if self.isAttr() or self.isMethod():