aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMs2ger <ms2ger@gmail.com>2014-06-07 17:21:52 +0200
committerMs2ger <ms2ger@gmail.com>2014-06-07 17:21:52 +0200
commit278f47ad46b2f02539f39cf86c21d68ad4a8c901 (patch)
tree757eca942fc367e0f3a611e562f671ef3db07b76
parent1184b500e50123fdcfb6824bed1df6e770b0d0f8 (diff)
downloadservo-278f47ad46b2f02539f39cf86c21d68ad4a8c901.tar.gz
servo-278f47ad46b2f02539f39cf86c21d68ad4a8c901.zip
Import the WebIDL parser from Gecko.
-rw-r--r--src/components/script/dom/bindings/codegen/parser/WebIDL.py1514
1 files changed, 1247 insertions, 267 deletions
diff --git a/src/components/script/dom/bindings/codegen/parser/WebIDL.py b/src/components/script/dom/bindings/codegen/parser/WebIDL.py
index 5740a21ca52..1903fc2ba4f 100644
--- a/src/components/script/dom/bindings/codegen/parser/WebIDL.py
+++ b/src/components/script/dom/bindings/codegen/parser/WebIDL.py
@@ -8,6 +8,7 @@ from ply import lex, yacc
import re
import os
import traceback
+import math
# Machinery
@@ -36,18 +37,25 @@ def parseInt(literal):
return value * sign
# Magic for creating enums
-def M_add_class_attribs(attribs):
+def M_add_class_attribs(attribs, start):
def foo(name, bases, dict_):
for v, k in enumerate(attribs):
- dict_[k] = v
+ dict_[k] = start + v
assert 'length' not in dict_
- dict_['length'] = len(attribs)
+ dict_['length'] = start + len(attribs)
return type(name, bases, dict_)
return foo
-def enum(*names):
- class Foo(object):
- __metaclass__ = M_add_class_attribs(names)
+def enum(*names, **kw):
+ if len(kw) == 1:
+ base = kw['base'].__class__
+ start = base.length
+ else:
+ assert len(kw) == 0
+ base = object
+ start = 0
+ class Foo(base):
+ __metaclass__ = M_add_class_attribs(names, start)
def __setattr__(self, name, value): # this makes it read-only
raise NotImplementedError
return Foo()
@@ -146,23 +154,6 @@ class IDLObject(object):
def isCallback(self):
return False
- def isSingleOperationInterface(self):
- assert self.isCallback() or self.isJSImplemented()
- return (
- # JS-implemented things should never need the
- # this-handling weirdness of single-operation interfaces.
- not self.isJSImplemented() and
- # Not inheriting from another interface
- not self.parent and
- # No consequential interfaces
- len(self.getConsequentialInterfaces()) == 0 and
- # No attributes of any kinds
- not any(m.isAttr() for m in self.members) and
- # There is at least one regular operation, and all regular
- # operations have the same identifier
- len(set(m.identifier.name for m in self.members if
- m.isMethod() and not m.isStatic())) == 1)
-
def isType(self):
return False
@@ -252,7 +243,7 @@ class IDLScope(IDLObject):
return
# ensureUnique twice with the same object is not allowed
- assert object != self._dict[identifier.name]
+ assert id(object) != id(self._dict[identifier.name])
replacement = self.resolveIdentifierConflict(self, identifier,
self._dict[identifier.name],
@@ -269,7 +260,31 @@ class IDLScope(IDLObject):
isinstance(newObject, IDLExternalInterface) and \
originalObject.identifier.name == newObject.identifier.name:
return originalObject
-
+
+ if (isinstance(originalObject, IDLExternalInterface) or
+ isinstance(newObject, IDLExternalInterface)):
+ raise WebIDLError(
+ "Name collision between "
+ "interface declarations for identifier '%s' at '%s' and '%s'"
+ % (identifier.name,
+ originalObject.location, newObject.location), [])
+
+ if (isinstance(originalObject, IDLDictionary) or
+ isinstance(newObject, IDLDictionary)):
+ raise WebIDLError(
+ "Name collision between dictionary declarations for "
+ "identifier '%s'.\n%s\n%s"
+ % (identifier.name,
+ originalObject.location, newObject.location), [])
+
+ # We do the merging of overloads here as opposed to in IDLInterface
+ # because we need to merge overloads of NamedConstructors and we need to
+ # detect conflicts in those across interfaces. See also the comment in
+ # IDLInterface.addExtendedAttributes for "NamedConstructor".
+ if originalObject.tag == IDLInterfaceMember.Tags.Method and \
+ newObject.tag == IDLInterfaceMember.Tags.Method:
+ return originalObject.addOverload(newObject)
+
# Default to throwing, derived classes can override.
conflictdesc = "\n\t%s at %s\n\t%s at %s" % \
(originalObject, originalObject.location, newObject, newObject.location)
@@ -316,12 +331,15 @@ class IDLUnresolvedIdentifier(IDLObject):
assert len(name) > 0
- if name[:2] == "__" and not allowDoubleUnderscore:
+ if name[:2] == "__" and name != "__content" and name != "___noSuchMethod__" and not allowDoubleUnderscore:
raise WebIDLError("Identifiers beginning with __ are reserved",
[location])
if name[0] == '_' and not allowDoubleUnderscore:
name = name[1:]
- if name in ["prototype", "constructor", "toString"] and not allowForbidden:
+ # TODO: Bug 872377, Restore "toJSON" to below list.
+ # We sometimes need custom serialization, so allow toJSON for now.
+ if (name in ["constructor", "toString"] and
+ not allowForbidden):
raise WebIDLError("Cannot use reserved identifier '%s'" % (name),
[location])
@@ -360,7 +378,6 @@ class IDLObjectWithIdentifier(IDLObject):
self.resolve(parentScope)
self.treatNullAs = "Default"
- self.treatUndefinedAs = "Default"
def resolve(self, parentScope):
assert isinstance(parentScope, IDLScope)
@@ -371,7 +388,7 @@ class IDLObjectWithIdentifier(IDLObject):
isDictionaryMember=False,
isOptional=False):
"""
- A helper function to deal with TreatNullAs and TreatUndefinedAs. Returns the list
+ A helper function to deal with TreatNullAs. Returns the list
of attrs it didn't handle itself.
"""
assert isinstance(self, IDLArgument) or isinstance(self, IDLAttribute)
@@ -397,30 +414,6 @@ class IDLObjectWithIdentifier(IDLObject):
"'EmptyString', not '%s'" % value,
[self.location])
self.treatNullAs = value
- elif identifier == "TreatUndefinedAs":
- if not self.type.isDOMString():
- raise WebIDLError("[TreatUndefinedAs] is only allowed on "
- "arguments or attributes whose type is "
- "DOMString or DOMString?",
- [self.location])
- if isDictionaryMember:
- raise WebIDLError("[TreatUndefinedAs] is not allowed for "
- "dictionary members", [self.location])
- if value == 'Null':
- if not self.type.nullable():
- raise WebIDLError("[TreatUndefinedAs=Null] is only "
- "allowed on arguments whose type is "
- "DOMString?", [self.location])
- elif value == 'Missing':
- if not isOptional:
- raise WebIDLError("[TreatUndefinedAs=Missing] is only "
- "allowed on optional arguments",
- [self.location])
- elif value != 'EmptyString':
- raise WebIDLError("[TreatUndefinedAs] must take the "
- "identifiers EmptyString or Null or "
- "Missing", [self.location])
- self.treatUndefinedAs = value
else:
unhandledAttrs.append(attr)
@@ -450,8 +443,44 @@ class IDLIdentifierPlaceholder(IDLObjectWithIdentifier):
class IDLExternalInterface(IDLObjectWithIdentifier):
def __init__(self, location, parentScope, identifier):
- raise WebIDLError("Servo does not support external interfaces.",
- [self.location])
+ assert isinstance(identifier, IDLUnresolvedIdentifier)
+ assert isinstance(parentScope, IDLScope)
+ self.parent = None
+ IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
+ IDLObjectWithIdentifier.resolve(self, parentScope)
+
+ def finish(self, scope):
+ pass
+
+ def validate(self):
+ pass
+
+ def isExternal(self):
+ return True
+
+ def isInterface(self):
+ return True
+
+ def isConsequential(self):
+ return False
+
+ def addExtendedAttributes(self, attrs):
+ assert len(attrs) == 0
+
+ def resolve(self, parentScope):
+ pass
+
+ def getJSImplementation(self):
+ return None
+
+ def isJSImplemented(self):
+ return False
+
+ def getNavigatorProperty(self):
+ return None
+
+ def _getDependentObjects(self):
+ return set()
class IDLInterface(IDLObjectWithScope):
def __init__(self, location, parentScope, name, parent, members,
@@ -475,6 +504,16 @@ class IDLInterface(IDLObjectWithScope):
# self or have self as a consequential interface, including self itself.
# Used for distinguishability checking.
self.interfacesBasedOnSelf = set([self])
+ # self.interfacesImplementingSelf is the set of interfaces that directly
+ # have self as a consequential interface
+ self.interfacesImplementingSelf = set()
+ self._hasChildInterfaces = False
+ self._isOnGlobalProtoChain = False
+ # Tracking of the number of reserved slots we need for our
+ # members and those of ancestor interfaces.
+ self.totalMembersInSlots = 0
+ # Tracking of the number of own own members we have in slots
+ self._ownMembersInSlots = 0
IDLObjectWithScope.__init__(self, location, parentScope, name)
@@ -482,7 +521,7 @@ class IDLInterface(IDLObjectWithScope):
self.setNonPartial(location, parent, members)
else:
# Just remember our members for now
- self.members = list(members) # clone the list
+ self.members = members
def __str__(self):
return "Interface '%s'" % self.identifier.name
@@ -500,14 +539,9 @@ class IDLInterface(IDLObjectWithScope):
assert isinstance(originalObject, IDLInterfaceMember)
assert isinstance(newObject, IDLInterfaceMember)
- if originalObject.tag != IDLInterfaceMember.Tags.Method or \
- newObject.tag != IDLInterfaceMember.Tags.Method:
- # Call the base class method, which will throw
- IDLScope.resolveIdentifierConflict(self, identifier, originalObject,
- newObject)
- assert False # Not reached
+ retval = IDLScope.resolveIdentifierConflict(self, scope, identifier,
+ originalObject, newObject)
- retval = originalObject.addOverload(newObject)
# Might be a ctor, which isn't in self.members
if newObject in self.members:
self.members.remove(newObject)
@@ -541,6 +575,19 @@ class IDLInterface(IDLObjectWithScope):
if self.parent:
self.parent.finish(scope)
+ self.parent._hasChildInterfaces = True
+
+ self.totalMembersInSlots = self.parent.totalMembersInSlots
+
+ # Interfaces with [Global] must not have anything inherit from them
+ if self.parent.getExtendedAttribute("Global"):
+ # Note: This is not a self.parent.isOnGlobalProtoChain() check
+ # because ancestors of a [Global] interface can have other
+ # descendants.
+ raise WebIDLError("[Global] interface has another interface "
+ "inheriting from it",
+ [self.location, self.parent.location])
+
# Callbacks must not inherit from non-callbacks or inherit from
# anything that has consequential interfaces.
# XXXbz Can non-callbacks inherit from callbacks? Spec issue pending.
@@ -590,7 +637,10 @@ class IDLInterface(IDLObjectWithScope):
if ctor is not None:
ctor.finish(scope)
- # Make a copy of our member list, so things tht implement us
+ for ctor in self.namedConstructors:
+ ctor.finish(scope)
+
+ # Make a copy of our member list, so things that implement us
# can get those without all the stuff we implement ourselves
# admixed.
self.originalMembers = list(self.members)
@@ -612,17 +662,68 @@ class IDLInterface(IDLObjectWithScope):
(member.identifier.name, self),
[additionalMember.location, member.location])
self.members.extend(additionalMembers)
+ iface.interfacesImplementingSelf.add(self)
for ancestor in self.getInheritedInterfaces():
ancestor.interfacesBasedOnSelf.add(self)
for ancestorConsequential in ancestor.getConsequentialInterfaces():
ancestorConsequential.interfacesBasedOnSelf.add(self)
+ for member in self.members:
+ if (member.isAttr() and member.isUnforgeable() and
+ not hasattr(member, "originatingInterface")):
+ member.originatingInterface = self
+
+ # Compute slot indices for our members before we pull in
+ # unforgeable members from our parent.
+ for member in self.members:
+ if (member.isAttr() and
+ (member.getExtendedAttribute("StoreInSlot") or
+ member.getExtendedAttribute("Cached"))):
+ member.slotIndex = self.totalMembersInSlots
+ self.totalMembersInSlots += 1
+ if member.getExtendedAttribute("StoreInSlot"):
+ self._ownMembersInSlots += 1
+
+ if self.parent:
+ # Make sure we don't shadow any of the [Unforgeable] attributes on
+ # our ancestor interfaces. We don't have to worry about
+ # consequential interfaces here, because those have already been
+ # imported into the relevant .members lists. And we don't have to
+ # worry about anything other than our parent, because it has already
+ # imported its ancestors unforgeable attributes into its member
+ # list.
+ for unforgeableAttr in (attr for attr in self.parent.members if
+ attr.isAttr() and not attr.isStatic() and
+ attr.isUnforgeable()):
+ shadows = [ m for m in self.members if
+ (m.isAttr() or m.isMethod()) and
+ not m.isStatic() and
+ m.identifier.name == unforgeableAttr.identifier.name ]
+ if len(shadows) != 0:
+ locs = [unforgeableAttr.location] + [ s.location for s
+ in shadows ]
+ raise WebIDLError("Interface %s shadows [Unforgeable] "
+ "members of %s" %
+ (self.identifier.name,
+ ancestor.identifier.name),
+ locs)
+ # And now just stick it in our members, since we won't be
+ # inheriting this down the proto chain. If we really cared we
+ # could try to do something where we set up the unforgeable
+ # attributes of ancestor interfaces, with their corresponding
+ # getters, on our interface, but that gets pretty complicated
+ # and seems unnecessary.
+ self.members.append(unforgeableAttr)
+
# Ensure that there's at most one of each {named,indexed}
- # {getter,setter,creator,deleter}.
- specialMembersSeen = set()
+ # {getter,setter,creator,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.
+ specialMembersSeen = {}
for member in self.members:
- if member.tag != IDLInterfaceMember.Tags.Method:
+ if not member.isMethod():
continue
if member.isGetter():
@@ -633,26 +734,100 @@ class IDLInterface(IDLObjectWithScope):
memberType = "creators"
elif member.isDeleter():
memberType = "deleters"
+ elif member.isStringifier():
+ memberType = "stringifiers"
+ elif member.isJsonifier():
+ memberType = "jsonifiers"
+ elif member.isLegacycaller():
+ memberType = "legacycallers"
else:
continue
- if member.isNamed():
- memberType = "named " + memberType
- elif member.isIndexed():
- memberType = "indexed " + memberType
- else:
- continue
+ if (memberType != "stringifiers" and memberType != "legacycallers" and
+ memberType != "jsonifiers"):
+ if member.isNamed():
+ memberType = "named " + memberType
+ else:
+ assert member.isIndexed()
+ memberType = "indexed " + memberType
if memberType in specialMembersSeen:
raise WebIDLError("Multiple " + memberType + " on %s" % (self),
- [self.location])
-
- specialMembersSeen.add(memberType)
+ [self.location,
+ specialMembersSeen[memberType].location,
+ member.location])
+
+ specialMembersSeen[memberType] = member
+
+ if self._isOnGlobalProtoChain:
+ # Make sure we have no named setters, creators, or deleters
+ for memberType in ["setter", "creator", "deleter"]:
+ memberId = "named " + memberType + "s"
+ if memberId in specialMembersSeen:
+ raise WebIDLError("Interface with [Global] has a named %s" %
+ memberType,
+ [self.location,
+ specialMembersSeen[memberId].location])
+ # Make sure we're not [OverrideBuiltins]
+ if self.getExtendedAttribute("OverrideBuiltins"):
+ raise WebIDLError("Interface with [Global] also has "
+ "[OverrideBuiltins]",
+ [self.location])
+ # Mark all of our ancestors as being on the global's proto chain too
+ parent = self.parent
+ while parent:
+ # Must not inherit from an interface with [OverrideBuiltins]
+ if parent.getExtendedAttribute("OverrideBuiltins"):
+ raise WebIDLError("Interface with [Global] inherits from "
+ "interface with [OverrideBuiltins]",
+ [self.location, parent.location])
+ parent._isOnGlobalProtoChain = True
+ parent = parent.parent
def validate(self):
for member in self.members:
member.validate()
+ # Check that PutForwards refers to another attribute and that no
+ # cycles exist in forwarded assignments.
+ if member.isAttr():
+ iface = self
+ attr = member
+ putForwards = attr.getExtendedAttribute("PutForwards")
+ if putForwards and self.isCallback():
+ raise WebIDLError("[PutForwards] used on an attribute "
+ "on interface %s which is a callback "
+ "interface" % self.identifier.name,
+ [self.location, member.location])
+
+ while putForwards is not None:
+ forwardIface = attr.type.unroll().inner
+ fowardAttr = None
+
+ for forwardedMember in forwardIface.members:
+ if (not forwardedMember.isAttr() or
+ forwardedMember.identifier.name != putForwards[0]):
+ continue
+ if forwardedMember == member:
+ raise WebIDLError("Cycle detected in forwarded "
+ "assignments for attribute %s on "
+ "%s" %
+ (member.identifier.name, self),
+ [member.location])
+ fowardAttr = forwardedMember
+ break
+
+ if fowardAttr is None:
+ raise WebIDLError("Attribute %s on %s forwards to "
+ "missing attribute %s" %
+ (attr.identifier.name, iface, putForwards),
+ [attr.location])
+
+ iface = forwardIface
+ attr = fowardAttr
+ putForwards = attr.getExtendedAttribute("PutForwards")
+
+
def isInterface(self):
return True
@@ -672,6 +847,23 @@ class IDLInterface(IDLObjectWithScope):
def isCallback(self):
return self._callback
+ def isSingleOperationInterface(self):
+ assert self.isCallback() or self.isJSImplemented()
+ return (
+ # JS-implemented things should never need the
+ # this-handling weirdness of single-operation interfaces.
+ not self.isJSImplemented() and
+ # Not inheriting from another interface
+ not self.parent and
+ # No consequential interfaces
+ len(self.getConsequentialInterfaces()) == 0 and
+ # No attributes of any kinds
+ not any(m.isAttr() for m in self.members) and
+ # There is at least one regular operation, and all regular
+ # operations have the same identifier
+ len(set(m.identifier.name for m in self.members if
+ m.isMethod() and not m.isStatic())) == 1)
+
def inheritanceDepth(self):
depth = 0
parent = self.parent
@@ -713,27 +905,111 @@ class IDLInterface(IDLObjectWithScope):
[self.location])
self._noInterfaceObject = True
- elif identifier == "Constructor":
- if not self.hasInterfaceObject():
- raise WebIDLError("Constructor and NoInterfaceObject are incompatible",
+ elif identifier == "Constructor" or identifier == "NamedConstructor" or identifier == "ChromeConstructor":
+ if identifier == "Constructor" and not self.hasInterfaceObject():
+ raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
+ [self.location])
+
+ if identifier == "NamedConstructor" and not attr.hasValue():
+ raise WebIDLError("NamedConstructor must either take an identifier or take a named argument list",
+ [attr.location])
+
+ if identifier == "ChromeConstructor" and not self.hasInterfaceObject():
+ raise WebIDLError(str(identifier) + " and NoInterfaceObject are incompatible",
[self.location])
args = attr.args() if attr.hasArgs() else []
retType = IDLWrapperType(self.location, self)
-
- identifier = IDLUnresolvedIdentifier(self.location, "constructor",
- allowForbidden=True)
- method = IDLMethod(self.location, identifier, retType, args)
- # Constructors are always Creators and are always
+ if identifier == "Constructor" or identifier == "ChromeConstructor":
+ name = "constructor"
+ allowForbidden = True
+ else:
+ name = attr.value()
+ allowForbidden = False
+
+ methodIdentifier = IDLUnresolvedIdentifier(self.location, name,
+ allowForbidden=allowForbidden)
+
+ method = IDLMethod(self.location, methodIdentifier, retType,
+ args, static=True)
+ # 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.
method.addExtendedAttributes(
- [IDLExtendedAttribute(self.location, ("Creator",)),
+ [IDLExtendedAttribute(self.location, ("NewObject",)),
IDLExtendedAttribute(self.location, ("Throws",))])
- method.resolve(self)
+ if identifier == "ChromeConstructor":
+ method.addExtendedAttributes(
+ [IDLExtendedAttribute(self.location, ("ChromeOnly",))])
+
+ if identifier == "Constructor" or identifier == "ChromeConstructor":
+ method.resolve(self)
+ else:
+ # We need to detect conflicts for NamedConstructors across
+ # interfaces. We first call resolve on the parentScope,
+ # which will merge all NamedConstructors with the same
+ # identifier accross interfaces as overloads.
+ method.resolve(self.parentScope)
+
+ # Then we look up the identifier on the parentScope. If the
+ # result is the same as the method we're adding then it
+ # hasn't been added as an overload and it's the first time
+ # we've encountered a NamedConstructor with that identifier.
+ # If the result is not the same as the method we're adding
+ # then it has been added as an overload and we need to check
+ # whether the result is actually one of our existing
+ # NamedConstructors.
+ newMethod = self.parentScope.lookupIdentifier(method.identifier)
+ if newMethod == method:
+ self.namedConstructors.append(method)
+ elif not newMethod in self.namedConstructors:
+ raise WebIDLError("NamedConstructor conflicts with a NamedConstructor of a different interface",
+ [method.location, newMethod.location])
+ elif (identifier == "ArrayClass"):
+ if not attr.noArguments():
+ raise WebIDLError("[ArrayClass] must take no arguments",
+ [attr.location])
+ if self.parent:
+ raise WebIDLError("[ArrayClass] must not be specified on "
+ "an interface with inherited interfaces",
+ [attr.location, self.location])
+ elif (identifier == "ExceptionClass"):
+ if not attr.noArguments():
+ raise WebIDLError("[ExceptionClass] must take no arguments",
+ [attr.location])
+ if self.parent:
+ raise WebIDLError("[ExceptionClass] must not be specified on "
+ "an interface with inherited interfaces",
+ [attr.location, self.location])
+ elif identifier == "Global":
+ if not attr.noArguments():
+ raise WebIDLError("[Global] must take no arguments",
+ [attr.location])
+ self._isOnGlobalProtoChain = True
+ elif (identifier == "NeedNewResolve" or
+ identifier == "OverrideBuiltins" or
+ identifier == "ChromeOnly"):
+ # Known extended attributes that do not take values
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ elif (identifier == "Pref" or
+ identifier == "JSImplementation" or
+ identifier == "HeaderFile" or
+ identifier == "NavigatorProperty" or
+ identifier == "AvailableIn" or
+ identifier == "Func" or
+ identifier == "CheckPermissions"):
+ # 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 interface" % identifier,
+ [attr.location])
attrlist = attr.listValue()
self._extendedAttrDict[identifier] = attrlist if len(attrlist) else True
@@ -820,6 +1096,21 @@ class IDLInterface(IDLObjectWithScope):
def isJSImplemented(self):
return bool(self.getJSImplementation())
+ def getNavigatorProperty(self):
+ naviProp = self.getExtendedAttribute("NavigatorProperty")
+ if not naviProp:
+ return None
+ assert len(naviProp) == 1
+ assert isinstance(naviProp, list)
+ assert len(naviProp[0]) != 0
+ return naviProp[0]
+
+ def hasChildInterfaces(self):
+ return self._hasChildInterfaces
+
+ def isOnGlobalProtoChain(self):
+ return self._isOnGlobalProtoChain
+
def _getDependentObjects(self):
deps = set(self.members)
deps.union(self.implementedInterfaces)
@@ -827,6 +1118,9 @@ class IDLInterface(IDLObjectWithScope):
deps.add(self.parent)
return deps
+ def hasMembersInSlots(self):
+ return self._ownMembersInSlots != 0
+
class IDLDictionary(IDLObjectWithScope):
def __init__(self, location, parentScope, name, parent, members):
assert isinstance(parentScope, IDLScope)
@@ -892,7 +1186,70 @@ class IDLDictionary(IDLObjectWithScope):
[member.location, inheritedMember.location])
def validate(self):
- pass
+ def typeContainsDictionary(memberType, dictionary):
+ """
+ Returns a tuple whose:
+
+ - First element is a Boolean value indicating whether
+ memberType contains dictionary.
+
+ - Second element is:
+ A list of locations that leads from the type that was passed in
+ the memberType argument, to the dictionary being validated,
+ if the boolean value in the first element is True.
+
+ None, if the boolean value in the first element is False.
+ """
+
+ if (memberType.nullable() or
+ memberType.isArray() or
+ memberType.isSequence() or
+ memberType.isMozMap()):
+ return typeContainsDictionary(memberType.inner, dictionary)
+
+ if memberType.isDictionary():
+ if memberType.inner == dictionary:
+ return (True, [memberType.location])
+
+ (contains, locations) = dictionaryContainsDictionary(memberType.inner, \
+ dictionary)
+ if contains:
+ return (True, [memberType.location] + locations)
+
+ if memberType.isUnion():
+ for member in memberType.flatMemberTypes:
+ (contains, locations) = typeContainsDictionary(member, dictionary)
+ if contains:
+ return (True, locations)
+
+ return (False, None)
+
+ def dictionaryContainsDictionary(dictMember, dictionary):
+ for member in dictMember.members:
+ (contains, locations) = typeContainsDictionary(member.type, dictionary)
+ if contains:
+ return (True, [member.location] + locations)
+
+ if dictMember.parent:
+ if dictMember.parent == dictionary:
+ return (True, [dictMember.location])
+ else:
+ (contains, locations) = dictionaryContainsDictionary(dictMember.parent, dictionary)
+ if contains:
+ return (True, [dictMember.location] + locations)
+
+ return (False, None)
+
+ for member in self.members:
+ if member.type.isDictionary() and member.type.nullable():
+ raise WebIDLError("Dictionary %s has member with nullable "
+ "dictionary type" % self.identifier.name,
+ [member.location])
+ (contains, locations) = typeContainsDictionary(member.type, self)
+ if contains:
+ raise WebIDLError("Dictionary %s has member with itself as type." %
+ self.identifier.name,
+ [member.location] + locations)
def addExtendedAttributes(self, attrs):
assert len(attrs) == 0
@@ -946,7 +1303,10 @@ class IDLType(IDLObject):
'uint64',
# Additional primitive types
'bool',
+ 'unrestricted_float',
'float',
+ 'unrestricted_double',
+ # "double" last primitive type to match IDLBuiltinType
'double',
# Other types
'any',
@@ -960,7 +1320,10 @@ class IDLType(IDLObject):
'dictionary',
'enum',
'callback',
- 'union'
+ 'union',
+ 'sequence',
+ 'mozmap',
+ 'array'
)
def __init__(self, location, name):
@@ -986,6 +1349,12 @@ class IDLType(IDLObject):
def isPrimitive(self):
return False
+ def isBoolean(self):
+ return False
+
+ def isNumeric(self):
+ return False
+
def isString(self):
return False
@@ -1001,6 +1370,9 @@ class IDLType(IDLObject):
def isSequence(self):
return False
+ def isMozMap(self):
+ return False
+
def isArray(self):
return False
@@ -1041,7 +1413,7 @@ class IDLType(IDLObject):
return False
def isAny(self):
- return self.tag() == IDLType.Tags.any and not self.isSequence()
+ return self.tag() == IDLType.Tags.any
def isDate(self):
return self.tag() == IDLType.Tags.date
@@ -1049,9 +1421,25 @@ class IDLType(IDLObject):
def isObject(self):
return self.tag() == IDLType.Tags.object
+ def isPromise(self):
+ return False
+
def isComplete(self):
return True
+ def includesRestrictedFloat(self):
+ return False
+
+ def isFloat(self):
+ return False
+
+ def isUnrestricted(self):
+ # Should only call this on float types
+ assert self.isFloat()
+
+ def isSerializable(self):
+ return False
+
def tag(self):
assert False # Override me!
@@ -1063,10 +1451,6 @@ class IDLType(IDLObject):
assert self.tag() == IDLType.Tags.callback
return self.nullable() and self.inner._treatNonObjectAsNull
- def markTreatNonCallableAsNull(self):
- assert not self.treatNonCallableAsNull()
- self._treatNonCallableAsNull = True
-
def addExtendedAttributes(self, attrs):
assert len(attrs) == 0
@@ -1082,7 +1466,7 @@ class IDLType(IDLObject):
class IDLUnresolvedType(IDLType):
"""
- Unresolved types are interface types
+ Unresolved types are interface types
"""
def __init__(self, location, name):
@@ -1138,6 +1522,12 @@ class IDLNullableType(IDLType):
def isPrimitive(self):
return self.inner.isPrimitive()
+ def isBoolean(self):
+ return self.inner.isBoolean()
+
+ def isNumeric(self):
+ return self.inner.isNumeric()
+
def isString(self):
return self.inner.isString()
@@ -1150,6 +1540,12 @@ class IDLNullableType(IDLType):
def isFloat(self):
return self.inner.isFloat()
+ def isUnrestricted(self):
+ return self.inner.isUnrestricted()
+
+ def includesRestrictedFloat(self):
+ return self.inner.includesRestrictedFloat()
+
def isInteger(self):
return self.inner.isInteger()
@@ -1159,6 +1555,9 @@ class IDLNullableType(IDLType):
def isSequence(self):
return self.inner.isSequence()
+ def isMozMap(self):
+ return self.inner.isMozMap()
+
def isArray(self):
return self.inner.isArray()
@@ -1189,6 +1588,9 @@ class IDLNullableType(IDLType):
def isUnion(self):
return self.inner.isUnion()
+ def isSerializable(self):
+ return self.inner.isSerializable()
+
def tag(self):
return self.inner.tag()
@@ -1210,17 +1612,6 @@ class IDLNullableType(IDLType):
raise WebIDLError("The inner type of a nullable type must not "
"be a union type that itself has a nullable "
"type as a member type", [self.location])
- # Check for dictionaries in the union
- for memberType in self.inner.flatMemberTypes:
- if memberType.isDictionary():
- raise WebIDLError("The inner type of a nullable type must "
- "not be a union type containing a "
- "dictionary type",
- [self.location, memberType.location])
-
- if self.inner.isDictionary():
- raise WebIDLError("The inner type of a nullable type must not be a "
- "dictionary type", [self.location])
self.name = self.inner.name
return self
@@ -1261,6 +1652,12 @@ class IDLSequenceType(IDLType):
def isString(self):
return False;
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
def isVoid(self):
return False
@@ -1279,9 +1676,14 @@ class IDLSequenceType(IDLType):
def isEnum(self):
return False
+ def isSerializable(self):
+ return self.inner.isSerializable()
+
+ def includesRestrictedFloat(self):
+ return self.inner.includesRestrictedFloat()
+
def tag(self):
- # XXXkhuey this is probably wrong.
- return self.inner.tag()
+ return IDLType.Tags.sequence
def resolveType(self, parentScope):
assert isinstance(parentScope, IDLScope)
@@ -1303,8 +1705,61 @@ 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.isDictionary() or other.isDate() or
- other.isNonCallbackInterface())
+ other.isDate() or other.isNonCallbackInterface())
+
+ def _getDependentObjects(self):
+ return self.inner._getDependentObjects()
+
+class IDLMozMapType(IDLType):
+ # XXXbz This is pretty similar to IDLSequenceType in various ways.
+ # And maybe to IDLNullableType. Should we have a superclass for
+ # "type containing this other type"? Bug 1015318.
+ def __init__(self, location, parameterType):
+ assert not parameterType.isVoid()
+
+ IDLType.__init__(self, location, parameterType.name)
+ self.inner = parameterType
+ self.builtin = False
+
+ def __eq__(self, other):
+ return isinstance(other, IDLMozMapType) and self.inner == other.inner
+
+ def __str__(self):
+ return self.inner.__str__() + "MozMap"
+
+ def isMozMap(self):
+ return True
+
+ def includesRestrictedFloat(self):
+ return self.inner.includesRestrictedFloat()
+
+ def tag(self):
+ return IDLType.Tags.mozmap
+
+ def resolveType(self, parentScope):
+ assert isinstance(parentScope, IDLScope)
+ self.inner.resolveType(parentScope)
+
+ def isComplete(self):
+ return self.inner.isComplete()
+
+ def complete(self, scope):
+ self.inner = self.inner.complete(scope)
+ self.name = self.inner.name
+ return self
+
+ def unroll(self):
+ # We do not unroll our inner. Just stop at ourselves. That
+ # lets us add headers for both ourselves and our inner as
+ # needed.
+ return self
+
+ def isDistinguishableFrom(self, other):
+ if other.isUnion():
+ # 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())
def _getDependentObjects(self):
return self.inner._getDependentObjects()
@@ -1314,6 +1769,7 @@ class IDLUnionType(IDLType):
IDLType.__init__(self, location, "")
self.memberTypes = memberTypes
self.hasNullableType = False
+ self.hasDictionaryType = False
self.flatMemberTypes = None
self.builtin = False
@@ -1326,6 +1782,12 @@ class IDLUnionType(IDLType):
def isUnion(self):
return True
+ def isSerializable(self):
+ return all(m.isSerializable() for m in self.memberTypes)
+
+ def includesRestrictedFloat(self):
+ return any(t.includesRestrictedFloat() for t in self.memberTypes)
+
def tag(self):
return IDLType.Tags.union
@@ -1345,7 +1807,8 @@ 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()):
+ if (isinstance(type, IDLType) and
+ (type.isArray() or type.isSequence() or type.isMozMap)):
return str(type)
return type.name
@@ -1361,11 +1824,24 @@ class IDLUnionType(IDLType):
if self.hasNullableType:
raise WebIDLError("Can't have more than one nullable types in a union",
[nullableType.location, self.flatMemberTypes[i].location])
+ if self.hasDictionaryType:
+ raise WebIDLError("Can't have a nullable type and a "
+ "dictionary type in a union",
+ [dictionaryType.location,
+ self.flatMemberTypes[i].location])
self.hasNullableType = True
nullableType = self.flatMemberTypes[i]
self.flatMemberTypes[i] = self.flatMemberTypes[i].inner
continue
- if self.flatMemberTypes[i].isUnion():
+ if self.flatMemberTypes[i].isDictionary():
+ if self.hasNullableType:
+ raise WebIDLError("Can't have a nullable type and a "
+ "dictionary type in a union",
+ [nullableType.location,
+ self.flatMemberTypes[i].location])
+ self.hasDictionaryType = True
+ dictionaryType = self.flatMemberTypes[i]
+ elif self.flatMemberTypes[i].isUnion():
self.flatMemberTypes[i:i + 1] = self.flatMemberTypes[i].memberTypes
continue
i += 1
@@ -1404,6 +1880,9 @@ class IDLArrayType(IDLType):
if parameterType.isSequence():
raise WebIDLError("Array type cannot parameterize over a sequence type",
[location])
+ if parameterType.isMozMap():
+ raise WebIDLError("Array type cannot parameterize over a MozMap type",
+ [location])
if parameterType.isDictionary():
raise WebIDLError("Array type cannot parameterize over a dictionary type",
[location])
@@ -1427,6 +1906,12 @@ class IDLArrayType(IDLType):
def isString(self):
return False
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
def isVoid(self):
return False
@@ -1448,8 +1933,7 @@ class IDLArrayType(IDLType):
return False
def tag(self):
- # XXXkhuey this is probably wrong.
- return self.inner.tag()
+ return IDLType.Tags.array
def resolveType(self, parentScope):
assert isinstance(parentScope, IDLScope)
@@ -1461,6 +1945,14 @@ class IDLArrayType(IDLType):
def complete(self, scope):
self.inner = self.inner.complete(scope)
self.name = self.inner.name
+
+ if self.inner.isDictionary():
+ raise WebIDLError("Array type must not contain "
+ "dictionary as element type.",
+ [self.inner.location])
+
+ assert not self.inner.isSequence()
+
return self
def unroll(self):
@@ -1471,8 +1963,7 @@ class IDLArrayType(IDLType):
# Just forward to the union; it'll deal
return other.isDistinguishableFrom(self)
return (other.isPrimitive() or other.isString() or other.isEnum() or
- other.isDictionary() or other.isDate() or
- other.isNonCallbackInterface())
+ other.isDate() or other.isNonCallbackInterface())
def _getDependentObjects(self):
return self.inner._getDependentObjects()
@@ -1501,15 +1992,30 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
def isPrimitive(self):
return self.inner.isPrimitive()
+ def isBoolean(self):
+ return self.inner.isBoolean()
+
+ def isNumeric(self):
+ return self.inner.isNumeric()
+
def isString(self):
return self.inner.isString()
+ def isByteString(self):
+ return self.inner.isByteString()
+
+ def isDOMString(self):
+ return self.inner.isDOMString()
+
def isVoid(self):
return self.inner.isVoid()
def isSequence(self):
return self.inner.isSequence()
+ def isMozMap(self):
+ return self.inner.isMozMap()
+
def isArray(self):
return self.inner.isArray()
@@ -1545,7 +2051,9 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
def finish(self, parentScope):
# Maybe the IDLObjectWithIdentifier for the typedef should be
- # a separate thing from the type?
+ # a separate thing from the type? If that happens, we can
+ # remove some hackery around avoiding isInterface() in
+ # Configuration.py.
self.complete(parentScope)
def validate(self):
@@ -1589,6 +2097,12 @@ class IDLWrapperType(IDLType):
def isString(self):
return False
+ def isByteString(self):
+ return False
+
+ def isDOMString(self):
+ return False
+
def isVoid(self):
return False
@@ -1614,6 +2128,23 @@ class IDLWrapperType(IDLType):
def isEnum(self):
return isinstance(self.inner, IDLEnum)
+ def isPromise(self):
+ return isinstance(self.inner, IDLInterface) and \
+ self.inner.identifier.name == "Promise"
+
+ def isSerializable(self):
+ if self.isInterface():
+ if self.inner.isExternal():
+ return False
+ return any(m.isMethod() and m.isJsonifier() for m in self.inner.members)
+ elif self.isEnum():
+ return True
+ elif self.isDictionary():
+ return all(m.type.isSerializable() for m in self.inner.members)
+ else:
+ raise WebIDLError("IDLWrapperType wraps type %s that we don't know if "
+ "is serializable" % type(self.inner), [self.location])
+
def resolveType(self, parentScope):
assert isinstance(parentScope, IDLScope)
self.inner.resolve(parentScope)
@@ -1637,20 +2168,18 @@ class IDLWrapperType(IDLType):
return other.isDistinguishableFrom(self)
assert self.isInterface() or self.isEnum() or self.isDictionary()
if self.isEnum():
- return (other.isInterface() or other.isObject() or
+ return (other.isPrimitive() or other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isArray() or
+ other.isSequence() or other.isMozMap() or other.isArray() or
other.isDate())
+ if self.isDictionary() and other.nullable():
+ return False
if other.isPrimitive() or other.isString() or other.isEnum() or other.isDate():
return True
if self.isDictionary():
- return (not other.nullable() and
- (other.isNonCallbackInterface() or other.isSequence() or
- other.isArray()))
+ return other.isNonCallbackInterface()
assert self.isInterface()
- # XXXbz need to check that the interfaces can't be implemented
- # by the same object
if other.isInterface():
if other.isSpiderMonkeyInterface():
# Just let |other| handle things
@@ -1663,7 +2192,7 @@ class IDLWrapperType(IDLType):
(self.isNonCallbackInterface() or
other.isNonCallbackInterface()))
if (other.isDictionary() or other.isCallback() or
- other.isSequence() or other.isArray()):
+ other.isSequence() or other.isMozMap() or other.isArray()):
return self.isNonCallbackInterface()
# Not much else |other| can be
@@ -1701,7 +2230,10 @@ class IDLBuiltinType(IDLType):
'unsigned_long_long',
# Additional primitive types
'boolean',
+ 'unrestricted_float',
'float',
+ 'unrestricted_double',
+ # IMPORTANT: "double" must be the last primitive type listed
'double',
# Other types
'any',
@@ -1734,7 +2266,9 @@ class IDLBuiltinType(IDLType):
Types.long_long: IDLType.Tags.int64,
Types.unsigned_long_long: IDLType.Tags.uint64,
Types.boolean: IDLType.Tags.bool,
+ Types.unrestricted_float: IDLType.Tags.unrestricted_float,
Types.float: IDLType.Tags.float,
+ Types.unrestricted_double: IDLType.Tags.unrestricted_double,
Types.double: IDLType.Tags.double,
Types.any: IDLType.Tags.any,
Types.domstring: IDLType.Tags.domstring,
@@ -1763,6 +2297,12 @@ class IDLBuiltinType(IDLType):
def isPrimitive(self):
return self._typeTag <= IDLBuiltinType.Types.double
+ def isBoolean(self):
+ return self._typeTag == IDLBuiltinType.Types.boolean
+
+ def isNumeric(self):
+ return self.isPrimitive() and not self.isBoolean()
+
def isString(self):
return self._typeTag == IDLBuiltinType.Types.domstring or \
self._typeTag == IDLBuiltinType.Types.bytestring
@@ -1800,7 +2340,20 @@ class IDLBuiltinType(IDLType):
def isFloat(self):
return self._typeTag == IDLBuiltinType.Types.float or \
- self._typeTag == IDLBuiltinType.Types.double
+ self._typeTag == IDLBuiltinType.Types.double or \
+ self._typeTag == IDLBuiltinType.Types.unrestricted_float or \
+ self._typeTag == IDLBuiltinType.Types.unrestricted_double
+
+ def isUnrestricted(self):
+ assert self.isFloat()
+ return self._typeTag == IDLBuiltinType.Types.unrestricted_float or \
+ self._typeTag == IDLBuiltinType.Types.unrestricted_double
+
+ def isSerializable(self):
+ return self.isPrimitive() or self.isDOMString() or self.isDate()
+
+ def includesRestrictedFloat(self):
+ return self.isFloat() and not self.isUnrestricted()
def tag(self):
return IDLBuiltinType.TagLookup[self._typeTag]
@@ -1809,10 +2362,23 @@ class IDLBuiltinType(IDLType):
if other.isUnion():
# Just forward to the union; it'll deal
return other.isDistinguishableFrom(self)
- if self.isPrimitive() or self.isString():
- return (other.isInterface() or other.isObject() or
+ if self.isBoolean():
+ return (other.isNumeric() or other.isString() or other.isEnum() or
+ other.isInterface() or other.isObject() or
+ other.isCallback() or other.isDictionary() or
+ other.isSequence() or other.isMozMap() or other.isArray() or
+ other.isDate())
+ if self.isNumeric():
+ return (other.isBoolean() or other.isString() or other.isEnum() or
+ other.isInterface() or other.isObject() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isArray() or
+ other.isSequence() or other.isMozMap() or other.isArray() or
+ other.isDate())
+ if self.isString():
+ return (other.isPrimitive() or other.isInterface() or
+ other.isObject() or
+ other.isCallback() or other.isDictionary() or
+ other.isSequence() or other.isMozMap() or other.isArray() or
other.isDate())
if self.isAny():
# Can't tell "any" apart from anything
@@ -1823,7 +2389,7 @@ class IDLBuiltinType(IDLType):
return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isInterface() or other.isCallback() or
other.isDictionary() or other.isSequence() or
- other.isArray())
+ other.isMozMap() or other.isArray())
if self.isVoid():
return not other.isVoid()
# Not much else we could be!
@@ -1831,7 +2397,8 @@ class IDLBuiltinType(IDLType):
# Like interfaces, but we know we're not a callback
return (other.isPrimitive() or other.isString() or other.isEnum() or
other.isCallback() or other.isDictionary() or
- other.isSequence() or other.isArray() or other.isDate() or
+ other.isSequence() or other.isMozMap() or other.isArray() or
+ other.isDate() or
(other.isInterface() and (
# ArrayBuffer is distinguishable from everything
# that's not an ArrayBuffer or a callback interface
@@ -1880,9 +2447,15 @@ BuiltinTypes = {
IDLBuiltinType.Types.float:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float",
IDLBuiltinType.Types.float),
+ IDLBuiltinType.Types.unrestricted_float:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnrestrictedFloat",
+ IDLBuiltinType.Types.unrestricted_float),
IDLBuiltinType.Types.double:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Double",
IDLBuiltinType.Types.double),
+ IDLBuiltinType.Types.unrestricted_double:
+ IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnrestrictedDouble",
+ IDLBuiltinType.Types.unrestricted_double),
IDLBuiltinType.Types.any:
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Any",
IDLBuiltinType.Types.any),
@@ -1969,13 +2542,31 @@ class IDLValue(IDLObject):
if type == self.type:
return self # Nothing to do
- # If the type allows null, rerun this matching on the inner type
- if type.nullable():
+ # We first check for unions to ensure that even if the union is nullable
+ # we end up with the right flat member type, not the union's type.
+ if type.isUnion():
+ # We use the flat member types here, because if we have a nullable
+ # member type, or a nested union, we want the type the value
+ # actually coerces to, not the nullable or nested union type.
+ for subtype in type.unroll().flatMemberTypes:
+ try:
+ coercedValue = self.coerceToType(subtype, location)
+ # Create a new IDLValue to make sure that we have the
+ # correct float/double type. This is necessary because we
+ # use the value's type when it is a default value of a
+ # union, and the union cares about the exact float type.
+ return IDLValue(self.location, subtype, coercedValue.value)
+ except:
+ pass
+ # If the type allows null, rerun this matching on the inner type, except
+ # nullable enums. We handle those specially, because we want our
+ # default string values to stay strings even when assigned to a nullable
+ # enum.
+ elif type.nullable() and not type.isEnum():
innerValue = self.coerceToType(type.inner, location)
return IDLValue(self.location, type, innerValue.value)
- # Else, see if we can coerce to 'type'.
- if self.type.isInteger() and type.isInteger():
+ elif self.type.isInteger() and type.isInteger():
# We're both integer types. See if we fit.
(min, max) = integerTypeSizes[type._typeTag]
@@ -1985,16 +2576,31 @@ class IDLValue(IDLObject):
else:
raise WebIDLError("Value %s is out of range for type %s." %
(self.value, type), [location])
+ elif self.type.isInteger() and type.isFloat():
+ # Convert an integer literal into float
+ if -2**24 <= self.value <= 2**24:
+ floatType = BuiltinTypes[IDLBuiltinType.Types.float]
+ return IDLValue(self.location, floatType, float(self.value))
+ else:
+ raise WebIDLError("Converting value %s to %s will lose precision." %
+ (self.value, type), [location])
elif self.type.isString() and type.isEnum():
# Just keep our string, but make sure it's a valid value for this enum
- if self.value not in type.inner.values():
+ enum = type.unroll().inner
+ if self.value not in enum.values():
raise WebIDLError("'%s' is not a valid default value for enum %s"
- % (self.value, type.inner.identifier.name),
- [location, type.inner.location])
+ % (self.value, enum.identifier.name),
+ [location, enum.location])
return self
- else:
- raise WebIDLError("Cannot coerce type %s to type %s." %
- (self.type, type), [location])
+ elif self.type.isFloat() and type.isFloat():
+ if (not type.isUnrestricted() and
+ (self.value == float("inf") or self.value == float("-inf") or
+ math.isnan(self.value))):
+ raise WebIDLError("Trying to convert unrestricted value %s to non-unrestricted"
+ % self.value, [location]);
+ return self
+ raise WebIDLError("Cannot coerce type %s to type %s." %
+ (self.type, type), [location])
def _getDependentObjects(self):
return set()
@@ -2008,18 +2614,43 @@ class IDLNullValue(IDLObject):
def coerceToType(self, type, location):
if (not isinstance(type, IDLNullableType) and
not (type.isUnion() and type.hasNullableType) and
+ not (type.isUnion() and type.hasDictionaryType) and
not type.isDictionary() and
not type.isAny()):
raise WebIDLError("Cannot coerce null value to type %s." % type,
[location])
nullValue = IDLNullValue(self.location)
+ if type.isUnion() and not type.nullable() and type.hasDictionaryType:
+ # We're actually a default value for the union's dictionary member.
+ # Use its type.
+ for t in type.flatMemberTypes:
+ if t.isDictionary():
+ nullValue.type = t
+ return nullValue
nullValue.type = type
return nullValue
-
+
def _getDependentObjects(self):
return set()
+class IDLUndefinedValue(IDLObject):
+ def __init__(self, location):
+ IDLObject.__init__(self, location)
+ self.type = None
+ self.value = None
+
+ def coerceToType(self, type, location):
+ if not type.isAny():
+ raise WebIDLError("Cannot coerce undefined value to type %s." % type,
+ [location])
+
+ undefinedValue = IDLUndefinedValue(self.location)
+ undefinedValue.type = type
+ return undefinedValue
+
+ def _getDependentObjects(self):
+ return set()
class IDLInterfaceMember(IDLObjectWithIdentifier):
@@ -2029,6 +2660,11 @@ class IDLInterfaceMember(IDLObjectWithIdentifier):
'Method'
)
+ Special = enum(
+ 'Static',
+ 'Stringifier'
+ )
+
def __init__(self, location, identifier, tag):
IDLObjectWithIdentifier.__init__(self, location, None, identifier)
self.tag = tag
@@ -2067,6 +2703,10 @@ class IDLConst(IDLInterfaceMember):
self.type = type
self.value = value
+ if identifier.name == "prototype":
+ raise WebIDLError("The identifier of a constant must not be 'prototype'",
+ [location])
+
def __str__(self):
return "'%s' const '%s'" % (self.type, self.identifier)
@@ -2095,8 +2735,8 @@ class IDLConst(IDLInterfaceMember):
return set([self.type, self.value])
class IDLAttribute(IDLInterfaceMember):
- def __init__(self, location, identifier, type, readonly, inherit,
- static=False):
+ def __init__(self, location, identifier, type, readonly, inherit=False,
+ static=False, stringifier=False):
IDLInterfaceMember.__init__(self, location, identifier,
IDLInterfaceMember.Tags.Attr)
@@ -2106,6 +2746,15 @@ class IDLAttribute(IDLInterfaceMember):
self.inherit = inherit
self.static = static
self.lenientThis = False
+ self._unforgeable = False
+ self.stringifier = stringifier
+ self.enforceRange = False
+ self.clamp = False
+ self.slotIndex = None
+
+ if static and identifier.name == "prototype":
+ raise WebIDLError("The identifier of a static attribute must not be 'prototype'",
+ [location])
if readonly and inherit:
raise WebIDLError("An attribute cannot be both 'readonly' and 'inherit'",
@@ -2126,14 +2775,17 @@ class IDLAttribute(IDLInterfaceMember):
assert not isinstance(t.name, IDLUnresolvedIdentifier)
self.type = t
- if self.type.isDictionary():
+ if self.type.isDictionary() and not self.getExtendedAttribute("Cached"):
raise WebIDLError("An attribute cannot be of a dictionary type",
[self.location])
- if self.type.isSequence():
- raise WebIDLError("An attribute cannot be of a sequence type",
- [self.location])
+ if self.type.isSequence() and not self.getExtendedAttribute("Cached"):
+ raise WebIDLError("A non-cached attribute cannot be of a sequence "
+ "type", [self.location])
+ if self.type.isMozMap() and not self.getExtendedAttribute("Cached"):
+ raise WebIDLError("A non-cached attribute cannot be of a MozMap "
+ "type", [self.location])
if self.type.isUnion():
- for f in self.type.flatMemberTypes:
+ for f in self.type.unroll().flatMemberTypes:
if f.isDictionary():
raise WebIDLError("An attribute cannot be of a union "
"type if one of its member types (or "
@@ -2146,18 +2798,51 @@ class IDLAttribute(IDLInterfaceMember):
"one of its member types's member "
"types, and so on) is a sequence "
"type", [self.location, f.location])
+ if f.isMozMap():
+ raise WebIDLError("An attribute cannot be of a union "
+ "type if one of its member types (or "
+ "one of its member types's member "
+ "types, and so on) is a MozMap "
+ "type", [self.location, f.location])
+ if not self.type.isInterface() and self.getExtendedAttribute("PutForwards"):
+ raise WebIDLError("An attribute with [PutForwards] must have an "
+ "interface type as its type", [self.location])
+
+ if not self.type.isInterface() and self.getExtendedAttribute("SameObject"):
+ raise WebIDLError("An attribute with [SameObject] must have an "
+ "interface type as its type", [self.location])
def validate(self):
- pass
+ if ((self.getExtendedAttribute("Cached") or
+ self.getExtendedAttribute("StoreInSlot")) and
+ not self.getExtendedAttribute("Constant") and
+ not self.getExtendedAttribute("Pure")):
+ raise WebIDLError("Cached attributes and attributes stored in "
+ "slots must be constant or pure, 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
+ not self.type.isMozMap()):
+ raise WebIDLError("[Frozen] is only allowed on "
+ "sequence-valued, dictionary-valued, and "
+ "MozMap-valued attributes",
+ [self.location])
def handleExtendedAttribute(self, attr):
identifier = attr.identifier()
- if identifier == "TreatNonCallableAsNull":
- self.type.markTreatNonCallableAsNull();
- elif identifier == "SetterInfallible" and self.readonly:
+ if identifier == "SetterThrows" and self.readonly:
raise WebIDLError("Readonly attributes must not be flagged as "
- "[SetterInfallible]",
+ "[SetterThrows]",
[self.location])
+ elif (((identifier == "Throws" or identifier == "GetterThrows") and
+ self.getExtendedAttribute("StoreInSlot")) or
+ (identifier == "StoreInSlot" and
+ (self.getExtendedAttribute("Throws") or
+ self.getExtendedAttribute("GetterThrows")))):
+ raise WebIDLError("Throwing things can't be [Pure] or [Constant] "
+ "or [SameObject] or [StoreInSlot]",
+ [attr.location])
elif identifier == "LenientThis":
if not attr.noArguments():
raise WebIDLError("[LenientThis] must take no arguments",
@@ -2165,7 +2850,107 @@ class IDLAttribute(IDLInterfaceMember):
if self.isStatic():
raise WebIDLError("[LenientThis] is only allowed on non-static "
"attributes", [attr.location, self.location])
+ if self.getExtendedAttribute("CrossOriginReadable"):
+ raise WebIDLError("[LenientThis] is not allowed in combination "
+ "with [CrossOriginReadable]",
+ [attr.location, self.location])
+ if self.getExtendedAttribute("CrossOriginWritable"):
+ raise WebIDLError("[LenientThis] is not allowed in combination "
+ "with [CrossOriginWritable]",
+ [attr.location, self.location])
self.lenientThis = True
+ elif identifier == "Unforgeable":
+ if not self.readonly:
+ raise WebIDLError("[Unforgeable] is only allowed on readonly "
+ "attributes", [attr.location, self.location])
+ if self.isStatic():
+ raise WebIDLError("[Unforgeable] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ self._unforgeable = True
+ elif identifier == "SameObject" and not self.readonly:
+ raise WebIDLError("[SameObject] only allowed on readonly attributes",
+ [attr.location, self.location])
+ elif identifier == "Constant" and not self.readonly:
+ raise WebIDLError("[Constant] only allowed on readonly attributes",
+ [attr.location, self.location])
+ elif identifier == "PutForwards":
+ if not self.readonly:
+ raise WebIDLError("[PutForwards] is only allowed on readonly "
+ "attributes", [attr.location, self.location])
+ if self.isStatic():
+ raise WebIDLError("[PutForwards] is only allowed on non-static "
+ "attributes", [attr.location, self.location])
+ if self.getExtendedAttribute("Replaceable") is not None:
+ raise WebIDLError("[PutForwards] and [Replaceable] can't both "
+ "appear on the same attribute",
+ [attr.location, self.location])
+ if not attr.hasValue():
+ raise WebIDLError("[PutForwards] takes an identifier",
+ [attr.location, self.location])
+ elif identifier == "Replaceable":
+ if self.getExtendedAttribute("PutForwards") is not None:
+ raise WebIDLError("[PutForwards] and [Replaceable] can't both "
+ "appear on the same attribute",
+ [attr.location, self.location])
+ elif identifier == "LenientFloat":
+ if self.readonly:
+ raise WebIDLError("[LenientFloat] used on a readonly attribute",
+ [attr.location, self.location])
+ if not self.type.includesRestrictedFloat():
+ raise WebIDLError("[LenientFloat] used on an attribute with a "
+ "non-restricted-float type",
+ [attr.location, self.location])
+ elif identifier == "EnforceRange":
+ if self.readonly:
+ raise WebIDLError("[EnforceRange] used on a readonly attribute",
+ [attr.location, self.location])
+ self.enforceRange = True
+ elif identifier == "Clamp":
+ if self.readonly:
+ raise WebIDLError("[Clamp] used on a readonly attribute",
+ [attr.location, self.location])
+ self.clamp = True
+ elif identifier == "StoreInSlot":
+ if self.getExtendedAttribute("Cached"):
+ raise WebIDLError("[StoreInSlot] and [Cached] must not be "
+ "specified on the same attribute",
+ [attr.location, self.location])
+ elif identifier == "Cached":
+ if self.getExtendedAttribute("StoreInSlot"):
+ raise WebIDLError("[Cached] and [StoreInSlot] must not be "
+ "specified on the same attribute",
+ [attr.location, self.location])
+ elif (identifier == "CrossOriginReadable" or
+ identifier == "CrossOriginWritable"):
+ if not attr.noArguments():
+ raise WebIDLError("[%s] must take no arguments" % identifier,
+ [attr.location])
+ if self.isStatic():
+ raise WebIDLError("[%s] is only allowed on non-static "
+ "attributes" % identifier,
+ [attr.location, self.location])
+ if self.getExtendedAttribute("LenientThis"):
+ raise WebIDLError("[LenientThis] is not allowed in combination "
+ "with [%s]" % identifier,
+ [attr.location, self.location])
+ 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 == "CheckPermissions"):
+ # Known attributes that we don't need to do anything with here
+ pass
+ else:
+ raise WebIDLError("Unknown extended attribute %s on attribute" % identifier,
+ [attr.location])
IDLInterfaceMember.handleExtendedAttribute(self, attr)
def resolve(self, parentScope):
@@ -2180,6 +2965,9 @@ class IDLAttribute(IDLInterfaceMember):
def hasLenientThis(self):
return self.lenientThis
+ def isUnforgeable(self):
+ return self._unforgeable
+
def _getDependentObjects(self):
return set([self.type])
@@ -2246,10 +3034,20 @@ class IDLArgument(IDLObjectWithIdentifier):
assert not isinstance(type.name, IDLUnresolvedIdentifier)
self.type = type
- if self.type.isDictionary() and self.optional and not self.defaultValue:
+ 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.defaultValue = IDLNullValue(self.location)
+ elif self.type.isAny():
+ assert (self.defaultValue is None or
+ isinstance(self.defaultValue, IDLNullValue))
+ # optional 'any' values always have a default value
+ if self.optional and not self.defaultValue and not self.variadic:
+ # Set the default value to undefined, for simplicity, so the
+ # codegen doesn't have to special-case this.
+ self.defaultValue = IDLUndefinedValue(self.location)
# Now do the coercing thing; this needs to happen after the
# above creation of a default value.
@@ -2286,9 +3084,6 @@ class IDLCallbackType(IDLType, IDLObjectWithScope):
self._treatNonCallableAsNull = False
self._treatNonObjectAsNull = False
- def module(self):
- return self.location.filename().split('/')[-1].split('.webidl')[0] + 'Binding'
-
def isCallback(self):
return True
@@ -2300,7 +3095,7 @@ class IDLCallbackType(IDLType, IDLObjectWithScope):
def finish(self, scope):
if not self._returnType.isComplete():
- type = returnType.complete(scope)
+ type = self._returnType.complete(scope)
assert not isinstance(type, IDLUnresolvedType)
assert not isinstance(type, IDLTypedefType)
@@ -2369,14 +3164,12 @@ class IDLMethodOverload:
class IDLMethod(IDLInterfaceMember, IDLScope):
Special = enum(
- 'None',
'Getter',
'Setter',
'Creator',
'Deleter',
'LegacyCaller',
- 'Stringifier',
- 'Static'
+ base=IDLInterfaceMember.Special
)
TypeSuffixModifier = enum(
@@ -2394,7 +3187,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def __init__(self, location, identifier, returnType, arguments,
static=False, getter=False, setter=False, creator=False,
deleter=False, specialType=NamedOrIndexed.Neither,
- legacycaller=False, stringifier=False):
+ legacycaller=False, stringifier=False, jsonifier=False):
# REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
IDLInterfaceMember.__init__(self, location, identifier,
IDLInterfaceMember.Tags.Method)
@@ -2420,8 +3213,14 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
self._legacycaller = legacycaller
assert isinstance(stringifier, bool)
self._stringifier = stringifier
+ assert isinstance(jsonifier, bool)
+ self._jsonifier = jsonifier
self._specialType = specialType
+ if static and identifier.name == "prototype":
+ raise WebIDLError("The identifier of a static operation must not be 'prototype'",
+ [location])
+
self.assertSignatureConstraints()
def __str__(self):
@@ -2453,6 +3252,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
assert len(overload.arguments) == 0
assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.domstring]
+ if self._jsonifier:
+ assert len(self._overloads) == 1
+ overload = self._overloads[0]
+ assert len(overload.arguments) == 0
+ assert overload.returnType == BuiltinTypes[IDLBuiltinType.Types.object]
+
def isStatic(self):
return self._static
@@ -2484,11 +3289,14 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def isStringifier(self):
return self._stringifier
+ def isJsonifier(self):
+ return self._jsonifier
+
def hasOverloads(self):
return self._hasOverloads
def isIdentifierLess(self):
- return self.identifier.name[:2] == "__"
+ return self.identifier.name[:2] == "__" and self.identifier.name != "__noSuchMethod__"
def resolve(self, parentScope):
assert isinstance(parentScope, IDLScope)
@@ -2529,6 +3337,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
assert not method.isDeleter()
assert not self.isStringifier()
assert not method.isStringifier()
+ assert not self.isJsonifier()
+ assert not method.isJsonifier()
return self
@@ -2537,61 +3347,69 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
self._overloads]
def finish(self, scope):
+ overloadWithPromiseReturnType = None
+ overloadWithoutPromiseReturnType = None
for overload in self._overloads:
- inOptionalArguments = False
variadicArgument = None
- sawOptionalWithNoDefault = False
arguments = overload.arguments
for (idx, argument) in enumerate(arguments):
- if argument.isComplete():
- continue
-
- argument.complete(scope)
+ if not argument.isComplete():
+ argument.complete(scope)
assert argument.type.isComplete()
- if argument.type.isDictionary():
- # Dictionaries at the end of the list or followed by
- # optional arguments must be optional.
+ if (argument.type.isDictionary() or
+ (argument.type.isUnion() and
+ argument.type.unroll().hasDictionaryType)):
+ # Dictionaries and unions containing dictionaries at the
+ # end of the list or followed by optional arguments must be
+ # optional.
if (not argument.optional and
- (idx == len(arguments) - 1 or arguments[idx+1].optional)):
- raise WebIDLError("Dictionary argument not followed by "
- "a required argument must be "
- "optional", [argument.location])
+ all(arg.optional for arg in arguments[idx+1:])):
+ raise WebIDLError("Dictionary argument or union "
+ "argument containing a dictionary "
+ "not followed by a required argument "
+ "must be optional",
+ [argument.location])
+
+ # An argument cannot be a Nullable Dictionary
+ if argument.type.nullable():
+ raise WebIDLError("An argument cannot be a nullable "
+ "dictionary or nullable union "
+ "containing a dictionary",
+ [argument.location])
# Only the last argument can be variadic
if variadicArgument:
raise WebIDLError("Variadic argument is not last argument",
[variadicArgument.location])
- # Once we see an optional argument, there can't be any non-optional
- # arguments.
- if inOptionalArguments and not argument.optional:
- raise WebIDLError("Non-optional argument after optional "
- "arguments",
- [argument.location])
- # Once we see an argument with no default value, there can
- # be no more default values.
- if sawOptionalWithNoDefault and argument.defaultValue:
- raise WebIDLError("Argument with default value after "
- "optional arguments with no default "
- "values",
- [argument.location])
- inOptionalArguments = argument.optional
if argument.variadic:
variadicArgument = argument
- sawOptionalWithNoDefault = (argument.optional and
- not argument.defaultValue)
returnType = overload.returnType
- if returnType.isComplete():
- continue
+ if not returnType.isComplete():
+ returnType = returnType.complete(scope)
+ assert not isinstance(returnType, IDLUnresolvedType)
+ assert not isinstance(returnType, IDLTypedefType)
+ assert not isinstance(returnType.name, IDLUnresolvedIdentifier)
+ overload.returnType = returnType
+
+ if returnType.isPromise():
+ overloadWithPromiseReturnType = overload
+ else:
+ overloadWithoutPromiseReturnType = overload
- type = returnType.complete(scope)
+ # Make sure either all our overloads return Promises or none do
+ if overloadWithPromiseReturnType and overloadWithoutPromiseReturnType:
+ raise WebIDLError("We have overloads with both Promise and "
+ "non-Promise return types",
+ [overloadWithPromiseReturnType.location,
+ overloadWithoutPromiseReturnType.location])
- assert not isinstance(type, IDLUnresolvedType)
- assert not isinstance(type, IDLTypedefType)
- assert not isinstance(type.name, IDLUnresolvedIdentifier)
- overload.returnType = type
+ if overloadWithPromiseReturnType and self._legacycaller:
+ raise WebIDLError("May not have a Promise return type for a "
+ "legacycaller.",
+ [overloadWithPromiseReturnType.location])
# Now compute various information that will be used by the
# WebIDL overload resolution algorithm.
@@ -2623,24 +3441,32 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
return [overload for overload in self._overloads if
len(overload.arguments) == argc or
(len(overload.arguments) > argc and
- overload.arguments[argc].optional)]
+ all(arg.optional for arg in overload.arguments[argc:])) or
+ (len(overload.arguments) < argc and
+ len(overload.arguments) > 0 and
+ overload.arguments[-1].variadic)]
def signaturesForArgCount(self, argc):
return [(overload.returnType, overload.arguments) for overload
in self.overloadsForArgCount(argc)]
def locationsForArgCount(self, argc):
- return [overload.location for overload in self._overloads if
- len(overload.arguments) == argc or
- (len(overload.arguments) > argc and
- overload.arguments[argc].optional)]
+ return [overload.location for overload in self.overloadsForArgCount(argc)]
def distinguishingIndexForArgCount(self, argc):
def isValidDistinguishingIndex(idx, signatures):
for (firstSigIndex, (firstRetval, firstArgs)) in enumerate(signatures[:-1]):
for (secondRetval, secondArgs) in signatures[firstSigIndex+1:]:
- firstType = firstArgs[idx].type
- secondType = secondArgs[idx].type
+ if idx < len(firstArgs):
+ firstType = firstArgs[idx].type
+ else:
+ assert(firstArgs[-1].variadic)
+ firstType = firstArgs[-1].type
+ if idx < len(secondArgs):
+ secondType = secondArgs[idx].type
+ else:
+ assert(secondArgs[-1].variadic)
+ secondType = secondArgs[-1].type
if not firstType.isDistinguishableFrom(secondType):
return False
return True
@@ -2656,16 +3482,58 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
def handleExtendedAttribute(self, attr):
identifier = attr.identifier()
- if identifier == "GetterInfallible":
+ if identifier == "GetterThrows":
+ raise WebIDLError("Methods must not be flagged as "
+ "[GetterThrows]",
+ [attr.location, self.location])
+ elif identifier == "SetterThrows":
raise WebIDLError("Methods must not be flagged as "
- "[GetterInfallible]",
+ "[SetterThrows]",
[attr.location, self.location])
- if identifier == "SetterInfallible":
+ elif identifier == "Unforgeable":
raise WebIDLError("Methods must not be flagged as "
- "[SetterInfallible]",
+ "[Unforgeable]",
+ [attr.location, self.location])
+ elif identifier == "SameObject":
+ raise WebIDLError("Methods must not be flagged as [SameObject]",
+ [attr.location, self.location]);
+ elif identifier == "Constant":
+ raise WebIDLError("Methods must not be flagged as [Constant]",
+ [attr.location, self.location]);
+ elif identifier == "PutForwards":
+ raise WebIDLError("Only attributes support [PutForwards]",
[attr.location, self.location])
+ elif identifier == "LenientFloat":
+ # This is called before we've done overload resolution
+ assert len(self.signatures()) == 1
+ sig = self.signatures()[0]
+ if not sig[0].isVoid():
+ raise WebIDLError("[LenientFloat] used on a non-void method",
+ [attr.location, self.location])
+ if not any(arg.type.includesRestrictedFloat() for arg in sig[1]):
+ raise WebIDLError("[LenientFloat] used on an operation with no "
+ "restricted float type arguments",
+ [attr.location, self.location])
+ elif (identifier == "Throws" or
+ identifier == "NewObject" or
+ identifier == "ChromeOnly" or
+ identifier == "Pref" or
+ identifier == "Func" or
+ identifier == "AvailableIn" or
+ identifier == "Pure" or
+ identifier == "CrossOriginCallable" or
+ identifier == "WebGLHandlesContextLoss" or
+ identifier == "CheckPermissions"):
+ # Known attributes that we don't need to do anything with here
+ pass
+ else:
+ raise WebIDLError("Unknown extended attribute %s on method" % identifier,
+ [attr.location])
IDLInterfaceMember.handleExtendedAttribute(self, attr)
+ def returnsPromise(self):
+ return self._overloads[0].returnType.isPromise()
+
def _getDependentObjects(self):
deps = set()
for overload in self._overloads:
@@ -2725,7 +3593,7 @@ class IDLExtendedAttribute(IDLObject):
return len(self._tuple) == 1
def hasValue(self):
- return len(self._tuple) == 2 and isinstance(self._tuple[1], str)
+ return len(self._tuple) >= 2 and isinstance(self._tuple[1], str)
def value(self):
assert(self.hasValue())
@@ -2758,6 +3626,11 @@ class Tokenizer(object):
"OTHER"
]
+ def t_FLOATLITERAL(self, t):
+ r'(-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+|Infinity))|NaN'
+ t.value = float(t.value)
+ return t
+
def t_INTEGER(self, t):
r'-?(0([0-7]+|[Xx][0-9A-Fa-f]+)?|[1-9][0-9]*)'
try:
@@ -2771,11 +3644,6 @@ class Tokenizer(object):
filename=self._filename)])
return t
- def t_FLOATLITERAL(self, t):
- r'-?(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][+-]?[0-9]+)?|[0-9]+[Ee][+-]?[0-9]+)'
- assert False
- return t
-
def t_IDENTIFIER(self, t):
r'[A-Z_a-z][0-9A-Z_a-z]*'
t.type = self.keywords.get(t.value, 'IDENTIFIER')
@@ -2814,7 +3682,10 @@ class Tokenizer(object):
"null": "NULL",
"true": "TRUE",
"false": "FALSE",
+ "serializer": "SERIALIZER",
"stringifier": "STRINGIFIER",
+ "jsonifier": "JSONIFIER",
+ "unrestricted": "UNRESTRICTED",
"attribute": "ATTRIBUTE",
"readonly": "READONLY",
"inherit": "INHERIT",
@@ -2840,6 +3711,7 @@ class Tokenizer(object):
"octet": "OCTET",
"optional": "OPTIONAL",
"sequence": "SEQUENCE",
+ "MozMap": "MOZMAP",
"short": "SHORT",
"unsigned": "UNSIGNED",
"void": "VOID",
@@ -2888,7 +3760,7 @@ class Parser(Tokenizer):
# The p_Foo functions here must match the WebIDL spec's grammar.
# It's acceptable to split things at '|' boundaries.
def p_Definitions(self, p):
- """
+ """
Definitions : ExtendedAttributeList Definition Definitions
"""
if p[2]:
@@ -2979,8 +3851,15 @@ class Parser(Tokenizer):
try:
if self.globalScope()._lookupIdentifier(identifier):
p[0] = self.globalScope()._lookupIdentifier(identifier)
+ if not isinstance(p[0], IDLExternalInterface):
+ raise WebIDLError("Name collision between external "
+ "interface declaration for identifier "
+ "%s and %s" % (identifier.name, p[0]),
+ [location, p[0].location])
return
- except:
+ except Exception, ex:
+ if isinstance(ex, WebIDLError):
+ raise ex
pass
p[0] = IDLExternalInterface(location, self.globalScope(), identifier)
@@ -3116,21 +3995,33 @@ class Parser(Tokenizer):
def p_EnumValueList(self, p):
"""
- EnumValueList : STRING EnumValues
+ EnumValueList : STRING EnumValueListComma
"""
p[0] = [p[1]]
p[0].extend(p[2])
- def p_EnumValues(self, p):
+ def p_EnumValueListComma(self, p):
"""
- EnumValues : COMMA STRING EnumValues
+ EnumValueListComma : COMMA EnumValueListString
"""
- p[0] = [p[2]]
- p[0].extend(p[3])
+ p[0] = p[2]
- def p_EnumValuesEmpty(self, p):
+ def p_EnumValueListCommaEmpty(self, p):
"""
- EnumValues :
+ EnumValueListComma :
+ """
+ p[0] = []
+
+ def p_EnumValueListString(self, p):
+ """
+ EnumValueListString : STRING EnumValueListComma
+ """
+ p[0] = [p[1]]
+ p[0].extend(p[2])
+
+ def p_EnumValueListStringEmpty(self, p):
+ """
+ EnumValueListString :
"""
p[0] = []
@@ -3203,8 +4094,8 @@ class Parser(Tokenizer):
"""
ConstValue : FLOATLITERAL
"""
- assert False
- pass
+ location = self.getLocation(p, 1)
+ p[0] = IDLValue(location, BuiltinTypes[IDLBuiltinType.Types.unrestricted_float], p[1])
def p_ConstValueString(self, p):
"""
@@ -3239,16 +4130,32 @@ class Parser(Tokenizer):
"""
p[0] = p[1]
+ def p_AttributeWithQualifier(self, p):
+ """
+ Attribute : Qualifier AttributeRest
+ """
+ static = IDLInterfaceMember.Special.Static in p[1]
+ stringifier = IDLInterfaceMember.Special.Stringifier in p[1]
+ (location, identifier, type, readonly) = p[2]
+ p[0] = IDLAttribute(location, identifier, type, readonly, static=static,
+ stringifier=stringifier)
+
def p_Attribute(self, p):
"""
- Attribute : Inherit ReadOnly ATTRIBUTE Type IDENTIFIER SEMICOLON
+ Attribute : Inherit AttributeRest
"""
- location = self.getLocation(p, 3)
- inherit = p[1]
- readonly = p[2]
- t = p[4]
- identifier = IDLUnresolvedIdentifier(self.getLocation(p, 5), p[5])
- p[0] = IDLAttribute(location, identifier, t, readonly, inherit)
+ (location, identifier, type, readonly) = p[2]
+ p[0] = IDLAttribute(location, identifier, type, readonly, inherit=p[1])
+
+ def p_AttributeRest(self, p):
+ """
+ AttributeRest : ReadOnly ATTRIBUTE Type IDENTIFIER SEMICOLON
+ """
+ location = self.getLocation(p, 2)
+ readonly = p[1]
+ t = p[3]
+ identifier = IDLUnresolvedIdentifier(self.getLocation(p, 4), p[4])
+ p[0] = (location, identifier, t, readonly)
def p_ReadOnly(self, p):
"""
@@ -3285,17 +4192,21 @@ class Parser(Tokenizer):
raise WebIDLError("Duplicate qualifiers are not allowed",
[self.getLocation(p, 1)])
- static = True if IDLMethod.Special.Static in p[1] else False
+ static = IDLInterfaceMember.Special.Static in p[1]
# If static is there that's all that's allowed. This is disallowed
# by the parser, so we can assert here.
assert not static or len(qualifiers) == 1
+ stringifier = IDLInterfaceMember.Special.Stringifier in p[1]
+ # If stringifier is there that's all that's allowed. This is disallowed
+ # by the parser, so we can assert here.
+ assert not stringifier or len(qualifiers) == 1
+
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
- stringifier = True if IDLMethod.Special.Stringifier in p[1] else False
if getter or deleter:
if setter or creator:
@@ -3364,21 +4275,6 @@ class Parser(Tokenizer):
raise WebIDLError("stringifier must have DOMString return type",
[self.getLocation(p, 2)])
- inOptionalArguments = False
- variadicArgument = False
- for argument in arguments:
- # Only the last argument can be variadic
- if variadicArgument:
- raise WebIDLError("Only the last argument can be variadic",
- [variadicArgument.location])
- # Once we see an optional argument, there can't be any non-optional
- # arguments.
- if inOptionalArguments and not argument.optional:
- raise WebIDLError("Cannot have a non-optional argument following an optional argument",
- [argument.location])
- inOptionalArguments = argument.optional
- variadicArgument = argument if argument.variadic else None
-
# identifier might be None. This is only permitted for special methods.
if not identifier:
if not getter and not setter and not creator and \
@@ -3403,15 +4299,49 @@ class Parser(Tokenizer):
legacycaller=legacycaller, stringifier=stringifier)
p[0] = method
- def p_QualifiersStatic(self, p):
+ def p_Stringifier(self, p):
+ """
+ Operation : STRINGIFIER SEMICOLON
+ """
+ identifier = IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+ "__stringifier",
+ allowDoubleUnderscore=True)
+ method = IDLMethod(self.getLocation(p, 1),
+ identifier,
+ returnType=BuiltinTypes[IDLBuiltinType.Types.domstring],
+ arguments=[],
+ stringifier=True)
+ p[0] = method
+
+ def p_Jsonifier(self, p):
+ """
+ Operation : JSONIFIER SEMICOLON
+ """
+ identifier = IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
+ "__jsonifier", allowDoubleUnderscore=True)
+ method = IDLMethod(self.getLocation(p, 1),
+ identifier,
+ returnType=BuiltinTypes[IDLBuiltinType.Types.object],
+ arguments=[],
+ jsonifier=True)
+ p[0] = method
+
+ def p_QualifierStatic(self, p):
"""
- Qualifiers : STATIC
+ Qualifier : STATIC
"""
- p[0] = [IDLMethod.Special.Static]
+ p[0] = [IDLInterfaceMember.Special.Static]
- def p_QualifiersSpecials(self, p):
+ def p_QualifierStringifier(self, p):
"""
- Qualifiers : Specials
+ Qualifier : STRINGIFIER
+ """
+ p[0] = [IDLInterfaceMember.Special.Stringifier]
+
+ def p_Qualifiers(self, p):
+ """
+ Qualifiers : Qualifier
+ | Specials
"""
p[0] = p[1]
@@ -3458,12 +4388,6 @@ class Parser(Tokenizer):
"""
p[0] = IDLMethod.Special.LegacyCaller
- def p_SpecialStringifier(self, p):
- """
- Special : STRINGIFIER
- """
- p[0] = IDLMethod.Special.Stringifier
-
def p_OperationRest(self, p):
"""
OperationRest : ReturnType OptionalIdentifier LPAREN ArgumentList RPAREN SEMICOLON
@@ -3510,7 +4434,7 @@ class Parser(Tokenizer):
def p_Argument(self, p):
"""
- Argument : ExtendedAttributeList Optional Type Ellipsis IDENTIFIER DefaultValue
+ Argument : ExtendedAttributeList Optional Type Ellipsis ArgumentName DefaultValue
"""
t = p[3]
assert isinstance(t, IDLType)
@@ -3524,6 +4448,10 @@ class Parser(Tokenizer):
raise WebIDLError("Mandatory arguments can't have a default value.",
[self.getLocation(p, 6)])
+ # We can't test t.isAny() here and give it a default value as needed,
+ # since at this point t is not a fully resolved type yet (e.g. it might
+ # be a typedef). We'll handle the 'any' case in IDLArgument.complete.
+
if variadic:
if optional:
raise WebIDLError("Variadic arguments should not be marked optional.",
@@ -3533,6 +4461,33 @@ class Parser(Tokenizer):
p[0] = IDLArgument(self.getLocation(p, 5), identifier, t, optional, defaultValue, variadic)
p[0].addExtendedAttributes(p[1])
+ def p_ArgumentName(self, p):
+ """
+ ArgumentName : IDENTIFIER
+ | ATTRIBUTE
+ | CALLBACK
+ | CONST
+ | CREATOR
+ | DELETER
+ | DICTIONARY
+ | ENUM
+ | EXCEPTION
+ | GETTER
+ | IMPLEMENTS
+ | INHERIT
+ | INTERFACE
+ | LEGACYCALLER
+ | PARTIAL
+ | SERIALIZER
+ | SETTER
+ | STATIC
+ | STRINGIFIER
+ | JSONIFIER
+ | TYPEDEF
+ | UNRESTRICTED
+ """
+ p[0] = p[1]
+
def p_Optional(self, p):
"""
Optional : OPTIONAL
@@ -3653,10 +4608,12 @@ class Parser(Tokenizer):
| OCTET
| OPTIONAL
| SEQUENCE
+ | MOZMAP
| SETTER
| SHORT
| STATIC
| STRINGIFIER
+ | JSONIFIER
| TRUE
| TYPEDEF
| UNSIGNED
@@ -3732,7 +4689,7 @@ class Parser(Tokenizer):
def p_UnionMemberTypesEmpty(self, p):
"""
- UnionMemberTypes :
+ UnionMemberTypes :
"""
p[0] = []
@@ -3761,6 +4718,16 @@ class Parser(Tokenizer):
type = IDLNullableType(self.getLocation(p, 5), type)
p[0] = type
+ def p_NonAnyTypeMozMapType(self, p):
+ """
+ NonAnyType : MOZMAP LT Type GT Null
+ """
+ innerType = p[3]
+ type = IDLMozMapType(self.getLocation(p, 1), innerType)
+ if p[5]:
+ type = IDLNullableType(self.getLocation(p, 5), type)
+ p[0] = type
+
def p_NonAnyTypeScopedName(self, p):
"""
NonAnyType : ScopedName TypeSuffix
@@ -3788,8 +4755,8 @@ class Parser(Tokenizer):
"""
NonAnyType : DATE TypeSuffix
"""
- assert False
- pass
+ p[0] = self.handleModifiers(BuiltinTypes[IDLBuiltinType.Types.date],
+ p[2])
def p_ConstType(self, p):
"""
@@ -3841,12 +4808,24 @@ class Parser(Tokenizer):
"""
p[0] = IDLBuiltinType.Types.float
+ def p_PrimitiveOrStringTypeUnrestictedFloat(self, p):
+ """
+ PrimitiveOrStringType : UNRESTRICTED FLOAT
+ """
+ p[0] = IDLBuiltinType.Types.unrestricted_float
+
def p_PrimitiveOrStringTypeDouble(self, p):
"""
PrimitiveOrStringType : DOUBLE
"""
p[0] = IDLBuiltinType.Types.double
+ def p_PrimitiveOrStringTypeUnrestictedDouble(self, p):
+ """
+ PrimitiveOrStringType : UNRESTRICTED DOUBLE
+ """
+ p[0] = IDLBuiltinType.Types.unrestricted_double
+
def p_PrimitiveOrStringTypeDOMString(self, p):
"""
PrimitiveOrStringType : DOMSTRING
@@ -4016,7 +4995,8 @@ class Parser(Tokenizer):
def p_error(self, p):
if not p:
- raise WebIDLError("Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both", [])
+ raise WebIDLError("Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both",
+ [self._filename])
else:
raise WebIDLError("invalid syntax", [Location(self.lexer, p.lineno, p.lexpos, self._filename)])