Update the WebIDL parser

This commit is contained in:
Anthony Ramine 2016-05-15 00:07:43 +02:00
parent aa8c835d3b
commit c9af465ae8
7 changed files with 244 additions and 56 deletions

View file

@ -578,8 +578,8 @@ class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
def isProbablyShortLivingObject(self): def isProbablyShortLivingObject(self):
return False return False
def getNavigatorProperty(self): def isNavigatorProperty(self):
return None return False
def _getDependentObjects(self): def _getDependentObjects(self):
return set() return set()
@ -1078,11 +1078,22 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
specialMembersSeen[memberType] = member specialMembersSeen[memberType] = member
if (self.getExtendedAttribute("LegacyUnenumerableNamedProperties") and if self.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
"named getters" not in specialMembersSeen): # Check that we have a named getter.
raise WebIDLError("[LegacyUnenumerableNamedProperties] used on an interface " if "named getters" not in specialMembersSeen:
"without a named getter", raise WebIDLError(
[self.location]) "Interface with [LegacyUnenumerableNamedProperties] does "
"not have a named getter",
[self.location])
ancestor = self.parent
while ancestor:
if ancestor.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
raise WebIDLError(
"Interface with [LegacyUnenumerableNamedProperties] "
"inherits from another interface with "
"[LegacyUnenumerableNamedProperties]",
[self.location, ancestor.location])
ancestor = ancestor.parent
if self._isOnGlobalProtoChain: if self._isOnGlobalProtoChain:
# Make sure we have no named setters, creators, or deleters # Make sure we have no named setters, creators, or deleters
@ -1246,7 +1257,7 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
# interface object, unless they're navigator properties. # interface object, unless they're navigator properties.
if (self.isExposedConditionally() and if (self.isExposedConditionally() and
not self.hasInterfaceObject() and not self.hasInterfaceObject() and
not self.getNavigatorProperty()): not self.isNavigatorProperty()):
raise WebIDLError("Interface with no interface object is " raise WebIDLError("Interface with no interface object is "
"exposed conditionally", "exposed conditionally",
[self.location]) [self.location])
@ -1471,8 +1482,9 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
identifier == "UnsafeInPrerendering" or identifier == "UnsafeInPrerendering" or
identifier == "LegacyEventInit" or identifier == "LegacyEventInit" or
identifier == "ProbablyShortLivingObject" or identifier == "ProbablyShortLivingObject" or
identifier == "Abstract" or identifier == "LegacyUnenumerableNamedProperties" or
identifier == "LegacyUnenumerableNamedProperties"): identifier == "NonOrdinaryGetPrototypeOf" or
identifier == "Abstract"):
# Known extended attributes that do not take values # Known extended attributes that do not take values
if not attr.noArguments(): if not attr.noArguments():
raise WebIDLError("[%s] must take no arguments" % identifier, raise WebIDLError("[%s] must take no arguments" % identifier,
@ -1594,6 +1606,15 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
current = current.parent current = current.parent
return False 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): def getNavigatorProperty(self):
naviProp = self.getExtendedAttribute("NavigatorProperty") naviProp = self.getExtendedAttribute("NavigatorProperty")
if not naviProp: if not naviProp:
@ -1601,7 +1622,22 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
assert len(naviProp) == 1 assert len(naviProp) == 1
assert isinstance(naviProp, list) assert isinstance(naviProp, list)
assert len(naviProp[0]) != 0 assert len(naviProp[0]) != 0
return naviProp[0] conditionExtendedAttributes = self._extendedAttrDict.viewkeys() & IDLInterface.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): def hasChildInterfaces(self):
return self._hasChildInterfaces return self._hasChildInterfaces
@ -1619,13 +1655,11 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
def hasMembersInSlots(self): def hasMembersInSlots(self):
return self._ownMembersInSlots != 0 return self._ownMembersInSlots != 0
conditionExtendedAttributes = [ "Pref", "ChromeOnly", "Func", "AvailableIn",
"CheckAnyPermissions",
"CheckAllPermissions" ]
def isExposedConditionally(self): def isExposedConditionally(self):
return (self.getExtendedAttribute("Pref") or return any(self.getExtendedAttribute(a) for a in self.conditionExtendedAttributes)
self.getExtendedAttribute("ChromeOnly") or
self.getExtendedAttribute("Func") or
self.getExtendedAttribute("AvailableIn") or
self.getExtendedAttribute("CheckAnyPermissions") or
self.getExtendedAttribute("CheckAllPermissions"))
class IDLDictionary(IDLObjectWithScope): class IDLDictionary(IDLObjectWithScope):
@ -3380,11 +3414,14 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
AffectsValues = ("Nothing", "Everything") AffectsValues = ("Nothing", "Everything")
DependsOnValues = ("Nothing", "DOMState", "DeviceState", "Everything") DependsOnValues = ("Nothing", "DOMState", "DeviceState", "Everything")
def __init__(self, location, identifier, tag): def __init__(self, location, identifier, tag, extendedAttrDict=None):
IDLObjectWithIdentifier.__init__(self, location, None, identifier) IDLObjectWithIdentifier.__init__(self, location, None, identifier)
IDLExposureMixins.__init__(self, location) IDLExposureMixins.__init__(self, location)
self.tag = tag self.tag = tag
self._extendedAttrDict = {} if extendedAttrDict is None:
self._extendedAttrDict = {}
else:
self._extendedAttrDict = extendedAttrDict
def isMethod(self): def isMethod(self):
return self.tag == IDLInterfaceMember.Tags.Method return self.tag == IDLInterfaceMember.Tags.Method
@ -3868,9 +3905,11 @@ class IDLConst(IDLInterfaceMember):
class IDLAttribute(IDLInterfaceMember): class IDLAttribute(IDLInterfaceMember):
def __init__(self, location, identifier, type, readonly, inherit=False, def __init__(self, location, identifier, type, readonly, inherit=False,
static=False, stringifier=False, maplikeOrSetlike=None): static=False, stringifier=False, maplikeOrSetlike=None,
extendedAttrDict=None, navigatorObjectGetter=False):
IDLInterfaceMember.__init__(self, location, identifier, IDLInterfaceMember.__init__(self, location, identifier,
IDLInterfaceMember.Tags.Attr) IDLInterfaceMember.Tags.Attr,
extendedAttrDict=extendedAttrDict)
assert isinstance(type, IDLType) assert isinstance(type, IDLType)
self.type = type self.type = type
@ -3887,6 +3926,7 @@ class IDLAttribute(IDLInterfaceMember):
self.maplikeOrSetlike = maplikeOrSetlike self.maplikeOrSetlike = maplikeOrSetlike
self.dependsOn = "Everything" self.dependsOn = "Everything"
self.affects = "Everything" self.affects = "Everything"
self.navigatorObjectGetter = navigatorObjectGetter
if static and identifier.name == "prototype": if static and identifier.name == "prototype":
raise WebIDLError("The identifier of a static attribute must not be 'prototype'", raise WebIDLError("The identifier of a static attribute must not be 'prototype'",
@ -4041,6 +4081,24 @@ class IDLAttribute(IDLInterfaceMember):
raise WebIDLError("[PutForwards] and [Replaceable] can't both " raise WebIDLError("[PutForwards] and [Replaceable] can't both "
"appear on the same attribute", "appear on the same attribute",
[attr.location, self.location]) [attr.location, self.location])
elif identifier == "LenientSetter":
if not attr.noArguments():
raise WebIDLError("[LenientSetter] must take no arguments",
[attr.location])
if not self.readonly:
raise WebIDLError("[LenientSetter] is only allowed on readonly "
"attributes", [attr.location, self.location])
if self.isStatic():
raise WebIDLError("[LenientSetter] is only allowed on non-static "
"attributes", [attr.location, self.location])
if self.getExtendedAttribute("PutForwards") is not None:
raise WebIDLError("[LenientSetter] and [PutForwards] can't both "
"appear on the same attribute",
[attr.location, self.location])
if self.getExtendedAttribute("Replaceable") is not None:
raise WebIDLError("[LenientSetter] and [Replaceable] can't both "
"appear on the same attribute",
[attr.location, self.location])
elif identifier == "LenientFloat": elif identifier == "LenientFloat":
if self.readonly: if self.readonly:
raise WebIDLError("[LenientFloat] used on a readonly attribute", raise WebIDLError("[LenientFloat] used on a readonly attribute",
@ -4116,6 +4174,14 @@ class IDLAttribute(IDLInterfaceMember):
raise WebIDLError("[UseCounter] must not be used on a " raise WebIDLError("[UseCounter] must not be used on a "
"stringifier attribute", "stringifier attribute",
[attr.location, self.location]) [attr.location, self.location])
elif identifier == "Unscopable":
if not attr.noArguments():
raise WebIDLError("[Unscopable] must take no arguments",
[attr.location])
if self.isStatic():
raise WebIDLError("[Unscopable] is only allowed on non-static "
"attributes and operations",
[attr.location, self.location])
elif (identifier == "Pref" or elif (identifier == "Pref" or
identifier == "Deprecated" or identifier == "Deprecated" or
identifier == "SetterThrows" or identifier == "SetterThrows" or
@ -4771,6 +4837,9 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
elif identifier == "PutForwards": elif identifier == "PutForwards":
raise WebIDLError("Only attributes support [PutForwards]", raise WebIDLError("Only attributes support [PutForwards]",
[attr.location, self.location]) [attr.location, self.location])
elif identifier == "LenientSetter":
raise WebIDLError("Only attributes support [LenientSetter]",
[attr.location, self.location])
elif identifier == "LenientFloat": elif identifier == "LenientFloat":
# This is called before we've done overload resolution # This is called before we've done overload resolution
assert len(self.signatures()) == 1 assert len(self.signatures()) == 1
@ -4816,6 +4885,14 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
raise WebIDLError("[UseCounter] must not be used on a special " raise WebIDLError("[UseCounter] must not be used on a special "
"operation", "operation",
[attr.location, self.location]) [attr.location, self.location])
elif identifier == "Unscopable":
if not attr.noArguments():
raise WebIDLError("[Unscopable] must take no arguments",
[attr.location])
if self.isStatic():
raise WebIDLError("[Unscopable] is only allowed on non-static "
"attributes and operations",
[attr.location, self.location])
elif (identifier == "Throws" or elif (identifier == "Throws" or
identifier == "NewObject" or identifier == "NewObject" or
identifier == "ChromeOnly" or identifier == "ChromeOnly" or
@ -5098,10 +5175,11 @@ class SqueakyCleanLogger(object):
info = debug info = debug
def warning(self, msg, *args, **kwargs): def warning(self, msg, *args, **kwargs):
if msg == "%s:%d: Rule '%s' defined, but not used": if msg == "%s:%d: Rule %r defined, but not used" or \
msg == "%s:%d: Rule '%s' defined, but not used":
# Munge things so we don't have to hardcode filenames and # Munge things so we don't have to hardcode filenames and
# line numbers in our whitelist. # line numbers in our whitelist.
whitelistmsg = "Rule '%s' defined, but not used" whitelistmsg = "Rule %r defined, but not used"
whitelistargs = args[2:] whitelistargs = args[2:]
else: else:
whitelistmsg = msg whitelistmsg = msg
@ -6588,11 +6666,26 @@ class Parser(Tokenizer):
def finish(self): def finish(self):
# If we have interfaces that are iterable, create their # If we have interfaces that are iterable, create their
# iterator interfaces and add them to the productions array. # iterator interfaces and add them to the productions array.
interfaceStatements = [p for p in self._productions if interfaceStatements = []
isinstance(p, IDLInterface)] for p in self._productions:
if isinstance(p, IDLInterface):
interfaceStatements.append(p)
if p.identifier.name == "Navigator":
navigatorInterface = p
iterableIteratorIface = None iterableIteratorIface = None
for iface in interfaceStatements: 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 = IDLPartialInterface(iface.location,
IDLUnresolvedIdentifier(iface.location, "Navigator"),
[ navigatorProperty ],
navigatorInterface)
self._productions.append(partialInterface)
iterable = None iterable = None
# We haven't run finish() on the interface yet, so we don't know # We haven't run finish() on the interface yet, so we don't know
# whether our interface is maplike/setlike/iterable or not. This # whether our interface is maplike/setlike/iterable or not. This

View file

@ -1,11 +1,11 @@
--- WebIDL.py --- WebIDL.py
+++ WebIDL.py +++ WebIDL.py
@@ -1416,7 +1416,8 @@ @@ -1416,7 +1416,8 @@
identifier == "Unforgeable" or
identifier == "UnsafeInPrerendering" or
identifier == "LegacyEventInit" or identifier == "LegacyEventInit" or
- identifier == "ProbablyShortLivingObject"): identifier == "ProbablyShortLivingObject" or
+ identifier == "ProbablyShortLivingObject" or identifier == "LegacyUnenumerableNamedProperties" or
- identifier == "NonOrdinaryGetPrototypeOf"):
+ identifier == "NonOrdinaryGetPrototypeOf" or
+ identifier == "Abstract"): + identifier == "Abstract"):
# Known extended attributes that do not take values # Known extended attributes that do not take values
if not attr.noArguments(): if not attr.noArguments():

View file

@ -1,25 +0,0 @@
--- WebIDL.py
+++ WebIDL.py
@@ -1069,6 +1069,12 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
specialMembersSeen[memberType] = member
+ if (self.getExtendedAttribute("LegacyUnenumerableNamedProperties") and
+ "named getters" not in specialMembersSeen):
+ raise WebIDLError("[LegacyUnenumerableNamedProperties] used on an interface "
+ "without a named getter",
+ [self.location])
+
if self._isOnGlobalProtoChain:
# Make sure we have no named setters, creators, or deleters
for memberType in ["setter", "creator", "deleter"]:
@@ -1417,7 +1423,8 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
identifier == "UnsafeInPrerendering" or
identifier == "LegacyEventInit" or
identifier == "ProbablyShortLivingObject" or
- identifier == "Abstract"):
+ identifier == "Abstract" or
+ identifier == "LegacyUnenumerableNamedProperties"):
# Known extended attributes that do not take values
if not attr.noArguments():
raise WebIDLError("[%s] must take no arguments" % identifier,

View file

@ -0,0 +1,58 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
def should_throw(parser, harness, message, code):
parser = parser.reset();
threw = False
try:
parser.parse(code)
parser.finish()
except:
threw = True
harness.ok(threw, "Should have thrown: %s" % message)
def WebIDLTest(parser, harness):
# The [LenientSetter] extended attribute MUST take no arguments.
should_throw(parser, harness, "no arguments", """
interface I {
[LenientSetter=X] readonly attribute long A;
};
""")
# An attribute with the [LenientSetter] extended attribute MUST NOT
# also be declared with the [PutForwards] extended attribute.
should_throw(parser, harness, "PutForwards", """
interface I {
[PutForwards=B, LenientSetter] readonly attribute J A;
};
interface J {
attribute long B;
};
""")
# An attribute with the [LenientSetter] extended attribute MUST NOT
# also be declared with the [Replaceable] extended attribute.
should_throw(parser, harness, "Replaceable", """
interface I {
[Replaceable, LenientSetter] readonly attribute J A;
};
""")
# The [LenientSetter] extended attribute MUST NOT be used on an
# attribute that is not read only.
should_throw(parser, harness, "writable attribute", """
interface I {
[LenientSetter] attribute long A;
};
""")
# The [LenientSetter] extended attribute MUST NOT be used on a
# static attribute.
should_throw(parser, harness, "static attribute", """
interface I {
[LenientSetter] static readonly attribute long A;
};
""")

View file

@ -0,0 +1,64 @@
def WebIDLTest(parser, harness):
parser.parse(
"""
interface Foo {};
[LegacyUnenumerableNamedProperties]
interface Bar : Foo {
getter long(DOMString name);
};
interface Baz : Bar {
getter long(DOMString name);
};
""");
results = parser.finish();
harness.check(len(results), 3, "Should have three interfaces")
parser = parser.reset()
threw = False
try:
parser.parse("""
[LegacyUnenumerableNamedProperties]
interface NoNamedGetter {
};
""")
results = parser.finish()
except Exception, x:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse("""
[LegacyUnenumerableNamedProperties=Foo]
interface ShouldNotHaveArg {
getter long(DOMString name);
};
""")
results = parser.finish()
except Exception, x:
threw = True
harness.ok(threw, "Should have thrown.")
parser = parser.reset()
threw = False
try:
parser.parse("""
[LegacyUnenumerableNamedProperties]
interface Foo {
getter long(DOMString name);
};
interface Bar : Foo {};
[LegacyUnenumerableNamedProperties]
interface Baz : Bar {
getter long(DOMString name);
};
""")
results = parser.finish()
except Exception, x:
threw = True
harness.ok(threw, "Should have thrown.")

View file

@ -1,7 +1,6 @@
wget https://mxr.mozilla.org/mozilla-central/source/dom/bindings/parser/WebIDL.py?raw=1 -O WebIDL.py wget https://mxr.mozilla.org/mozilla-central/source/dom/bindings/parser/WebIDL.py?raw=1 -O WebIDL.py
patch < abstract.patch patch < abstract.patch
patch < debug.patch patch < debug.patch
patch < legacy-unenumerable-named-properties.patch
wget https://hg.mozilla.org/mozilla-central/archive/tip.tar.gz/dom/bindings/parser/tests/ -O tests.tar.gz wget https://hg.mozilla.org/mozilla-central/archive/tip.tar.gz/dom/bindings/parser/tests/ -O tests.tar.gz
rm -r tests rm -r tests

View file

@ -3,7 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// https://html.spec.whatwg.org/multipage/#htmlformcontrolscollection // https://html.spec.whatwg.org/multipage/#htmlformcontrolscollection
[LegacyUnenumerableNamedProperties]
interface HTMLFormControlsCollection : HTMLCollection { interface HTMLFormControlsCollection : HTMLCollection {
// inherits length and item() // inherits length and item()
getter (RadioNodeList or Element)? namedItem(DOMString name); // shadows inherited namedItem() getter (RadioNodeList or Element)? namedItem(DOMString name); // shadows inherited namedItem()