Bug 1578173 part 1. Add support for constructor operations in the parser. r=edgar

The grammar changes parallel those in https://github.com/heycam/webidl/pull/700

We don't prevent having both a constructor operation and [Constructor] or
[ChromeConstructor], because those extended attributes are about to get
removed, once they are no longer used in our IDL.

Differential Revision: https://phabricator.services.mozilla.com/D45387

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Boris Zbarsky 2019-09-11 20:44:01 +00:00
Родитель c873e19b84
Коммит c0dd336d70
2 изменённых файлов: 369 добавлений и 35 удалений

Просмотреть файл

@ -1077,9 +1077,20 @@ class IDLInterfaceOrNamespace(IDLInterfaceOrInterfaceMixinOrNamespace):
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])
assert(len(ctor._exposureGlobalNames) == 0 or
ctor._exposureGlobalNames == self._exposureGlobalNames)
ctor._exposureGlobalNames.update(self._exposureGlobalNames)
ctor.finish(scope)
if ctor in self.members:
# constructor operation.
self.members.remove(ctor)
else:
# extended attribute.
ctor.finish(scope)
for ctor in self.namedConstructors:
assert len(ctor._exposureGlobalNames) == 0
@ -1741,19 +1752,15 @@ class IDLInterface(IDLInterfaceOrNamespace):
name = attr.value()
allowForbidden = False
methodIdentifier = IDLUnresolvedIdentifier(self.location, name,
allowForbidden=allowForbidden)
method = IDLConstructor(
attr.location, args, name,
htmlConstructor=(identifier == "HTMLConstructor"))
method.reallyInit(self)
method = IDLMethod(self.location, methodIdentifier, retType,
args, static=True,
htmlConstructor=(identifier == "HTMLConstructor"))
# 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.
# 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",))])
[IDLExtendedAttribute(self.location, ("Throws",))])
if identifier == "ChromeConstructor":
method.addExtendedAttributes(
[IDLExtendedAttribute(self.location, ("ChromeOnly",))])
@ -1887,6 +1894,17 @@ class IDLInterface(IDLInterfaceOrNamespace):
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):
@ -5481,6 +5499,52 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
return deps
class IDLConstructor(IDLMethod):
def __init__(self, location, args, name, htmlConstructor=False):
# 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._htmlConstructor = htmlConstructor
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"):
IDLMethod.handleExtendedAttribute(self, attr)
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, htmlConstructor=self._htmlConstructor)
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 IDLImplementsStatement(IDLObject):
def __init__(self, location, implementor, implementee):
IDLObject.__init__(self, location)
@ -6061,7 +6125,7 @@ class Parser(Tokenizer):
def p_PartialInterfaceRest(self, p):
"""
PartialInterfaceRest : IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
PartialInterfaceRest : IDENTIFIER LBRACE PartialInterfaceMembers RBRACE SEMICOLON
"""
location = self.getLocation(p, 1)
identifier = IDLUnresolvedIdentifier(location, p[1])
@ -6142,8 +6206,38 @@ 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]

Просмотреть файл

