Update the WebIDL parser

This commit is contained in:
Anthony Ramine 2018-04-03 14:06:07 +02:00
parent 866a523914
commit 938f1362e7
12 changed files with 104 additions and 244 deletions

View file

@ -1095,12 +1095,12 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
testInterface = testInterface.parent
# Ensure that there's at most one of each {named,indexed}
# {getter,setter,creator,deleter}, at most one stringifier,
# {getter,setter,deleter}, at most one stringifier,
# and at most one legacycaller. Note that this last is not
# quite per spec, but in practice no one overloads
# legacycallers. Also note that in practice we disallow
# indexed deleters, but it simplifies some other code to
# treat deleter analogously to getter/setter/creator by
# treat deleter analogously to getter/setter by
# prefixing it with "named".
specialMembersSeen = {}
for member in self.members:
@ -1111,8 +1111,6 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
memberType = "getters"
elif member.isSetter():
memberType = "setters"
elif member.isCreator():
memberType = "creators"
elif member.isDeleter():
memberType = "deleters"
elif member.isStringifier():
@ -1158,8 +1156,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
ancestor = ancestor.parent
if self._isOnGlobalProtoChain:
# Make sure we have no named setters, creators, or deleters
for memberType in ["setter", "creator", "deleter"]:
# Make sure we have no named setters or deleters
for memberType in ["setter", "deleter"]:
memberId = "named " + memberType + "s"
if memberId in specialMembersSeen:
raise WebIDLError("Interface with [Global] has a named %s" %
@ -1183,6 +1181,22 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
parent = parent.parent
def validate(self):
def checkDuplicateNames(member, name, attributeName):
for m in self.members:
if m.identifier.name == name:
raise WebIDLError("[%s=%s] has same name as interface member" %
(attributeName, name),
[member.location, m.location])
if m.isMethod() and m != member and name in m.aliases:
raise WebIDLError("conflicting [%s=%s] definitions" %
(attributeName, name),
[member.location, m.location])
if m.isAttr() and m != member and name in m.bindingAliases:
raise WebIDLError("conflicting [%s=%s] definitions" %
(attributeName, name),
[member.location, m.location])
# We don't support consequential unforgeable interfaces. Need to check
# this here, because in finish() an interface might not know yet that
# it's consequential.
@ -1295,15 +1309,15 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins):
raise WebIDLError("[Alias] must not be used on an "
"[Unforgeable] operation",
[member.location])
for m in self.members:
if m.identifier.name == alias:
raise WebIDLError("[Alias=%s] has same name as "
"interface member" % alias,
[member.location, m.location])
if m.isMethod() and m != member and alias in m.aliases:
raise WebIDLError("duplicate [Alias=%s] definitions" %
alias,
[member.location, m.location])
checkDuplicateNames(member, alias, "Alias")
# Check that the name of a [BindingAlias] doesn't conflict with an
# interface member.
if member.isAttr():
for bindingAlias in member.bindingAliases:
checkDuplicateNames(member, bindingAlias, "BindingAlias")
# Conditional exposure makes no sense for interfaces with no
# interface object, unless they're navigator properties.
@ -1720,10 +1734,10 @@ class IDLInterface(IDLInterfaceOrNamespace):
identifier == "OverrideBuiltins" or
identifier == "ChromeOnly" or
identifier == "Unforgeable" or
identifier == "UnsafeInPrerendering" or
identifier == "LegacyEventInit" or
identifier == "ProbablyShortLivingWrapper" or
identifier == "LegacyUnenumerableNamedProperties" or
identifier == "RunConstructorInCallerCompartment" or
identifier == "NonOrdinaryGetPrototypeOf" or
identifier == "Abstract" or
identifier == "Inline"):
@ -1780,10 +1794,16 @@ class IDLNamespace(IDLInterfaceOrNamespace):
if not attr.hasValue():
raise WebIDLError("[%s] must have a value" % identifier,
[attr.location])
elif identifier == "ProtoObjectHack":
elif (identifier == "ProtoObjectHack" or
identifier == "ChromeOnly"):
if not attr.noArguments():
raise WebIDLError("[%s] must not have arguments" % identifier,
[attr.location])
elif identifier == "Pref":
# Known extended attributes that take a string value
if not attr.hasValue():
raise WebIDLError("[%s] must have a value" % identifier,
[attr.location])
else:
raise WebIDLError("Unknown extended attribute %s on namespace" %
identifier,
@ -3581,6 +3601,11 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
[self.location])
self.aliases.append(alias)
def _addBindingAlias(self, bindingAlias):
if bindingAlias in self.bindingAliases:
raise WebIDLError("Duplicate [BindingAlias=%s] on attribute" % bindingAlias,
[self.location])
self.bindingAliases.append(bindingAlias)
class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
@ -3701,6 +3726,11 @@ class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
if isIteratorAlias:
method.addExtendedAttributes(
[IDLExtendedAttribute(self.location, ("Alias", "@@iterator"))])
# Methods generated for iterables should be enumerable, but the ones for
# maplike/setlike should not be.
if not self.isIterable():
method.addExtendedAttributes(
[IDLExtendedAttribute(self.location, ("NonEnumerable",))])
members.append(method)
def resolve(self, parentScope):
@ -3824,11 +3854,15 @@ class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
specification during parsing.
"""
# Both maplike and setlike have a size attribute
members.append(IDLAttribute(self.location,
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
True,
maplikeOrSetlike=self))
sizeAttr = IDLAttribute(self.location,
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
True,
maplikeOrSetlike=self)
# This should be non-enumerable.
sizeAttr.addExtendedAttributes(
[IDLExtendedAttribute(self.location, ("NonEnumerable",))])
members.append(sizeAttr)
self.reserved_ro_names = ["size"]
self.disallowedMemberNames.append("size")
@ -3964,7 +3998,9 @@ class IDLConst(IDLInterfaceMember):
elif (identifier == "Pref" or
identifier == "ChromeOnly" or
identifier == "Func" or
identifier == "SecureContext"):
identifier == "SecureContext" or
identifier == "NonEnumerable" or
identifier == "NeedsWindowsUndef"):
# Known attributes that we don't need to do anything with here
pass
else:
@ -4000,6 +4036,7 @@ class IDLAttribute(IDLInterfaceMember):
self.dependsOn = "Everything"
self.affects = "Everything"
self.navigatorObjectGetter = navigatorObjectGetter
self.bindingAliases = []
if static and identifier.name == "prototype":
raise WebIDLError("The identifier of a static attribute must not be 'prototype'",
@ -4138,11 +4175,17 @@ class IDLAttribute(IDLInterfaceMember):
def handleExtendedAttribute(self, attr):
identifier = attr.identifier()
if ((identifier == "SetterThrows" or identifier == "SetterCanOOM")
if ((identifier == "SetterThrows" or identifier == "SetterCanOOM" or
identifier == "SetterNeedsSubjectPrincipal")
and self.readonly):
raise WebIDLError("Readonly attributes must not be flagged as "
"[%s]" % identifier,
[self.location])
elif identifier == "BindingAlias":
if not attr.hasValue():
raise WebIDLError("[BindingAlias] takes an identifier or string",
[attr.location])
self._addBindingAlias(attr.value())
elif (((identifier == "Throws" or identifier == "GetterThrows" or
identifier == "CanOOM" or identifier == "GetterCanOOM") and
self.getExtendedAttribute("StoreInSlot")) or
@ -4338,11 +4381,13 @@ class IDLAttribute(IDLInterfaceMember):
identifier == "SecureContext" or
identifier == "Frozen" or
identifier == "NewObject" or
identifier == "UnsafeInPrerendering" or
identifier == "NeedsSubjectPrincipal" or
identifier == "SetterNeedsSubjectPrincipal" or
identifier == "GetterNeedsSubjectPrincipal" or
identifier == "NeedsCallerType" or
identifier == "ReturnValueNeedsContainsHack" or
identifier == "BinaryName"):
identifier == "BinaryName" or
identifier == "NonEnumerable"):
# Known attributes that we don't need to do anything with here
pass
else:
@ -4606,7 +4651,6 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
Special = enum(
'Getter',
'Setter',
'Creator',
'Deleter',
'LegacyCaller',
base=IDLInterfaceMember.Special
@ -4619,7 +4663,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
)
def __init__(self, location, identifier, returnType, arguments,
static=False, getter=False, setter=False, creator=False,
static=False, getter=False, setter=False,
deleter=False, specialType=NamedOrIndexed.Neither,
legacycaller=False, stringifier=False, jsonifier=False,
maplikeOrSetlikeOrIterable=None, htmlConstructor=False):
@ -4640,8 +4684,6 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
self._getter = getter
assert isinstance(setter, bool)
self._setter = setter
assert isinstance(creator, bool)
self._creator = creator
assert isinstance(deleter, bool)
self._deleter = deleter
assert isinstance(legacycaller, bool)
@ -4682,7 +4724,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
assert not arguments[0].optional and not arguments[0].variadic
assert not self._getter or not overload.returnType.isVoid()
if self._setter or self._creator:
if self._setter:
assert len(self._overloads) == 1
arguments = self._overloads[0].arguments
assert len(arguments) == 2
@ -4715,9 +4757,6 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def isSetter(self):
return self._setter
def isCreator(self):
return self._creator
def isDeleter(self):
return self._deleter
@ -4750,7 +4789,6 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def isSpecial(self):
return (self.isGetter() or
self.isSetter() or
self.isCreator() or
self.isDeleter() or
self.isLegacycaller() or
self.isStringifier() or
@ -4806,8 +4844,6 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
assert not method.isGetter()
assert not self.isSetter()
assert not method.isSetter()
assert not self.isCreator()
assert not method.isCreator()
assert not self.isDeleter()
assert not method.isDeleter()
assert not self.isStringifier()
@ -4984,7 +5020,9 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
if (identifier == "GetterThrows" or
identifier == "SetterThrows" or
identifier == "GetterCanOOM" or
identifier == "SetterCanOOM"):
identifier == "SetterCanOOM" or
identifier == "SetterNeedsSubjectPrincipal" or
identifier == "GetterNeedsSubjectPrincipal"):
raise WebIDLError("Methods must not be flagged as "
"[%s]" % identifier,
[attr.location, self.location])
@ -5071,7 +5109,6 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
identifier == "CanOOM" or
identifier == "NewObject" or
identifier == "ChromeOnly" or
identifier == "UnsafeInPrerendering" or
identifier == "Pref" or
identifier == "Deprecated" or
identifier == "Func" or
@ -5079,7 +5116,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
identifier == "BinaryName" or
identifier == "NeedsSubjectPrincipal" or
identifier == "NeedsCallerType" or
identifier == "StaticClassOverride"):
identifier == "StaticClassOverride" or
identifier == "NonEnumerable"):
# Known attributes that we don't need to do anything with here
pass
else:
@ -5262,7 +5300,6 @@ class Tokenizer(object):
"static": "STATIC",
"getter": "GETTER",
"setter": "SETTER",
"creator": "CREATOR",
"deleter": "DELETER",
"legacycaller": "LEGACYCALLER",
"optional": "OPTIONAL",
@ -5977,13 +6014,12 @@ class Parser(Tokenizer):
getter = True if IDLMethod.Special.Getter in p[1] else False
setter = True if IDLMethod.Special.Setter in p[1] else False
creator = True if IDLMethod.Special.Creator in p[1] else False
deleter = True if IDLMethod.Special.Deleter in p[1] else False
legacycaller = True if IDLMethod.Special.LegacyCaller in p[1] else False
if getter or deleter:
if setter or creator:
raise WebIDLError("getter and deleter are incompatible with setter and creator",
if setter:
raise WebIDLError("getter and deleter are incompatible with setter",
[self.getLocation(p, 1)])
(returnType, identifier, arguments) = p[2]
@ -6018,10 +6054,9 @@ class Parser(Tokenizer):
if returnType.isVoid():
raise WebIDLError("getter cannot have void return type",
[self.getLocation(p, 2)])
if setter or creator:
if setter:
if len(arguments) != 2:
raise WebIDLError("%s has wrong number of arguments" %
("setter" if setter else "creator"),
raise WebIDLError("setter has wrong number of arguments",
[self.getLocation(p, 2)])
argType = arguments[0].type
if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
@ -6029,18 +6064,15 @@ class Parser(Tokenizer):
elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
specialType = IDLMethod.NamedOrIndexed.Indexed
else:
raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" %
("setter" if setter else "creator"),
raise WebIDLError("settter has wrong argument type (must be DOMString or UnsignedLong)",
[arguments[0].location])
if arguments[0].optional or arguments[0].variadic:
raise WebIDLError("%s cannot have %s argument" %
("setter" if setter else "creator",
"optional" if arguments[0].optional else "variadic"),
raise WebIDLError("setter cannot have %s argument" %
("optional" if arguments[0].optional else "variadic"),
[arguments[0].location])
if arguments[1].optional or arguments[1].variadic:
raise WebIDLError("%s cannot have %s argument" %
("setter" if setter else "creator",
"optional" if arguments[1].optional else "variadic"),
raise WebIDLError("setter cannot have %s argument" %
("optional" if arguments[1].optional else "variadic"),
[arguments[1].location])
if stringifier:
@ -6053,7 +6085,7 @@ class Parser(Tokenizer):
# identifier might be None. This is only permitted for special methods.
if not identifier:
if (not getter and not setter and not creator and
if (not getter and not setter and
not deleter and not legacycaller and not stringifier):
raise WebIDLError("Identifier required for non-special methods",
[self.getLocation(p, 2)])
@ -6061,19 +6093,18 @@ class Parser(Tokenizer):
location = BuiltinLocation("<auto-generated-identifier>")
identifier = IDLUnresolvedIdentifier(
location,
"__%s%s%s%s%s%s%s" %
"__%s%s%s%s%s%s" %
("named" if specialType == IDLMethod.NamedOrIndexed.Named else
"indexed" if specialType == IDLMethod.NamedOrIndexed.Indexed else "",
"getter" if getter else "",
"setter" if setter else "",
"deleter" if deleter else "",
"creator" if creator else "",
"legacycaller" if legacycaller else "",
"stringifier" if stringifier else ""),
allowDoubleUnderscore=True)
method = IDLMethod(self.getLocation(p, 2), identifier, returnType, arguments,
static=static, getter=getter, setter=setter, creator=creator,
static=static, getter=getter, setter=setter,
deleter=deleter, specialType=specialType,
legacycaller=legacycaller, stringifier=stringifier)
p[0] = method
@ -6149,12 +6180,6 @@ class Parser(Tokenizer):
"""
p[0] = IDLMethod.Special.Setter
def p_SpecialCreator(self, p):
"""
Special : CREATOR
"""
p[0] = IDLMethod.Special.Creator
def p_SpecialDeleter(self, p):
"""
Special : DELETER
@ -6246,7 +6271,6 @@ class Parser(Tokenizer):
| ATTRIBUTE
| CALLBACK
| CONST
| CREATOR
| DELETER
| DICTIONARY
| ENUM
@ -6396,7 +6420,6 @@ class Parser(Tokenizer):
| BYTE
| LEGACYCALLER
| CONST
| CREATOR
| DELETER
| DOUBLE
| EXCEPTION