Update the WebIDL parser.

This commit is contained in:
Ms2ger 2015-03-13 16:48:48 +01:00
parent 2382d71242
commit 4157a2b02b
5 changed files with 339 additions and 105 deletions

View file

@ -204,7 +204,7 @@ class IDLObject(object):
deps.add(self.filename())
for d in self._getDependentObjects():
deps = deps.union(d.getDeps(visited))
deps.update(d.getDeps(visited))
return deps
@ -338,7 +338,10 @@ class IDLUnresolvedIdentifier(IDLObject):
assert len(name) > 0
if name[:2] == "__" and name != "__content" and name != "___noSuchMethod__" and not allowDoubleUnderscore:
if name == "__noSuchMethod__":
raise WebIDLError("__noSuchMethod__ is deprecated", [location])
if name[:2] == "__" and name != "__content" and not allowDoubleUnderscore:
raise WebIDLError("Identifiers beginning with __ are reserved",
[location])
if name[0] == '_' and not allowDoubleUnderscore:
@ -448,7 +451,59 @@ class IDLIdentifierPlaceholder(IDLObjectWithIdentifier):
obj = self.identifier.resolve(scope, None)
return scope.lookupIdentifier(obj)
class IDLExternalInterface(IDLObjectWithIdentifier):
class IDLExposureMixins():
def __init__(self, location):
# _exposureGlobalNames are the global names listed in our [Exposed]
# extended attribute. exposureSet is the exposure set as defined in the
# Web IDL spec: it contains interface names.
self._exposureGlobalNames = set()
self.exposureSet = set()
self._location = location
self._globalScope = None
def finish(self, scope):
assert scope.parentScope is None
self._globalScope = scope
# Verify that our [Exposed] value, if any, makes sense.
for globalName in self._exposureGlobalNames:
if globalName not in scope.globalNames:
raise WebIDLError("Unknown [Exposed] value %s" % globalName,
[self._location])
if len(self._exposureGlobalNames) == 0:
self._exposureGlobalNames.add(scope.primaryGlobalName)
globalNameSetToExposureSet(scope, self._exposureGlobalNames,
self.exposureSet)
def isExposedInWindow(self):
return 'Window' in self.exposureSet
def isExposedInAnyWorker(self):
return len(self.getWorkerExposureSet()) > 0
def isExposedInSystemGlobals(self):
return 'BackstagePass' in self.exposureSet
def isExposedInSomeButNotAllWorkers(self):
"""
Returns true if the Exposed extended attribute for this interface
exposes it in some worker globals but not others. The return value does
not depend on whether the interface is exposed in Window or System
globals.
"""
if not self.isExposedInAnyWorker():
return False
workerScopes = self.parentScope.globalNameMapping["Worker"]
return len(workerScopes.difference(self.exposureSet)) > 0
def getWorkerExposureSet(self):
workerScopes = self._globalScope.globalNameMapping["Worker"]
return workerScopes.intersection(self.exposureSet)
class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
def __init__(self, location, parentScope, identifier):
raise WebIDLError("Servo does not support external interfaces.",
[self.location])
@ -511,7 +566,7 @@ def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
for name in nameSet:
exposureSet.update(globalScope.globalNameMapping[name])
class IDLInterface(IDLObjectWithScope):
class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
def __init__(self, location, parentScope, name, parent, members,
isKnownNonPartial):
assert isinstance(parentScope, IDLScope)
@ -546,13 +601,9 @@ class IDLInterface(IDLObjectWithScope):
self.totalMembersInSlots = 0
# Tracking of the number of own own members we have in slots
self._ownMembersInSlots = 0
# _exposureGlobalNames are the global names listed in our [Exposed]
# extended attribute. exposureSet is the exposure set as defined in the
# Web IDL spec: it contains interface names.
self._exposureGlobalNames = set()
self.exposureSet = set()
IDLObjectWithScope.__init__(self, location, parentScope, name)
IDLExposureMixins.__init__(self, location)
if isKnownNonPartial:
self.setNonPartial(location, parent, members)
@ -592,17 +643,7 @@ class IDLInterface(IDLObjectWithScope):
"declaration" % self.identifier.name,
[self.location])
# Verify that our [Exposed] value, if any, makes sense.
for globalName in self._exposureGlobalNames:
if globalName not in scope.globalNames:
raise WebIDLError("Unknown [Exposed] value %s" % globalName,
[self.location])
if len(self._exposureGlobalNames) == 0:
self._exposureGlobalNames.add(scope.primaryGlobalName)
globalNameSetToExposureSet(scope, self._exposureGlobalNames,
self.exposureSet)
IDLExposureMixins.finish(self, scope)
# Now go ahead and merge in our partial interfaces.
for partial in self._partialInterfaces:
@ -681,6 +722,17 @@ class IDLInterface(IDLObjectWithScope):
self.parent.identifier.name),
[self.location, self.parent.location])
# Interfaces which have interface objects can't inherit
# from [NoInterfaceObject] interfaces.
if (self.parent.getExtendedAttribute("NoInterfaceObject") and
not self.getExtendedAttribute("NoInterfaceObject")):
raise WebIDLError("Interface %s does not have "
"[NoInterfaceObject] but inherits from "
"interface %s which does" %
(self.identifier.name,
self.parent.identifier.name),
[self.location, self.parent.location])
for iface in self.implementedInterfaces:
iface.finish(scope)
@ -718,9 +770,13 @@ class IDLInterface(IDLObjectWithScope):
ctor = self.ctor()
if ctor is not None:
assert len(ctor._exposureGlobalNames) == 0
ctor._exposureGlobalNames.update(self._exposureGlobalNames)
ctor.finish(scope)
for ctor in self.namedConstructors:
assert len(ctor._exposureGlobalNames) == 0
ctor._exposureGlobalNames.update(self._exposureGlobalNames)
ctor.finish(scope)
# Make a copy of our member list, so things that implement us
@ -921,10 +977,15 @@ class IDLInterface(IDLObjectWithScope):
list(i.location for i in
self.interfacesBasedOnSelf if i.parent == self))
for member in self.members:
member.validate()
if self.isCallback() and member.getExtendedAttribute("Replaceable"):
raise WebIDLError("[Replaceable] used on an attribute on "
"interface %s which is a callback interface" %
self.identifier.name,
[self.location, member.location])
# Check that PutForwards refers to another attribute and that no
# cycles exist in forwarded assignments.
if member.isAttr():
@ -966,10 +1027,25 @@ class IDLInterface(IDLObjectWithScope):
if (self.getExtendedAttribute("Pref") and
self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])):
raise WebIDLError("[Pref] used on an member that is not %s-only" %
raise WebIDLError("[Pref] used on an interface that is not %s-only" %
self.parentScope.primaryGlobalName,
[self.location])
if (self.getExtendedAttribute("CheckPermissions") and
self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])):
raise WebIDLError("[CheckPermissions] used on an interface that is "
"not %s-only" %
self.parentScope.primaryGlobalName,
[self.location])
# Conditional exposure makes no sense for interfaces with no
# interface object, unless they're navigator properties.
if (self.isExposedConditionally() and
not self.hasInterfaceObject() and
not self.getNavigatorProperty()):
raise WebIDLError("Interface with no interface object is "
"exposed conditionally",
[self.location])
def isInterface(self):
return True
@ -1151,10 +1227,11 @@ class IDLInterface(IDLObjectWithScope):
self.parentScope.globalNames.add(self.identifier.name)
self.parentScope.globalNameMapping[self.identifier.name].add(self.identifier.name)
self._isOnGlobalProtoChain = True
elif (identifier == "NeedNewResolve" or
elif (identifier == "NeedResolve" or
identifier == "OverrideBuiltins" or
identifier == "ChromeOnly" or
identifier == "Unforgeable" or
identifier == "UnsafeInPrerendering" or
identifier == "LegacyEventInit"):
# Known extended attributes that do not take values
if not attr.noArguments():
@ -1284,7 +1361,7 @@ class IDLInterface(IDLObjectWithScope):
def _getDependentObjects(self):
deps = set(self.members)
deps.union(self.implementedInterfaces)
deps.update(self.implementedInterfaces)
if self.parent:
deps.add(self.parent)
return deps
@ -1292,6 +1369,13 @@ class IDLInterface(IDLObjectWithScope):
def hasMembersInSlots(self):
return self._ownMembersInSlots != 0
def isExposedConditionally(self):
return (self.getExtendedAttribute("Pref") or
self.getExtendedAttribute("ChromeOnly") or
self.getExtendedAttribute("Func") or
self.getExtendedAttribute("AvailableIn") or
self.getExtendedAttribute("CheckPermissions"))
class IDLDictionary(IDLObjectWithScope):
def __init__(self, location, parentScope, name, parent, members):
assert isinstance(parentScope, IDLScope)
@ -1486,7 +1570,7 @@ class IDLType(IDLObject):
'any',
'domstring',
'bytestring',
'scalarvaluestring',
'usvstring',
'object',
'date',
'void',
@ -1539,7 +1623,7 @@ class IDLType(IDLObject):
def isDOMString(self):
return False
def isScalarValueString(self):
def isUSVString(self):
return False
def isVoid(self):
@ -1688,7 +1772,10 @@ class IDLNullableType(IDLType):
assert not innerType.isVoid()
assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any]
IDLType.__init__(self, location, innerType.name)
name = innerType.name
if innerType.isComplete():
name += "OrNull"
IDLType.__init__(self, location, name)
self.inner = innerType
self.builtin = False
@ -1722,8 +1809,8 @@ class IDLNullableType(IDLType):
def isDOMString(self):
return self.inner.isDOMString()
def isScalarValueString(self):
return self.inner.isScalarValueString()
def isUSVString(self):
return self.inner.isUSVString()
def isFloat(self):
return self.inner.isFloat()
@ -1801,7 +1888,7 @@ class IDLNullableType(IDLType):
"be a union type that itself has a nullable "
"type as a member type", [self.location])
self.name = self.inner.name
self.name = self.inner.name + "OrNull"
return self
def unroll(self):
@ -1824,6 +1911,10 @@ class IDLSequenceType(IDLType):
IDLType.__init__(self, location, parameterType.name)
self.inner = parameterType
self.builtin = False
# Need to set self.name up front if our inner type is already complete,
# since in that case our .complete() won't be called.
if self.inner.isComplete():
self.name = self.inner.name + "Sequence"
def __eq__(self, other):
return isinstance(other, IDLSequenceType) and self.inner == other.inner
@ -1846,7 +1937,7 @@ class IDLSequenceType(IDLType):
def isDOMString(self):
return False
def isScalarValueString(self):
def isUSVString(self):
return False
def isVoid(self):
@ -1885,7 +1976,7 @@ class IDLSequenceType(IDLType):
def complete(self, scope):
self.inner = self.inner.complete(scope)
self.name = self.inner.name
self.name = self.inner.name + "Sequence"
return self
def unroll(self):
@ -1896,7 +1987,8 @@ class IDLSequenceType(IDLType):
# Just forward to the union; it'll deal
return other.isDistinguishableFrom(self)
return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isDate() or other.isNonCallbackInterface() or other.isMozMap())
other.isDate() or other.isInterface() or other.isDictionary() or
other.isCallback() or other.isMozMap())
def _getDependentObjects(self):
return self.inner._getDependentObjects()
@ -1911,6 +2003,10 @@ class IDLMozMapType(IDLType):
IDLType.__init__(self, location, parameterType.name)
self.inner = parameterType
self.builtin = False
# Need to set self.name up front if our inner type is already complete,
# since in that case our .complete() won't be called.
if self.inner.isComplete():
self.name = self.inner.name + "MozMap"
def __eq__(self, other):
return isinstance(other, IDLMozMapType) and self.inner == other.inner
@ -1936,7 +2032,7 @@ class IDLMozMapType(IDLType):
def complete(self, scope):
self.inner = self.inner.complete(scope)
self.name = self.inner.name
self.name = self.inner.name + "MozMap"
return self
def unroll(self):
@ -1970,6 +2066,10 @@ class IDLUnionType(IDLType):
def __eq__(self, other):
return isinstance(other, IDLUnionType) and self.memberTypes == other.memberTypes
def __hash__(self):
assert self.isComplete()
return self.name.__hash__()
def isVoid(self):
return False
@ -2001,9 +2101,6 @@ class IDLUnionType(IDLType):
return typeName(type._identifier.object())
if isinstance(type, IDLObjectWithIdentifier):
return typeName(type.identifier)
if (isinstance(type, IDLType) and
(type.isArray() or type.isSequence() or type.isMozMap)):
return str(type)
return type.name
for (i, type) in enumerate(self.memberTypes):
@ -2114,7 +2211,7 @@ class IDLArrayType(IDLType):
def isDOMString(self):
return False
def isScalarValueString(self):
def isUSVString(self):
return False
def isVoid(self):
@ -2212,8 +2309,8 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
def isDOMString(self):
return self.inner.isDOMString()
def isScalarValueString(self):
return self.inner.isScalarValueString()
def isUSVString(self):
return self.inner.isUSVString()
def isVoid(self):
return self.inner.isVoid()
@ -2313,7 +2410,7 @@ class IDLWrapperType(IDLType):
def isDOMString(self):
return False
def isScalarValueString(self):
def isUSVString(self):
return False
def isVoid(self):
@ -2387,7 +2484,8 @@ class IDLWrapperType(IDLType):
other.isDate())
if self.isDictionary() and other.nullable():
return False
if other.isPrimitive() or other.isString() or other.isEnum() or other.isDate():
if (other.isPrimitive() or other.isString() or other.isEnum() or
other.isDate() or other.isSequence()):
return True
if self.isDictionary():
return other.isNonCallbackInterface()
@ -2405,7 +2503,7 @@ class IDLWrapperType(IDLType):
(self.isNonCallbackInterface() or
other.isNonCallbackInterface()))
if (other.isDictionary() or other.isCallback() or
other.isSequence() or other.isMozMap() or other.isArray()):
other.isMozMap() or other.isArray()):
return self.isNonCallbackInterface()
# Not much else |other| can be
@ -2441,6 +2539,13 @@ class IDLWrapperType(IDLType):
# Bindings.conf, which is still a global dependency.
# 2) Changing an interface to a dictionary (or vice versa) with the
# same identifier should be incredibly rare.
#
# On the other hand, if our type is a dictionary, we should
# depend on it, because the member types of a dictionary
# affect whether a method taking the dictionary as an argument
# takes a JSContext* argument or not.
if self.isDictionary():
return set([self.inner])
return set()
class IDLBuiltinType(IDLType):
@ -2466,7 +2571,7 @@ class IDLBuiltinType(IDLType):
'any',
'domstring',
'bytestring',
'scalarvaluestring',
'usvstring',
'object',
'date',
'void',
@ -2501,7 +2606,7 @@ class IDLBuiltinType(IDLType):
Types.any: IDLType.Tags.any,
Types.domstring: IDLType.Tags.domstring,
Types.bytestring: IDLType.Tags.bytestring,
Types.scalarvaluestring: IDLType.Tags.scalarvaluestring,
Types.usvstring: IDLType.Tags.usvstring,
Types.object: IDLType.Tags.object,
Types.date: IDLType.Tags.date,
Types.void: IDLType.Tags.void,
@ -2535,7 +2640,7 @@ class IDLBuiltinType(IDLType):
def isString(self):
return self._typeTag == IDLBuiltinType.Types.domstring or \
self._typeTag == IDLBuiltinType.Types.bytestring or \
self._typeTag == IDLBuiltinType.Types.scalarvaluestring
self._typeTag == IDLBuiltinType.Types.usvstring
def isByteString(self):
return self._typeTag == IDLBuiltinType.Types.bytestring
@ -2543,8 +2648,8 @@ class IDLBuiltinType(IDLType):
def isDOMString(self):
return self._typeTag == IDLBuiltinType.Types.domstring
def isScalarValueString(self):
return self._typeTag == IDLBuiltinType.Types.scalarvaluestring
def isUSVString(self):
return self._typeTag == IDLBuiltinType.Types.usvstring
def isInteger(self):
return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long
@ -2698,9 +2803,9 @@ BuiltinTypes = {
IDLBuiltinType.Types.bytestring:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "ByteString",
IDLBuiltinType.Types.bytestring),
IDLBuiltinType.Types.scalarvaluestring:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "ScalarValueString",
IDLBuiltinType.Types.scalarvaluestring),
IDLBuiltinType.Types.usvstring:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "USVString",
IDLBuiltinType.Types.usvstring),
IDLBuiltinType.Types.object:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Object",
IDLBuiltinType.Types.object),
@ -2835,10 +2940,10 @@ class IDLValue(IDLObject):
raise WebIDLError("Trying to convert unrestricted value %s to non-unrestricted"
% self.value, [location]);
return self
elif self.type.isString() and type.isScalarValueString():
# Allow ScalarValueStrings to use default value just like
elif self.type.isString() and type.isUSVString():
# Allow USVStrings to use default value just like
# DOMString. No coercion is required in this case as Codegen.py
# treats ScalarValueString just like DOMString, but with an
# treats USVString just like DOMString, but with an
# extra normalization step.
assert self.type.isDOMString()
return self
@ -2923,7 +3028,7 @@ class IDLUndefinedValue(IDLObject):
def _getDependentObjects(self):
return set()
class IDLInterfaceMember(IDLObjectWithIdentifier):
class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
Tags = enum(
'Const',
@ -2936,15 +3041,14 @@ class IDLInterfaceMember(IDLObjectWithIdentifier):
'Stringifier'
)
AffectsValues = ("Nothing", "Everything")
DependsOnValues = ("Nothing", "DOMState", "DeviceState", "Everything")
def __init__(self, location, identifier, tag):
IDLObjectWithIdentifier.__init__(self, location, None, identifier)
IDLExposureMixins.__init__(self, location)
self.tag = tag
self._extendedAttrDict = {}
# _exposureGlobalNames are the global names listed in our [Exposed]
# extended attribute. exposureSet is the exposure set as defined in the
# Web IDL spec: it contains interface names.
self._exposureGlobalNames = set()
self.exposureSet = set()
def isMethod(self):
return self.tag == IDLInterfaceMember.Tags.Method
@ -2968,21 +3072,53 @@ class IDLInterfaceMember(IDLObjectWithIdentifier):
return self._extendedAttrDict.get(name, None)
def finish(self, scope):
for globalName in self._exposureGlobalNames:
if globalName not in scope.globalNames:
raise WebIDLError("Unknown [Exposed] value %s" % globalName,
[self.location])
globalNameSetToExposureSet(scope, self._exposureGlobalNames,
self.exposureSet)
self._scope = scope
# We better be exposed _somewhere_.
if (len(self._exposureGlobalNames) == 0):
print self.identifier.name
assert len(self._exposureGlobalNames) != 0
IDLExposureMixins.finish(self, scope)
def validate(self):
if (self.getExtendedAttribute("Pref") and
self.exposureSet != set([self._scope.primaryGlobalName])):
self.exposureSet != set([self._globalScope.primaryGlobalName])):
raise WebIDLError("[Pref] used on an interface member that is not "
"%s-only" % self._scope.primaryGlobalName,
"%s-only" % self._globalScope.primaryGlobalName,
[self.location])
if (self.getExtendedAttribute("CheckPermissions") and
self.exposureSet != set([self._globalScope.primaryGlobalName])):
raise WebIDLError("[CheckPermissions] used on an interface member "
"that is not %s-only" %
self._globalScope.primaryGlobalName,
[self.location])
if self.isAttr() or self.isMethod():
if self.affects == "Everything" and self.dependsOn != "Everything":
raise WebIDLError("Interface member is flagged as affecting "
"everything but not depending on everything. "
"That seems rather unlikely.",
[self.location])
def _setDependsOn(self, dependsOn):
if self.dependsOn != "Everything":
raise WebIDLError("Trying to specify multiple different DependsOn, "
"Pure, or Constant extended attributes for "
"attribute", [self.location])
if dependsOn not in IDLInterfaceMember.DependsOnValues:
raise WebIDLError("Invalid [DependsOn=%s] on attribute" % dependsOn,
[self.location])
self.dependsOn = dependsOn
def _setAffects(self, affects):
if self.affects != "Everything":
raise WebIDLError("Trying to specify multiple different Affects, "
"Pure, or Constant extended attributes for "
"attribute", [self.location])
if affects not in IDLInterfaceMember.AffectsValues:
raise WebIDLError("Invalid [Affects=%s] on attribute" % dependsOn,
[self.location])
self.affects = affects
class IDLConst(IDLInterfaceMember):
def __init__(self, location, identifier, type, value):
IDLInterfaceMember.__init__(self, location, identifier,
@ -3061,6 +3197,8 @@ class IDLAttribute(IDLInterfaceMember):
self.enforceRange = False
self.clamp = False
self.slotIndex = None
self.dependsOn = "Everything"
self.affects = "Everything"
if static and identifier.name == "prototype":
raise WebIDLError("The identifier of a static attribute must not be 'prototype'",
@ -3129,11 +3267,11 @@ class IDLAttribute(IDLInterfaceMember):
if ((self.getExtendedAttribute("Cached") or
self.getExtendedAttribute("StoreInSlot")) and
not self.getExtendedAttribute("Constant") and
not self.getExtendedAttribute("Pure")):
not self.affects == "Nothing"):
raise WebIDLError("Cached attributes and attributes stored in "
"slots must be constant or pure, since the "
"getter won't always be called.",
"slots must be Constant or Pure or "
"Affects=Nothing, since the getter won't always "
"be called.",
[self.location])
if self.getExtendedAttribute("Frozen"):
if (not self.type.isSequence() and not self.type.isDictionary() and
@ -3158,8 +3296,7 @@ class IDLAttribute(IDLInterfaceMember):
(identifier == "StoreInSlot" and
(self.getExtendedAttribute("Throws") or
self.getExtendedAttribute("GetterThrows")))):
raise WebIDLError("Throwing things can't be [Pure] or [Constant] "
"or [SameObject] or [StoreInSlot]",
raise WebIDLError("Throwing things can't be [StoreInSlot]",
[attr.location])
elif identifier == "LenientThis":
if not attr.noArguments():
@ -3203,6 +3340,15 @@ class IDLAttribute(IDLInterfaceMember):
raise WebIDLError("[PutForwards] takes an identifier",
[attr.location, self.location])
elif identifier == "Replaceable":
if not attr.noArguments():
raise WebIDLError("[Replaceable] must take no arguments",
[attr.location])
if not self.readonly:
raise WebIDLError("[Replaceable] is only allowed on readonly "
"attributes", [attr.location, self.location])
if self.isStatic():
raise WebIDLError("[Replaceable] is only allowed on non-static "
"attributes", [attr.location, self.location])
if self.getExtendedAttribute("PutForwards") is not None:
raise WebIDLError("[PutForwards] and [Replaceable] can't both "
"appear on the same attribute",
@ -3250,18 +3396,43 @@ class IDLAttribute(IDLInterfaceMember):
[attr.location, self.location])
elif identifier == "Exposed":
convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
elif identifier == "Pure":
if not attr.noArguments():
raise WebIDLError("[Pure] must take no arguments",
[attr.location])
self._setDependsOn("DOMState")
self._setAffects("Nothing")
elif identifier == "Constant" or identifier == "SameObject":
if not attr.noArguments():
raise WebIDLError("[%s] must take no arguments" % identifier,
[attr.location])
self._setDependsOn("Nothing")
self._setAffects("Nothing")
elif identifier == "Affects":
if not attr.hasValue():
raise WebIDLError("[Affects] takes an identifier",
[attr.location])
self._setAffects(attr.value())
elif identifier == "DependsOn":
if not attr.hasValue():
raise WebIDLError("[DependsOn] takes an identifier",
[attr.location])
if (attr.value() != "Everything" and attr.value() != "DOMState" and
not self.readonly):
raise WebIDLError("[DependsOn=%s] only allowed on "
"readonly attributes" % attr.value(),
[attr.location, self.location])
self._setDependsOn(attr.value())
elif (identifier == "Pref" or
identifier == "SetterThrows" or
identifier == "Pure" or
identifier == "Throws" or
identifier == "GetterThrows" or
identifier == "ChromeOnly" or
identifier == "SameObject" or
identifier == "Constant" or
identifier == "Func" or
identifier == "Frozen" or
identifier == "AvailableIn" or
identifier == "NewObject" or
identifier == "UnsafeInPrerendering" or
identifier == "CheckPermissions" or
identifier == "BinaryName"):
# Known attributes that we don't need to do anything with here
@ -3306,6 +3477,7 @@ class IDLArgument(IDLObjectWithIdentifier):
self._allowTreatNonCallableAsNull = False
assert not variadic or optional
assert not variadic or not defaultValue
def addExtendedAttributes(self, attrs):
attrs = self.checkForStringHandlingExtendedAttributes(
@ -3354,9 +3526,9 @@ class IDLArgument(IDLObjectWithIdentifier):
if ((self.type.isDictionary() or
self.type.isUnion() and self.type.unroll().hasDictionaryType) and
self.optional and not self.defaultValue):
# Default optional dictionaries to null, for simplicity,
# so the codegen doesn't have to special-case this.
self.optional and not self.defaultValue and not self.variadic):
# Default optional non-variadic dictionaries to null,
# for simplicity, so the codegen doesn't have to special-case this.
self.defaultValue = IDLNullValue(self.location)
elif self.type.isAny():
assert (self.defaultValue is None or
@ -3383,6 +3555,9 @@ class IDLArgument(IDLObjectWithIdentifier):
deps.add(self.defaultValue)
return deps
def canHaveMissingValue(self):
return self.optional and not self.defaultValue
class IDLCallbackType(IDLType, IDLObjectWithScope):
def __init__(self, location, parentScope, identifier, returnType, arguments):
assert isinstance(returnType, IDLType)
@ -3442,7 +3617,8 @@ class IDLCallbackType(IDLType, IDLObjectWithScope):
# Just forward to the union; it'll deal
return other.isDistinguishableFrom(self)
return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isNonCallbackInterface() or other.isDate())
other.isNonCallbackInterface() or other.isDate() or
other.isSequence())
def addExtendedAttributes(self, attrs):
unhandledAttrs = []
@ -3538,6 +3714,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
self._jsonifier = jsonifier
self._specialType = specialType
self._unforgeable = False
self.dependsOn = "Everything"
self.affects = "Everything"
if static and identifier.name == "prototype":
raise WebIDLError("The identifier of a static operation must not be 'prototype'",
@ -3618,7 +3796,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
return self._hasOverloads
def isIdentifierLess(self):
return self.identifier.name[:2] == "__" and self.identifier.name != "__noSuchMethod__"
return self.identifier.name[:2] == "__"
def resolve(self, parentScope):
assert isinstance(parentScope, IDLScope)
@ -3849,16 +4027,32 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
[attr.location, self.location])
elif identifier == "Exposed":
convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
elif (identifier == "Pure" or
identifier == "CrossOriginCallable" or
elif (identifier == "CrossOriginCallable" or
identifier == "WebGLHandlesContextLoss"):
# Known no-argument attributes.
if not attr.noArguments():
raise WebIDLError("[%s] must take no arguments" % identifier,
[attr.location])
elif identifier == "Pure":
if not attr.noArguments():
raise WebIDLError("[Pure] must take no arguments",
[attr.location])
self._setDependsOn("DOMState")
self._setAffects("Nothing")
elif identifier == "Affects":
if not attr.hasValue():
raise WebIDLError("[Affects] takes an identifier",
[attr.location])
self._setAffects(attr.value())
elif identifier == "DependsOn":
if not attr.hasValue():
raise WebIDLError("[DependsOn] takes an identifier",
[attr.location])
self._setDependsOn(attr.value())
elif (identifier == "Throws" or
identifier == "NewObject" or
identifier == "ChromeOnly" or
identifier == "UnsafeInPrerendering" or
identifier == "Pref" or
identifier == "Func" or
identifier == "AvailableIn" or
@ -3880,7 +4074,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def _getDependentObjects(self):
deps = set()
for overload in self._overloads:
deps.union(overload._getDependentObjects())
deps.update(overload._getDependentObjects())
return deps
class IDLImplementsStatement(IDLObject):
@ -3888,8 +4082,11 @@ class IDLImplementsStatement(IDLObject):
IDLObject.__init__(self, location)
self.implementor = implementor;
self.implementee = implementee
self._finished = False
def finish(self, scope):
if self._finished:
return
assert(isinstance(self.implementor, IDLIdentifierPlaceholder))
assert(isinstance(self.implementee, IDLIdentifierPlaceholder))
implementor = self.implementor.finish(scope)
@ -3914,6 +4111,8 @@ class IDLImplementsStatement(IDLObject):
"interface",
[self.implementee.location])
implementor.addImplementedInterface(implementee)
self.implementor = implementor
self.implementee = implementee
def validate(self):
pass
@ -4044,7 +4243,7 @@ class Tokenizer(object):
"Date": "DATE",
"DOMString": "DOMSTRING",
"ByteString": "BYTESTRING",
"ScalarValueString": "SCALARVALUESTRING",
"USVString": "USVSTRING",
"any": "ANY",
"boolean": "BOOLEAN",
"byte": "BYTE",
@ -4053,8 +4252,8 @@ class Tokenizer(object):
"long": "LONG",
"object": "OBJECT",
"octet": "OCTET",
"optional": "OPTIONAL",
"Promise": "PROMISE",
"required": "REQUIRED",
"sequence": "SEQUENCE",
"MozMap": "MOZMAP",
"short": "SHORT",
@ -4339,15 +4538,21 @@ class Parser(Tokenizer):
def p_DictionaryMember(self, p):
"""
DictionaryMember : Type IDENTIFIER Default SEMICOLON
DictionaryMember : Required Type IDENTIFIER Default SEMICOLON
"""
# These quack a lot like optional arguments, so just treat them that way.
t = p[1]
t = p[2]
assert isinstance(t, IDLType)
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
defaultValue = p[3]
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
defaultValue = p[4]
optional = not p[1]
p[0] = IDLArgument(self.getLocation(p, 2), identifier, t, optional=True,
if not optional and defaultValue:
raise WebIDLError("Required dictionary members can't have a default value.",
[self.getLocation(p, 4)])
p[0] = IDLArgument(self.getLocation(p, 3), identifier, t,
optional=optional,
defaultValue=defaultValue, variadic=False,
dictionaryMember=True)
@ -4545,7 +4750,7 @@ class Parser(Tokenizer):
def p_AttributeRest(self, p):
"""
AttributeRest : ReadOnly ATTRIBUTE Type IDENTIFIER SEMICOLON
AttributeRest : ReadOnly ATTRIBUTE Type AttributeName SEMICOLON
"""
location = self.getLocation(p, 2)
readonly = p[1]
@ -4874,6 +5079,7 @@ class Parser(Tokenizer):
| INTERFACE
| LEGACYCALLER
| PARTIAL
| REQUIRED
| SERIALIZER
| SETTER
| STATIC
@ -4884,6 +5090,13 @@ class Parser(Tokenizer):
"""
p[0] = p[1]
def p_AttributeName(self, p):
"""
AttributeName : IDENTIFIER
| REQUIRED
"""
p[0] = p[1]
def p_Optional(self, p):
"""
Optional : OPTIONAL
@ -4896,6 +5109,18 @@ class Parser(Tokenizer):
"""
p[0] = False
def p_Required(self, p):
"""
Required : REQUIRED
"""
p[0] = True
def p_RequiredEmpty(self, p):
"""
Required :
"""
p[0] = False
def p_Ellipsis(self, p):
"""
Ellipsis : ELLIPSIS
@ -4982,7 +5207,7 @@ class Parser(Tokenizer):
| DATE
| DOMSTRING
| BYTESTRING
| SCALARVALUESTRING
| USVSTRING
| ANY
| ATTRIBUTE
| BOOLEAN
@ -5255,11 +5480,11 @@ class Parser(Tokenizer):
"""
p[0] = IDLBuiltinType.Types.bytestring
def p_PrimitiveOrStringTypeScalarValueString(self, p):
def p_PrimitiveOrStringTypeUSVString(self, p):
"""
PrimitiveOrStringType : SCALARVALUESTRING
PrimitiveOrStringType : USVSTRING
"""
p[0] = IDLBuiltinType.Types.scalarvaluestring
p[0] = IDLBuiltinType.Types.usvstring
def p_UnsignedIntegerTypeUnsigned(self, p):
"""
@ -5473,6 +5698,10 @@ class Parser(Tokenizer):
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._installBuiltins(self._globalScope)
self._productions = []
@ -5548,6 +5777,7 @@ class Parser(Tokenizer):
# Builtin IDL defined by WebIDL
_builtins = """
typedef unsigned long long DOMTimeStamp;
typedef (ArrayBufferView or ArrayBuffer) BufferSource;
"""
def main():