@ -57,26 +57,54 @@ def WebIDLTest(parser, harness):
};
""")
results = parser.finish()
harness.check(len(results), 3, "Should be three productions")
harness.ok(isinstance(results[0], WebIDL.IDLInterface),
"Should be an IDLInterface")
harness.ok(isinstance(results[1], WebIDL.IDLInterface),
"Should be an IDLInterface")
harness.ok(isinstance(results[2], WebIDL.IDLInterface),
"Should be an IDLInterface")
checkMethod(results[0].ctor(), "::TestConstructorNoArgs::constructor",
"constructor", [("TestConstructorNoArgs (Wrapper)", [])])
checkMethod(results[1].ctor(), "::TestConstructorWithArgs::constructor",
"constructor",
[("TestConstructorWithArgs (Wrapper)",
[("::TestConstructorWithArgs::constructor::name", "name", "String", False, False)])])
checkMethod(results[2].ctor(), "::TestConstructorOverloads::constructor",
"constructor",
[("TestConstructorOverloads (Wrapper)",
[("::TestConstructorOverloads::constructor::foo", "foo", "Object", False, False)]),
("TestConstructorOverloads (Wrapper)",
[("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])])
def checkResults(results):
harness.check(len(results), 3, "Should be three productions")
harness.ok(isinstance(results[0], WebIDL.IDLInterface),
"Should be an IDLInterface")
harness.ok(isinstance(results[1], WebIDL.IDLInterface),
"Should be an IDLInterface")
harness.ok(isinstance(results[2], WebIDL.IDLInterface),
"Should be an IDLInterface")
checkMethod(results[0].ctor(), "::TestConstructorNoArgs::constructor",
"constructor", [("TestConstructorNoArgs (Wrapper)", [])])
harness.check(len(results[0].members), 0,
"TestConstructorNoArgs should not have members")
checkMethod(results[1].ctor(), "::TestConstructorWithArgs::constructor",
"constructor",
[("TestConstructorWithArgs (Wrapper)",
[("::TestConstructorWithArgs::constructor::name", "name", "String", False, False)])])
harness.check(len(results[1].members), 0,
"TestConstructorWithArgs should not have members")
checkMethod(results[2].ctor(), "::TestConstructorOverloads::constructor",
"constructor",
[("TestConstructorOverloads (Wrapper)",
[("::TestConstructorOverloads::constructor::foo", "foo", "Object", False, False)]),
("TestConstructorOverloads (Wrapper)",
[("::TestConstructorOverloads::constructor::bar", "bar", "Boolean", False, False)])])
harness.check(len(results[2].members), 0,
"TestConstructorOverloads should not have members")
checkResults(results)
parser = parser.reset()
parser.parse("""
interface TestConstructorNoArgs {
constructor();
};
interface TestConstructorWithArgs {
constructor(DOMString name);
};
interface TestConstructorOverloads {
constructor(object foo);
constructor(boolean bar);
};
""")
results = parser.finish()
checkResults(results)
parser = parser.reset()
parser.parse("""
@ -93,6 +121,21 @@ def WebIDLTest(parser, harness):
"constructor", [("TestChromeConstructor (Wrapper)", [])],
chromeOnly=True)
parser = parser.reset()
parser.parse("""
interface TestChromeConstructor {
[ChromeOnly] constructor();
};
""")
results = parser.finish()
harness.check(len(results), 1, "Should be one production")
harness.ok(isinstance(results[0], WebIDL.IDLInterface),
"Should be an IDLInterface")
checkMethod(results[0].ctor(), "::TestChromeConstructor::constructor",
"constructor", [("TestChromeConstructor (Wrapper)", [])],
chromeOnly=True)
parser = parser.reset()
parser.parse("""
[HTMLConstructor]
@ -193,6 +236,7 @@ def WebIDLTest(parser, harness):
interface TestHTMLConstructorAndConstructor {
};
""")
results = parser.finish()
except:
threw = True
@ -207,11 +251,79 @@ def WebIDLTest(parser, harness):
interface TestHTMLConstructorAndConstructor {
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a Constructor")
# Test HTMLConstructor and constructor operation
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor]
interface TestHTMLConstructorAndConstructor {
constructor();
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw, "Can't have both a constructor and a HTMLConstructor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor]
interface TestHTMLConstructorAndConstructor {
[Throws]
constructor();
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Can't have both a throwing constructor and a HTMLConstructor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor]
interface TestHTMLConstructorAndConstructor {
constructor(DOMString a);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Can't have both a HTMLConstructor and a constructor operation")
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor]
interface TestHTMLConstructorAndConstructor {
[Throws]
constructor(DOMString a);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Can't have both a HTMLConstructor and a throwing constructor "
"operation")
# Test HTMLConstructor and ChromeConstructor
parser = parser.reset()
threw = False
@ -270,3 +382,131 @@ def WebIDLTest(parser, harness):
threw = True
harness.ok(threw, "Can't have both a HTMLConstructor and a ChromeConstructor")
# Test HTMLConstructor and [ChromeOnly] constructor operation
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor]
interface TestHTMLConstructorAndConstructor {
[ChromeOnly]
constructor();
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Can't have both a ChromeOnly constructor and a HTMLConstructor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor]
interface TestHTMLConstructorAndConstructor {
[Throws, ChromeOnly]
constructor();
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Can't have both a throwing chromeonly constructor and a "
"HTMLConstructor")
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor]
interface TestHTMLConstructorAndConstructor {
[ChromeOnly]
constructor(DOMString a);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Can't have both a HTMLConstructor and a chromeonly constructor "
"operation")
parser = parser.reset()
threw = False
try:
parser.parse("""
[HTMLConstructor]
interface TestHTMLConstructorAndConstructor {
[Throws, ChromeOnly]
constructor(DOMString a);
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Can't have both a HTMLConstructor and a throwing chromeonly "
"constructor operation")
parser = parser.reset()
threw = False
try:
parser.parse("""
[NoInterfaceObject]
interface InterfaceWithoutInterfaceObject {
constructor();
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Can't have a constructor operation on a [NoInterfaceObject] "
"interface")
parser = parser.reset()
threw = False
try:
parser.parse("""
interface InterfaceWithPartial {
};
partial interface InterfaceWithPartial {
constructor();
};
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Can't have a constructor operation on a partial interface")
parser = parser.reset()
threw = False
try:
parser.parse("""
interface InterfaceWithMixin {
};
interface mixin Mixin {
constructor();
};
InterfaceWithMixin includes Mixin
""")
results = parser.finish()
except:
threw = True
harness.ok(threw,
"Can't have a constructor operation on a mixin")