mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-04 16:15:25 +00:00
5677 lines
208 KiB
Python
5677 lines
208 KiB
Python
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
""" A WebIDL parser. """
|
|
|
|
from ply import lex, yacc
|
|
import re
|
|
import os
|
|
import traceback
|
|
import math
|
|
from collections import defaultdict
|
|
|
|
# Machinery
|
|
|
|
def parseInt(literal):
|
|
string = literal
|
|
sign = 0
|
|
base = 0
|
|
|
|
if string[0] == '-':
|
|
sign = -1
|
|
string = string[1:]
|
|
else:
|
|
sign = 1
|
|
|
|
if string[0] == '0' and len(string) > 1:
|
|
if string[1] == 'x' or string[1] == 'X':
|
|
base = 16
|
|
string = string[2:]
|
|
else:
|
|
base = 8
|
|
string = string[1:]
|
|
else:
|
|
base = 10
|
|
|
|
value = int(string, base)
|
|
return value * sign
|
|
|
|
# Magic for creating enums
|
|
def M_add_class_attribs(attribs, start):
|
|
def foo(name, bases, dict_):
|
|
for v, k in enumerate(attribs):
|
|
dict_[k] = start + v
|
|
assert 'length' not in dict_
|
|
dict_['length'] = start + len(attribs)
|
|
return type(name, bases, dict_)
|
|
return foo
|
|
|
|
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()
|
|
|
|
class WebIDLError(Exception):
|
|
def __init__(self, message, locations, warning=False):
|
|
self.message = message
|
|
self.locations = [str(loc) for loc in locations]
|
|
self.warning = warning
|
|
|
|
def __str__(self):
|
|
return "%s: %s%s%s" % (self.warning and 'warning' or 'error',
|
|
self.message,
|
|
", " if len(self.locations) != 0 else "",
|
|
"\n".join(self.locations))
|
|
|
|
class Location(object):
|
|
def __init__(self, lexer, lineno, lexpos, filename):
|
|
self._line = None
|
|
self._lineno = lineno
|
|
self._lexpos = lexpos
|
|
self._lexdata = lexer.lexdata
|
|
self._file = filename if filename else "<unknown>"
|
|
|
|
def __eq__(self, other):
|
|
return self._lexpos == other._lexpos and \
|
|
self._file == other._file
|
|
|
|
def filename(self):
|
|
return self._file
|
|
|
|
def resolve(self):
|
|
if self._line:
|
|
return
|
|
|
|
startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1
|
|
endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80)
|
|
if endofline != -1:
|
|
self._line = self._lexdata[startofline:endofline]
|
|
else:
|
|
self._line = self._lexdata[startofline:]
|
|
self._colno = self._lexpos - startofline
|
|
|
|
# Our line number seems to point to the start of self._lexdata
|
|
self._lineno += self._lexdata.count('\n', 0, startofline)
|
|
|
|
def get(self):
|
|
self.resolve()
|
|
return "%s line %s:%s" % (self._file, self._lineno, self._colno)
|
|
|
|
def _pointerline(self):
|
|
return " " * self._colno + "^"
|
|
|
|
def __str__(self):
|
|
self.resolve()
|
|
return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno,
|
|
self._line, self._pointerline())
|
|
|
|
class BuiltinLocation(object):
|
|
def __init__(self, text):
|
|
self.msg = text + "\n"
|
|
|
|
def __eq__(self, other):
|
|
return isinstance(other, BuiltinLocation) and \
|
|
self.msg == other.msg
|
|
|
|
def filename(self):
|
|
return '<builtin>'
|
|
|
|
def resolve(self):
|
|
pass
|
|
|
|
def get(self):
|
|
return self.msg
|
|
|
|
def __str__(self):
|
|
return self.get()
|
|
|
|
|
|
# Data Model
|
|
|
|
class IDLObject(object):
|
|
def __init__(self, location):
|
|
self.location = location
|
|
self.userData = dict()
|
|
|
|
def filename(self):
|
|
return self.location.filename()
|
|
|
|
def isInterface(self):
|
|
return False
|
|
|
|
def isEnum(self):
|
|
return False
|
|
|
|
def isCallback(self):
|
|
return False
|
|
|
|
def isType(self):
|
|
return False
|
|
|
|
def isDictionary(self):
|
|
return False;
|
|
|
|
def isUnion(self):
|
|
return False
|
|
|
|
def getUserData(self, key, default):
|
|
return self.userData.get(key, default)
|
|
|
|
def setUserData(self, key, value):
|
|
self.userData[key] = value
|
|
|
|
def addExtendedAttributes(self, attrs):
|
|
assert False # Override me!
|
|
|
|
def handleExtendedAttribute(self, attr):
|
|
assert False # Override me!
|
|
|
|
def _getDependentObjects(self):
|
|
assert False # Override me!
|
|
|
|
def getDeps(self, visited=None):
|
|
""" Return a set of files that this object depends on. If any of
|
|
these files are changed the parser needs to be rerun to regenerate
|
|
a new IDLObject.
|
|
|
|
The visited argument is a set of all the objects already visited.
|
|
We must test to see if we are in it, and if so, do nothing. This
|
|
prevents infinite recursion."""
|
|
|
|
# NB: We can't use visited=set() above because the default value is
|
|
# evaluated when the def statement is evaluated, not when the function
|
|
# is executed, so there would be one set for all invocations.
|
|
if visited == None:
|
|
visited = set()
|
|
|
|
if self in visited:
|
|
return set()
|
|
|
|
visited.add(self)
|
|
|
|
deps = set()
|
|
if self.filename() != "<builtin>":
|
|
deps.add(self.filename())
|
|
|
|
for d in self._getDependentObjects():
|
|
deps = deps.union(d.getDeps(visited))
|
|
|
|
return deps
|
|
|
|
class IDLScope(IDLObject):
|
|
def __init__(self, location, parentScope, identifier):
|
|
IDLObject.__init__(self, location)
|
|
|
|
self.parentScope = parentScope
|
|
if identifier:
|
|
assert isinstance(identifier, IDLIdentifier)
|
|
self._name = identifier
|
|
else:
|
|
self._name = None
|
|
|
|
self._dict = {}
|
|
self.globalNames = set()
|
|
# A mapping from global name to the set of global interfaces
|
|
# that have that global name.
|
|
self.globalNameMapping = defaultdict(set)
|
|
self.primaryGlobalAttr = None
|
|
self.primaryGlobalName = None
|
|
|
|
def __str__(self):
|
|
return self.QName()
|
|
|
|
def QName(self):
|
|
if self._name:
|
|
return self._name.QName() + "::"
|
|
return "::"
|
|
|
|
def ensureUnique(self, identifier, object):
|
|
"""
|
|
Ensure that there is at most one 'identifier' in scope ('self').
|
|
Note that object can be None. This occurs if we end up here for an
|
|
interface type we haven't seen yet.
|
|
"""
|
|
assert isinstance(identifier, IDLUnresolvedIdentifier)
|
|
assert not object or isinstance(object, IDLObjectWithIdentifier)
|
|
assert not object or object.identifier == identifier
|
|
|
|
if identifier.name in self._dict:
|
|
if not object:
|
|
return
|
|
|
|
# ensureUnique twice with the same object is not allowed
|
|
assert id(object) != id(self._dict[identifier.name])
|
|
|
|
replacement = self.resolveIdentifierConflict(self, identifier,
|
|
self._dict[identifier.name],
|
|
object)
|
|
self._dict[identifier.name] = replacement
|
|
return
|
|
|
|
assert object
|
|
|
|
self._dict[identifier.name] = object
|
|
|
|
def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
|
|
if isinstance(originalObject, IDLExternalInterface) and \
|
|
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)
|
|
|
|
raise WebIDLError(
|
|
"Multiple unresolvable definitions of identifier '%s' in scope '%s%s"
|
|
% (identifier.name, str(self), conflictdesc), [])
|
|
|
|
def _lookupIdentifier(self, identifier):
|
|
return self._dict[identifier.name]
|
|
|
|
def lookupIdentifier(self, identifier):
|
|
assert isinstance(identifier, IDLIdentifier)
|
|
assert identifier.scope == self
|
|
return self._lookupIdentifier(identifier)
|
|
|
|
class IDLIdentifier(IDLObject):
|
|
def __init__(self, location, scope, name):
|
|
IDLObject.__init__(self, location)
|
|
|
|
self.name = name
|
|
assert isinstance(scope, IDLScope)
|
|
self.scope = scope
|
|
|
|
def __str__(self):
|
|
return self.QName()
|
|
|
|
def QName(self):
|
|
return self.scope.QName() + self.name
|
|
|
|
def __hash__(self):
|
|
return self.QName().__hash__()
|
|
|
|
def __eq__(self, other):
|
|
return self.QName() == other.QName()
|
|
|
|
def object(self):
|
|
return self.scope.lookupIdentifier(self)
|
|
|
|
class IDLUnresolvedIdentifier(IDLObject):
|
|
def __init__(self, location, name, allowDoubleUnderscore = False,
|
|
allowForbidden = False):
|
|
IDLObject.__init__(self, location)
|
|
|
|
assert len(name) > 0
|
|
|
|
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:]
|
|
# 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])
|
|
|
|
self.name = name
|
|
|
|
def __str__(self):
|
|
return self.QName()
|
|
|
|
def QName(self):
|
|
return "<unresolved scope>::" + self.name
|
|
|
|
def resolve(self, scope, object):
|
|
assert isinstance(scope, IDLScope)
|
|
assert not object or isinstance(object, IDLObjectWithIdentifier)
|
|
assert not object or object.identifier == self
|
|
|
|
scope.ensureUnique(self, object)
|
|
|
|
identifier = IDLIdentifier(self.location, scope, self.name)
|
|
if object:
|
|
object.identifier = identifier
|
|
return identifier
|
|
|
|
def finish(self):
|
|
assert False # Should replace with a resolved identifier first.
|
|
|
|
class IDLObjectWithIdentifier(IDLObject):
|
|
def __init__(self, location, parentScope, identifier):
|
|
IDLObject.__init__(self, location)
|
|
|
|
assert isinstance(identifier, IDLUnresolvedIdentifier)
|
|
|
|
self.identifier = identifier
|
|
|
|
if parentScope:
|
|
self.resolve(parentScope)
|
|
|
|
self.treatNullAs = "Default"
|
|
|
|
def resolve(self, parentScope):
|
|
assert isinstance(parentScope, IDLScope)
|
|
assert isinstance(self.identifier, IDLUnresolvedIdentifier)
|
|
self.identifier.resolve(parentScope, self)
|
|
|
|
def checkForStringHandlingExtendedAttributes(self, attrs,
|
|
isDictionaryMember=False,
|
|
isOptional=False):
|
|
"""
|
|
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)
|
|
unhandledAttrs = list()
|
|
for attr in attrs:
|
|
if not attr.hasValue():
|
|
unhandledAttrs.append(attr)
|
|
continue
|
|
|
|
identifier = attr.identifier()
|
|
value = attr.value()
|
|
if identifier == "TreatNullAs":
|
|
if not self.type.isDOMString() or self.type.nullable():
|
|
raise WebIDLError("[TreatNullAs] is only allowed on "
|
|
"arguments or attributes whose type is "
|
|
"DOMString",
|
|
[self.location])
|
|
if isDictionaryMember:
|
|
raise WebIDLError("[TreatNullAs] is not allowed for "
|
|
"dictionary members", [self.location])
|
|
if value != 'EmptyString':
|
|
raise WebIDLError("[TreatNullAs] must take the identifier "
|
|
"'EmptyString', not '%s'" % value,
|
|
[self.location])
|
|
self.treatNullAs = value
|
|
else:
|
|
unhandledAttrs.append(attr)
|
|
|
|
return unhandledAttrs
|
|
|
|
class IDLObjectWithScope(IDLObjectWithIdentifier, IDLScope):
|
|
def __init__(self, location, parentScope, identifier):
|
|
assert isinstance(identifier, IDLUnresolvedIdentifier)
|
|
|
|
IDLObjectWithIdentifier.__init__(self, location, parentScope, identifier)
|
|
IDLScope.__init__(self, location, parentScope, self.identifier)
|
|
|
|
class IDLIdentifierPlaceholder(IDLObjectWithIdentifier):
|
|
def __init__(self, location, identifier):
|
|
assert isinstance(identifier, IDLUnresolvedIdentifier)
|
|
IDLObjectWithIdentifier.__init__(self, location, None, identifier)
|
|
|
|
def finish(self, scope):
|
|
try:
|
|
scope._lookupIdentifier(self.identifier)
|
|
except:
|
|
raise WebIDLError("Unresolved type '%s'." % self.identifier,
|
|
[self.location])
|
|
|
|
obj = self.identifier.resolve(scope, None)
|
|
return scope.lookupIdentifier(obj)
|
|
|
|
class IDLExternalInterface(IDLObjectWithIdentifier):
|
|
def __init__(self, location, parentScope, identifier):
|
|
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 IDLPartialInterface(IDLObject):
|
|
def __init__(self, location, name, members, nonPartialInterface):
|
|
assert isinstance(name, IDLUnresolvedIdentifier)
|
|
|
|
IDLObject.__init__(self, location)
|
|
self.identifier = name
|
|
self.members = members
|
|
# propagatedExtendedAttrs are the ones that should get
|
|
# propagated to our non-partial interface.
|
|
self.propagatedExtendedAttrs = []
|
|
self._nonPartialInterface = nonPartialInterface
|
|
self._finished = False
|
|
nonPartialInterface.addPartialInterface(self)
|
|
|
|
def addExtendedAttributes(self, attrs):
|
|
for attr in attrs:
|
|
identifier = attr.identifier()
|
|
|
|
if identifier in ["Constructor", "NamedConstructor"]:
|
|
self.propagatedExtendedAttrs.append(attr)
|
|
elif identifier == "Exposed":
|
|
# This just gets propagated to all our members.
|
|
for member in self.members:
|
|
if len(member._exposureGlobalNames) != 0:
|
|
raise WebIDLError("[Exposed] specified on both a "
|
|
"partial interface member and on the "
|
|
"partial interface itself",
|
|
[member.location, attr.location])
|
|
member.addExtendedAttributes([attr])
|
|
else:
|
|
raise WebIDLError("Unknown extended attribute %s on partial "
|
|
"interface" % identifier,
|
|
[attr.location])
|
|
|
|
def finish(self, scope):
|
|
if self._finished:
|
|
return
|
|
self._finished = True
|
|
# Need to make sure our non-partial interface gets finished so it can
|
|
# report cases when we only have partial interfaces.
|
|
self._nonPartialInterface.finish(scope)
|
|
|
|
def validate(self):
|
|
pass
|
|
|
|
|
|
def convertExposedAttrToGlobalNameSet(exposedAttr, targetSet):
|
|
assert len(targetSet) == 0
|
|
if exposedAttr.hasValue():
|
|
targetSet.add(exposedAttr.value())
|
|
else:
|
|
assert exposedAttr.hasArgs()
|
|
targetSet.update(exposedAttr.args())
|
|
|
|
def globalNameSetToExposureSet(globalScope, nameSet, exposureSet):
|
|
for name in nameSet:
|
|
exposureSet.update(globalScope.globalNameMapping[name])
|
|
|
|
class IDLInterface(IDLObjectWithScope):
|
|
def __init__(self, location, parentScope, name, parent, members,
|
|
isKnownNonPartial):
|
|
assert isinstance(parentScope, IDLScope)
|
|
assert isinstance(name, IDLUnresolvedIdentifier)
|
|
assert isKnownNonPartial or not parent
|
|
assert isKnownNonPartial or len(members) == 0
|
|
|
|
self.parent = None
|
|
self._callback = False
|
|
self._finished = False
|
|
self.members = []
|
|
self._partialInterfaces = []
|
|
self._extendedAttrDict = {}
|
|
# namedConstructors needs deterministic ordering because bindings code
|
|
# outputs the constructs in the order that namedConstructors enumerates
|
|
# them.
|
|
self.namedConstructors = list()
|
|
self.implementedInterfaces = set()
|
|
self._consequential = False
|
|
self._isKnownNonPartial = False
|
|
# self.interfacesBasedOnSelf is the set of interfaces that inherit from
|
|
# 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
|
|
# _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)
|
|
|
|
if isKnownNonPartial:
|
|
self.setNonPartial(location, parent, members)
|
|
|
|
def __str__(self):
|
|
return "Interface '%s'" % self.identifier.name
|
|
|
|
def ctor(self):
|
|
identifier = IDLUnresolvedIdentifier(self.location, "constructor",
|
|
allowForbidden=True)
|
|
try:
|
|
return self._lookupIdentifier(identifier)
|
|
except:
|
|
return None
|
|
|
|
def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
|
|
assert isinstance(scope, IDLScope)
|
|
assert isinstance(originalObject, IDLInterfaceMember)
|
|
assert isinstance(newObject, IDLInterfaceMember)
|
|
|
|
retval = IDLScope.resolveIdentifierConflict(self, scope, identifier,
|
|
originalObject, newObject)
|
|
|
|
# Might be a ctor, which isn't in self.members
|
|
if newObject in self.members:
|
|
self.members.remove(newObject)
|
|
return retval
|
|
|
|
def finish(self, scope):
|
|
if self._finished:
|
|
return
|
|
|
|
self._finished = True
|
|
|
|
if not self._isKnownNonPartial:
|
|
raise WebIDLError("Interface %s does not have a non-partial "
|
|
"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)
|
|
|
|
# Now go ahead and merge in our partial interfaces.
|
|
for partial in self._partialInterfaces:
|
|
partial.finish(scope)
|
|
self.addExtendedAttributes(partial.propagatedExtendedAttrs)
|
|
self.members.extend(partial.members)
|
|
|
|
# Now that we've merged in our partial interfaces, set the
|
|
# _exposureGlobalNames on any members that don't have it set yet. Note
|
|
# that any partial interfaces that had [Exposed] set have already set up
|
|
# _exposureGlobalNames on all the members coming from them, so this is
|
|
# just implementing the "members default to interface that defined them"
|
|
# and "partial interfaces default to interface they're a partial for"
|
|
# rules from the spec.
|
|
for m in self.members:
|
|
# If m, or the partial interface m came from, had [Exposed]
|
|
# specified, it already has a nonempty exposure global names set.
|
|
if len(m._exposureGlobalNames) == 0:
|
|
m._exposureGlobalNames.update(self._exposureGlobalNames)
|
|
|
|
assert not self.parent or isinstance(self.parent, IDLIdentifierPlaceholder)
|
|
parent = self.parent.finish(scope) if self.parent else None
|
|
if parent and isinstance(parent, IDLExternalInterface):
|
|
raise WebIDLError("%s inherits from %s which does not have "
|
|
"a definition" %
|
|
(self.identifier.name,
|
|
self.parent.identifier.name),
|
|
[self.location])
|
|
assert not parent or isinstance(parent, IDLInterface)
|
|
|
|
self.parent = parent
|
|
|
|
assert iter(self.members)
|
|
|
|
if self.parent:
|
|
self.parent.finish(scope)
|
|
|
|
self.parent._hasChildInterfaces = True
|
|
|
|
self.totalMembersInSlots = self.parent.totalMembersInSlots
|
|
|
|
# Interfaces with [Global] or [PrimaryGlobal] must not
|
|
# have anything inherit from them
|
|
if (self.parent.getExtendedAttribute("Global") or
|
|
self.parent.getExtendedAttribute("PrimaryGlobal")):
|
|
# 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])
|
|
|
|
# Make sure that we're not exposed in places where our parent is not
|
|
if not self.exposureSet.issubset(self.parent.exposureSet):
|
|
raise WebIDLError("Interface %s is exposed in globals where its "
|
|
"parent interface %s is not exposed." %
|
|
(self.identifier.name,
|
|
self.parent.identifier.name),
|
|
[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.
|
|
# XXXbz Can callbacks have consequential interfaces? Spec issue pending
|
|
if self.isCallback():
|
|
if not self.parent.isCallback():
|
|
raise WebIDLError("Callback interface %s inheriting from "
|
|
"non-callback interface %s" %
|
|
(self.identifier.name,
|
|
self.parent.identifier.name),
|
|
[self.location, self.parent.location])
|
|
elif self.parent.isCallback():
|
|
raise WebIDLError("Non-callback interface %s inheriting from "
|
|
"callback interface %s" %
|
|
(self.identifier.name,
|
|
self.parent.identifier.name),
|
|
[self.location, self.parent.location])
|
|
|
|
for iface in self.implementedInterfaces:
|
|
iface.finish(scope)
|
|
|
|
cycleInGraph = self.findInterfaceLoopPoint(self)
|
|
if cycleInGraph:
|
|
raise WebIDLError("Interface %s has itself as ancestor or "
|
|
"implemented interface" % self.identifier.name,
|
|
[self.location, cycleInGraph.location])
|
|
|
|
if self.isCallback():
|
|
# "implements" should have made sure we have no
|
|
# consequential interfaces.
|
|
assert len(self.getConsequentialInterfaces()) == 0
|
|
# And that we're not consequential.
|
|
assert not self.isConsequential()
|
|
|
|
# Now resolve() and finish() our members before importing the
|
|
# ones from our implemented interfaces.
|
|
|
|
# resolve() will modify self.members, so we need to iterate
|
|
# over a copy of the member list here.
|
|
for member in list(self.members):
|
|
member.resolve(self)
|
|
|
|
for member in self.members:
|
|
member.finish(scope)
|
|
|
|
# Now that we've finished our members, which has updated their exposure
|
|
# sets, make sure they aren't exposed in places where we are not.
|
|
for member in self.members:
|
|
if not member.exposureSet.issubset(self.exposureSet):
|
|
raise WebIDLError("Interface member has larger exposure set "
|
|
"than the interface itself",
|
|
[member.location, self.location])
|
|
|
|
ctor = self.ctor()
|
|
if ctor is not None:
|
|
ctor.finish(scope)
|
|
|
|
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)
|
|
|
|
# Import everything from our consequential interfaces into
|
|
# self.members. Sort our consequential interfaces by name
|
|
# just so we have a consistent order.
|
|
for iface in sorted(self.getConsequentialInterfaces(),
|
|
cmp=cmp,
|
|
key=lambda x: x.identifier.name):
|
|
# Flag the interface as being someone's consequential interface
|
|
iface.setIsConsequentialInterfaceOf(self)
|
|
# Verify that we're not exposed somewhere where iface is not exposed
|
|
if not self.exposureSet.issubset(iface.exposureSet):
|
|
raise WebIDLError("Interface %s is exposed in globals where its "
|
|
"consequential interface %s is not exposed." %
|
|
(self.identifier.name, iface.identifier.name),
|
|
[self.location, iface.location])
|
|
additionalMembers = iface.originalMembers;
|
|
for additionalMember in additionalMembers:
|
|
for member in self.members:
|
|
if additionalMember.identifier.name == member.identifier.name:
|
|
raise WebIDLError(
|
|
"Multiple definitions of %s on %s coming from 'implements' statements" %
|
|
(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)
|
|
|
|
# Deal with interfaces marked [Unforgeable], now that we have our full
|
|
# member list, except unforgeables pulled in from parents. We want to
|
|
# do this before we set "originatingInterface" on our unforgeable
|
|
# members.
|
|
if self.getExtendedAttribute("Unforgeable"):
|
|
# Check that the interface already has all the things the
|
|
# spec would otherwise require us to synthesize and is
|
|
# missing the ones we plan to synthesize.
|
|
if not any(m.isMethod() and m.isStringifier() for m in self.members):
|
|
raise WebIDLError("Unforgeable interface %s does not have a "
|
|
"stringifier" % self.identifier.name,
|
|
[self.location])
|
|
|
|
for m in self.members:
|
|
if ((m.isMethod() and m.isJsonifier()) or
|
|
m.identifier.name == "toJSON"):
|
|
raise WebIDLError("Unforgeable interface %s has a "
|
|
"jsonifier so we won't be able to add "
|
|
"one ourselves" % self.identifier.name,
|
|
[self.location, m.location])
|
|
|
|
if m.identifier.name == "valueOf" and not m.isStatic():
|
|
raise WebIDLError("Unforgeable interface %s has a valueOf "
|
|
"member so we won't be able to add one "
|
|
"ourselves" % self.identifier.name,
|
|
[self.location, m.location])
|
|
|
|
for member in self.members:
|
|
if ((member.isAttr() or member.isMethod()) 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 unforgeableMember in (member for member in self.parent.members if
|
|
(member.isAttr() or member.isMethod()) and
|
|
member.isUnforgeable()):
|
|
shadows = [ m for m in self.members if
|
|
(m.isAttr() or m.isMethod()) and
|
|
not m.isStatic() and
|
|
m.identifier.name == unforgeableMember.identifier.name ]
|
|
if len(shadows) != 0:
|
|
locs = [unforgeableMember.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/methods of ancestor interfaces, with their
|
|
# corresponding getters, on our interface, but that gets pretty
|
|
# complicated and seems unnecessary.
|
|
self.members.append(unforgeableMember)
|
|
|
|
# Ensure that there's at most one of each {named,indexed}
|
|
# {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 not member.isMethod():
|
|
continue
|
|
|
|
if member.isGetter():
|
|
memberType = "getters"
|
|
elif member.isSetter():
|
|
memberType = "setters"
|
|
elif member.isCreator():
|
|
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 (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[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):
|
|
# We don't support consequential unforgeable interfaces. Need to check
|
|
# this here, becaue in finish() an interface might not know yet that
|
|
# it's consequential.
|
|
if self.getExtendedAttribute("Unforgeable") and self.isConsequential():
|
|
raise WebIDLError(
|
|
"%s is an unforgeable consequential interface" %
|
|
self.identifier.name,
|
|
[self.location] +
|
|
list(i.location for i in
|
|
(self.interfacesBasedOnSelf - { self }) ))
|
|
|
|
# We also don't support inheriting from unforgeable interfaces.
|
|
if self.getExtendedAttribute("Unforgeable") and self.hasChildInterfaces():
|
|
raise WebIDLError("%s is an unforgeable ancestor interface" %
|
|
self.identifier.name,
|
|
[self.location] +
|
|
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():
|
|
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")
|
|
|
|
if (self.getExtendedAttribute("Pref") and
|
|
self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])):
|
|
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
|
|
|
|
def isExternal(self):
|
|
return False
|
|
|
|
def setIsConsequentialInterfaceOf(self, other):
|
|
self._consequential = True
|
|
self.interfacesBasedOnSelf.add(other)
|
|
|
|
def isConsequential(self):
|
|
return self._consequential
|
|
|
|
def setCallback(self, value):
|
|
self._callback = value
|
|
|
|
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 isExposedInWindow(self):
|
|
return 'Window' in self.exposureSet
|
|
|
|
def isExposedInAnyWorker(self):
|
|
return len(self.getWorkerExposureSet()) > 0
|
|
|
|
def isExposedOnlyInSomeWorkers(self):
|
|
assert self.isExposedInAnyWorker()
|
|
workerScopes = self.parentScope.globalNameMapping["Worker"]
|
|
return len(workerScopes.difference(self.exposureSet)) > 0
|
|
|
|
def getWorkerExposureSet(self):
|
|
workerScopes = self.parentScope.globalNameMapping["Worker"]
|
|
return workerScopes.intersection(self.exposureSet)
|
|
|
|
def inheritanceDepth(self):
|
|
depth = 0
|
|
parent = self.parent
|
|
while parent:
|
|
depth = depth + 1
|
|
parent = parent.parent
|
|
return depth
|
|
|
|
def hasConstants(self):
|
|
return any(m.isConst() for m in self.members)
|
|
|
|
def hasInterfaceObject(self):
|
|
if self.isCallback():
|
|
return self.hasConstants()
|
|
return not hasattr(self, "_noInterfaceObject")
|
|
|
|
def hasInterfacePrototypeObject(self):
|
|
return not self.isCallback() and self.getUserData('hasConcreteDescendant', False)
|
|
|
|
def addExtendedAttributes(self, attrs):
|
|
for attr in attrs:
|
|
identifier = attr.identifier()
|
|
|
|
# Special cased attrs
|
|
if identifier == "TreatNonCallableAsNull":
|
|
raise WebIDLError("TreatNonCallableAsNull cannot be specified on interfaces",
|
|
[attr.location, self.location])
|
|
if identifier == "TreatNonObjectAsNull":
|
|
raise WebIDLError("TreatNonObjectAsNull cannot be specified on interfaces",
|
|
[attr.location, self.location])
|
|
elif identifier == "NoInterfaceObject":
|
|
if not attr.noArguments():
|
|
raise WebIDLError("[NoInterfaceObject] must take no arguments",
|
|
[attr.location])
|
|
|
|
if self.ctor():
|
|
raise WebIDLError("Constructor and NoInterfaceObject are incompatible",
|
|
[self.location])
|
|
|
|
self._noInterfaceObject = True
|
|
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)
|
|
|
|
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, ("NewObject",)),
|
|
IDLExtendedAttribute(self.location, ("Throws",))])
|
|
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 attr.hasValue():
|
|
self.globalNames = [ attr.value() ]
|
|
elif attr.hasArgs():
|
|
self.globalNames = attr.args()
|
|
else:
|
|
self.globalNames = [ self.identifier.name ]
|
|
self.parentScope.globalNames.update(self.globalNames)
|
|
for globalName in self.globalNames:
|
|
self.parentScope.globalNameMapping[globalName].add(self.identifier.name)
|
|
self._isOnGlobalProtoChain = True
|
|
elif identifier == "PrimaryGlobal":
|
|
if not attr.noArguments():
|
|
raise WebIDLError("[PrimaryGlobal] must take no arguments",
|
|
[attr.location])
|
|
if self.parentScope.primaryGlobalAttr is not None:
|
|
raise WebIDLError(
|
|
"[PrimaryGlobal] specified twice",
|
|
[attr.location,
|
|
self.parentScope.primaryGlobalAttr.location])
|
|
self.parentScope.primaryGlobalAttr = attr
|
|
self.parentScope.primaryGlobalName = self.identifier.name
|
|
self.parentScope.globalNames.add(self.identifier.name)
|
|
self.parentScope.globalNameMapping[self.identifier.name].add(self.identifier.name)
|
|
self._isOnGlobalProtoChain = True
|
|
elif (identifier == "NeedNewResolve" 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():
|
|
raise WebIDLError("[%s] must take no arguments" % identifier,
|
|
[attr.location])
|
|
elif identifier == "Exposed":
|
|
convertExposedAttrToGlobalNameSet(attr,
|
|
self._exposureGlobalNames)
|
|
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
|
|
|
|
def addImplementedInterface(self, implementedInterface):
|
|
assert(isinstance(implementedInterface, IDLInterface))
|
|
self.implementedInterfaces.add(implementedInterface)
|
|
|
|
def getInheritedInterfaces(self):
|
|
"""
|
|
Returns a list of the interfaces this interface inherits from
|
|
(not including this interface itself). The list is in order
|
|
from most derived to least derived.
|
|
"""
|
|
assert(self._finished)
|
|
if not self.parent:
|
|
return []
|
|
parentInterfaces = self.parent.getInheritedInterfaces()
|
|
parentInterfaces.insert(0, self.parent)
|
|
return parentInterfaces
|
|
|
|
def getConsequentialInterfaces(self):
|
|
assert(self._finished)
|
|
# The interfaces we implement directly
|
|
consequentialInterfaces = set(self.implementedInterfaces)
|
|
|
|
# And their inherited interfaces
|
|
for iface in self.implementedInterfaces:
|
|
consequentialInterfaces |= set(iface.getInheritedInterfaces())
|
|
|
|
# And now collect up the consequential interfaces of all of those
|
|
temp = set()
|
|
for iface in consequentialInterfaces:
|
|
temp |= iface.getConsequentialInterfaces()
|
|
|
|
return consequentialInterfaces | temp
|
|
|
|
def findInterfaceLoopPoint(self, otherInterface):
|
|
"""
|
|
Finds an interface, amongst our ancestors and consequential interfaces,
|
|
that inherits from otherInterface or implements otherInterface
|
|
directly. If there is no such interface, returns None.
|
|
"""
|
|
if self.parent:
|
|
if self.parent == otherInterface:
|
|
return self
|
|
loopPoint = self.parent.findInterfaceLoopPoint(otherInterface)
|
|
if loopPoint:
|
|
return loopPoint
|
|
if otherInterface in self.implementedInterfaces:
|
|
return self
|
|
for iface in self.implementedInterfaces:
|
|
loopPoint = iface.findInterfaceLoopPoint(otherInterface)
|
|
if loopPoint:
|
|
return loopPoint
|
|
return None
|
|
|
|
def getExtendedAttribute(self, name):
|
|
return self._extendedAttrDict.get(name, None)
|
|
|
|
def setNonPartial(self, location, parent, members):
|
|
assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
|
|
if self._isKnownNonPartial:
|
|
raise WebIDLError("Two non-partial definitions for the "
|
|
"same interface",
|
|
[location, self.location])
|
|
self._isKnownNonPartial = True
|
|
# Now make it look like we were parsed at this new location, since
|
|
# that's the place where the interface is "really" defined
|
|
self.location = location
|
|
assert not self.parent
|
|
self.parent = parent
|
|
# Put the new members at the beginning
|
|
self.members = members + self.members
|
|
|
|
def addPartialInterface(self, partial):
|
|
assert self.identifier.name == partial.identifier.name
|
|
self._partialInterfaces.append(partial)
|
|
|
|
def getJSImplementation(self):
|
|
classId = self.getExtendedAttribute("JSImplementation")
|
|
if not classId:
|
|
return classId
|
|
assert isinstance(classId, list)
|
|
assert len(classId) == 1
|
|
return classId[0]
|
|
|
|
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)
|
|
if self.parent:
|
|
deps.add(self.parent)
|
|
return deps
|
|
|
|
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)
|
|
assert isinstance(name, IDLUnresolvedIdentifier)
|
|
assert not parent or isinstance(parent, IDLIdentifierPlaceholder)
|
|
|
|
self.parent = parent
|
|
self._finished = False
|
|
self.members = list(members)
|
|
|
|
IDLObjectWithScope.__init__(self, location, parentScope, name)
|
|
|
|
def __str__(self):
|
|
return "Dictionary '%s'" % self.identifier.name
|
|
|
|
def isDictionary(self):
|
|
return True;
|
|
|
|
def finish(self, scope):
|
|
if self._finished:
|
|
return
|
|
|
|
self._finished = True
|
|
|
|
if self.parent:
|
|
assert isinstance(self.parent, IDLIdentifierPlaceholder)
|
|
oldParent = self.parent
|
|
self.parent = self.parent.finish(scope)
|
|
if not isinstance(self.parent, IDLDictionary):
|
|
raise WebIDLError("Dictionary %s has parent that is not a dictionary" %
|
|
self.identifier.name,
|
|
[oldParent.location, self.parent.location])
|
|
|
|
# Make sure the parent resolves all its members before we start
|
|
# looking at them.
|
|
self.parent.finish(scope)
|
|
|
|
for member in self.members:
|
|
member.resolve(self)
|
|
if not member.isComplete():
|
|
member.complete(scope)
|
|
assert member.type.isComplete()
|
|
|
|
# Members of a dictionary are sorted in lexicographic order
|
|
self.members.sort(cmp=cmp, key=lambda x: x.identifier.name)
|
|
|
|
inheritedMembers = []
|
|
ancestor = self.parent
|
|
while ancestor:
|
|
if ancestor == self:
|
|
raise WebIDLError("Dictionary %s has itself as an ancestor" %
|
|
self.identifier.name,
|
|
[self.identifier.location])
|
|
inheritedMembers.extend(ancestor.members)
|
|
ancestor = ancestor.parent
|
|
|
|
# Catch name duplication
|
|
for inheritedMember in inheritedMembers:
|
|
for member in self.members:
|
|
if member.identifier.name == inheritedMember.identifier.name:
|
|
raise WebIDLError("Dictionary %s has two members with name %s" %
|
|
(self.identifier.name, member.identifier.name),
|
|
[member.location, inheritedMember.location])
|
|
|
|
def validate(self):
|
|
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
|
|
|
|
def _getDependentObjects(self):
|
|
deps = set(self.members)
|
|
if (self.parent):
|
|
deps.add(self.parent)
|
|
return deps
|
|
|
|
class IDLEnum(IDLObjectWithIdentifier):
|
|
def __init__(self, location, parentScope, name, values):
|
|
assert isinstance(parentScope, IDLScope)
|
|
assert isinstance(name, IDLUnresolvedIdentifier)
|
|
|
|
if len(values) != len(set(values)):
|
|
raise WebIDLError("Enum %s has multiple identical strings" % name.name,
|
|
[location])
|
|
|
|
IDLObjectWithIdentifier.__init__(self, location, parentScope, name)
|
|
self._values = values
|
|
|
|
def values(self):
|
|
return self._values
|
|
|
|
def finish(self, scope):
|
|
pass
|
|
|
|
def validate(self):
|
|
pass
|
|
|
|
def isEnum(self):
|
|
return True
|
|
|
|
def addExtendedAttributes(self, attrs):
|
|
assert len(attrs) == 0
|
|
|
|
def _getDependentObjects(self):
|
|
return set()
|
|
|
|
class IDLType(IDLObject):
|
|
Tags = enum(
|
|
# The integer types
|
|
'int8',
|
|
'uint8',
|
|
'int16',
|
|
'uint16',
|
|
'int32',
|
|
'uint32',
|
|
'int64',
|
|
'uint64',
|
|
# Additional primitive types
|
|
'bool',
|
|
'unrestricted_float',
|
|
'float',
|
|
'unrestricted_double',
|
|
# "double" last primitive type to match IDLBuiltinType
|
|
'double',
|
|
# Other types
|
|
'any',
|
|
'domstring',
|
|
'bytestring',
|
|
'scalarvaluestring',
|
|
'object',
|
|
'date',
|
|
'void',
|
|
# Funny stuff
|
|
'interface',
|
|
'dictionary',
|
|
'enum',
|
|
'callback',
|
|
'union',
|
|
'sequence',
|
|
'mozmap',
|
|
'array'
|
|
)
|
|
|
|
def __init__(self, location, name):
|
|
IDLObject.__init__(self, location)
|
|
self.name = name
|
|
self.builtin = False
|
|
|
|
def __eq__(self, other):
|
|
return other and self.builtin == other.builtin and self.name == other.name
|
|
|
|
def __ne__(self, other):
|
|
return not self == other
|
|
|
|
def __str__(self):
|
|
return str(self.name)
|
|
|
|
def isType(self):
|
|
return True
|
|
|
|
def nullable(self):
|
|
return False
|
|
|
|
def isPrimitive(self):
|
|
return False
|
|
|
|
def isBoolean(self):
|
|
return False
|
|
|
|
def isNumeric(self):
|
|
return False
|
|
|
|
def isString(self):
|
|
return False
|
|
|
|
def isByteString(self):
|
|
return False
|
|
|
|
def isDOMString(self):
|
|
return False
|
|
|
|
def isScalarValueString(self):
|
|
return False
|
|
|
|
def isVoid(self):
|
|
return self.name == "Void"
|
|
|
|
def isSequence(self):
|
|
return False
|
|
|
|
def isMozMap(self):
|
|
return False
|
|
|
|
def isArray(self):
|
|
return False
|
|
|
|
def isArrayBuffer(self):
|
|
return False
|
|
|
|
def isArrayBufferView(self):
|
|
return False
|
|
|
|
def isTypedArray(self):
|
|
return False
|
|
|
|
def isCallbackInterface(self):
|
|
return False
|
|
|
|
def isNonCallbackInterface(self):
|
|
return False
|
|
|
|
def isGeckoInterface(self):
|
|
""" Returns a boolean indicating whether this type is an 'interface'
|
|
type that is implemented in Gecko. At the moment, this returns
|
|
true for all interface types that are not types from the TypedArray
|
|
spec."""
|
|
return self.isInterface() and not self.isSpiderMonkeyInterface()
|
|
|
|
def isSpiderMonkeyInterface(self):
|
|
""" Returns a boolean indicating whether this type is an 'interface'
|
|
type that is implemented in Spidermonkey. At the moment, this
|
|
only returns true for the types from the TypedArray spec. """
|
|
return self.isInterface() and (self.isArrayBuffer() or \
|
|
self.isArrayBufferView() or \
|
|
self.isTypedArray())
|
|
|
|
def isDictionary(self):
|
|
return False
|
|
|
|
def isInterface(self):
|
|
return False
|
|
|
|
def isAny(self):
|
|
return self.tag() == IDLType.Tags.any
|
|
|
|
def isDate(self):
|
|
return self.tag() == IDLType.Tags.date
|
|
|
|
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!
|
|
|
|
def treatNonCallableAsNull(self):
|
|
assert self.tag() == IDLType.Tags.callback
|
|
return self.nullable() and self.inner._treatNonCallableAsNull
|
|
|
|
def treatNonObjectAsNull(self):
|
|
assert self.tag() == IDLType.Tags.callback
|
|
return self.nullable() and self.inner._treatNonObjectAsNull
|
|
|
|
def addExtendedAttributes(self, attrs):
|
|
assert len(attrs) == 0
|
|
|
|
def resolveType(self, parentScope):
|
|
pass
|
|
|
|
def unroll(self):
|
|
return self
|
|
|
|
def isDistinguishableFrom(self, other):
|
|
raise TypeError("Can't tell whether a generic type is or is not "
|
|
"distinguishable from other things")
|
|
|
|
def isExposedInAllOf(self, exposureSet):
|
|
return True
|
|
|
|
class IDLUnresolvedType(IDLType):
|
|
"""
|
|
Unresolved types are interface types
|
|
"""
|
|
|
|
def __init__(self, location, name, promiseInnerType=None):
|
|
IDLType.__init__(self, location, name)
|
|
self._promiseInnerType = promiseInnerType
|
|
|
|
def isComplete(self):
|
|
return False
|
|
|
|
def complete(self, scope):
|
|
obj = None
|
|
try:
|
|
obj = scope._lookupIdentifier(self.name)
|
|
except:
|
|
raise WebIDLError("Unresolved type '%s'." % self.name,
|
|
[self.location])
|
|
|
|
assert obj
|
|
if obj.isType():
|
|
# obj itself might not be complete; deal with that.
|
|
assert obj != self
|
|
if not obj.isComplete():
|
|
obj = obj.complete(scope)
|
|
return obj
|
|
|
|
if self._promiseInnerType and not self._promiseInnerType.isComplete():
|
|
self._promiseInnerType = self._promiseInnerType.complete(scope)
|
|
|
|
name = self.name.resolve(scope, None)
|
|
return IDLWrapperType(self.location, obj, self._promiseInnerType)
|
|
|
|
def isDistinguishableFrom(self, other):
|
|
raise TypeError("Can't tell whether an unresolved type is or is not "
|
|
"distinguishable from other things")
|
|
|
|
class IDLNullableType(IDLType):
|
|
def __init__(self, location, innerType):
|
|
assert not innerType.isVoid()
|
|
assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any]
|
|
|
|
IDLType.__init__(self, location, innerType.name)
|
|
self.inner = innerType
|
|
self.builtin = False
|
|
|
|
def __eq__(self, other):
|
|
return isinstance(other, IDLNullableType) and self.inner == other.inner
|
|
|
|
def __str__(self):
|
|
return self.inner.__str__() + "OrNull"
|
|
|
|
def nullable(self):
|
|
return True
|
|
|
|
def isCallback(self):
|
|
return self.inner.isCallback()
|
|
|
|
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 isScalarValueString(self):
|
|
return self.inner.isScalarValueString()
|
|
|
|
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()
|
|
|
|
def isVoid(self):
|
|
return False
|
|
|
|
def isSequence(self):
|
|
return self.inner.isSequence()
|
|
|
|
def isMozMap(self):
|
|
return self.inner.isMozMap()
|
|
|
|
def isArray(self):
|
|
return self.inner.isArray()
|
|
|
|
def isArrayBuffer(self):
|
|
return self.inner.isArrayBuffer()
|
|
|
|
def isArrayBufferView(self):
|
|
return self.inner.isArrayBufferView()
|
|
|
|
def isTypedArray(self):
|
|
return self.inner.isTypedArray()
|
|
|
|
def isDictionary(self):
|
|
return self.inner.isDictionary()
|
|
|
|
def isInterface(self):
|
|
return self.inner.isInterface()
|
|
|
|
def isCallbackInterface(self):
|
|
return self.inner.isCallbackInterface()
|
|
|
|
def isNonCallbackInterface(self):
|
|
return self.inner.isNonCallbackInterface()
|
|
|
|
def isEnum(self):
|
|
return self.inner.isEnum()
|
|
|
|
def isUnion(self):
|
|
return self.inner.isUnion()
|
|
|
|
def isSerializable(self):
|
|
return self.inner.isSerializable()
|
|
|
|
def tag(self):
|
|
return self.inner.tag()
|
|
|
|
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)
|
|
if self.inner.nullable():
|
|
raise WebIDLError("The inner type of a nullable type must not be "
|
|
"a nullable type",
|
|
[self.location, self.inner.location])
|
|
if self.inner.isUnion():
|
|
if self.inner.hasNullableType:
|
|
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])
|
|
|
|
self.name = self.inner.name
|
|
return self
|
|
|
|
def unroll(self):
|
|
return self.inner.unroll()
|
|
|
|
def isDistinguishableFrom(self, other):
|
|
if (other.nullable() or (other.isUnion() and other.hasNullableType) or
|
|
other.isDictionary()):
|
|
# Can't tell which type null should become
|
|
return False
|
|
return self.inner.isDistinguishableFrom(other)
|
|
|
|
def _getDependentObjects(self):
|
|
return self.inner._getDependentObjects()
|
|
|
|
class IDLSequenceType(IDLType):
|
|
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, IDLSequenceType) and self.inner == other.inner
|
|
|
|
def __str__(self):
|
|
return self.inner.__str__() + "Sequence"
|
|
|
|
def nullable(self):
|
|
return False
|
|
|
|
def isPrimitive(self):
|
|
return False;
|
|
|
|
def isString(self):
|
|
return False;
|
|
|
|
def isByteString(self):
|
|
return False
|
|
|
|
def isDOMString(self):
|
|
return False
|
|
|
|
def isScalarValueString(self):
|
|
return False
|
|
|
|
def isVoid(self):
|
|
return False
|
|
|
|
def isSequence(self):
|
|
return True
|
|
|
|
def isArray(self):
|
|
return False
|
|
|
|
def isDictionary(self):
|
|
return False
|
|
|
|
def isInterface(self):
|
|
return False
|
|
|
|
def isEnum(self):
|
|
return False
|
|
|
|
def isSerializable(self):
|
|
return self.inner.isSerializable()
|
|
|
|
def includesRestrictedFloat(self):
|
|
return self.inner.includesRestrictedFloat()
|
|
|
|
def tag(self):
|
|
return IDLType.Tags.sequence
|
|
|
|
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):
|
|
return self.inner.unroll()
|
|
|
|
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() or other.isMozMap())
|
|
|
|
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() or other.isSequence())
|
|
|
|
def isExposedInAllOf(self, exposureSet):
|
|
return self.inner.unroll().isExposedInAllOf(exposureSet)
|
|
|
|
def _getDependentObjects(self):
|
|
return self.inner._getDependentObjects()
|
|
|
|
class IDLUnionType(IDLType):
|
|
def __init__(self, location, memberTypes):
|
|
IDLType.__init__(self, location, "")
|
|
self.memberTypes = memberTypes
|
|
self.hasNullableType = False
|
|
self.hasDictionaryType = False
|
|
self.flatMemberTypes = None
|
|
self.builtin = False
|
|
|
|
def __eq__(self, other):
|
|
return isinstance(other, IDLUnionType) and self.memberTypes == other.memberTypes
|
|
|
|
def isVoid(self):
|
|
return False
|
|
|
|
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
|
|
|
|
def resolveType(self, parentScope):
|
|
assert isinstance(parentScope, IDLScope)
|
|
for t in self.memberTypes:
|
|
t.resolveType(parentScope)
|
|
|
|
def isComplete(self):
|
|
return self.flatMemberTypes is not None
|
|
|
|
def complete(self, scope):
|
|
def typeName(type):
|
|
if isinstance(type, IDLNullableType):
|
|
return typeName(type.inner) + "OrNull"
|
|
if isinstance(type, IDLWrapperType):
|
|
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):
|
|
if not type.isComplete():
|
|
self.memberTypes[i] = type.complete(scope)
|
|
|
|
self.name = "Or".join(typeName(type) for type in self.memberTypes)
|
|
self.flatMemberTypes = list(self.memberTypes)
|
|
i = 0
|
|
while i < len(self.flatMemberTypes):
|
|
if self.flatMemberTypes[i].nullable():
|
|
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].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
|
|
|
|
for (i, t) in enumerate(self.flatMemberTypes[:-1]):
|
|
for u in self.flatMemberTypes[i + 1:]:
|
|
if not t.isDistinguishableFrom(u):
|
|
raise WebIDLError("Flat member types of a union should be "
|
|
"distinguishable, " + str(t) + " is not "
|
|
"distinguishable from " + str(u),
|
|
[self.location, t.location, u.location])
|
|
|
|
return self
|
|
|
|
def isDistinguishableFrom(self, other):
|
|
if self.hasNullableType and other.nullable():
|
|
# Can't tell which type null should become
|
|
return False
|
|
if other.isUnion():
|
|
otherTypes = other.unroll().memberTypes
|
|
else:
|
|
otherTypes = [other]
|
|
# For every type in otherTypes, check that it's distinguishable from
|
|
# every type in our types
|
|
for u in otherTypes:
|
|
if any(not t.isDistinguishableFrom(u) for t in self.memberTypes):
|
|
return False
|
|
return True
|
|
|
|
def isExposedInAllOf(self, exposureSet):
|
|
# We could have different member types in different globals. Just make sure that each thing in exposureSet has one of our member types exposed in it.
|
|
for globalName in exposureSet:
|
|
if not any(t.unroll().isExposedInAllOf(set([globalName])) for t
|
|
in self.flatMemberTypes):
|
|
return False
|
|
return True
|
|
|
|
def _getDependentObjects(self):
|
|
return set(self.memberTypes)
|
|
|
|
class IDLArrayType(IDLType):
|
|
def __init__(self, location, parameterType):
|
|
assert not parameterType.isVoid()
|
|
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])
|
|
|
|
IDLType.__init__(self, location, parameterType.name)
|
|
self.inner = parameterType
|
|
self.builtin = False
|
|
|
|
def __eq__(self, other):
|
|
return isinstance(other, IDLArrayType) and self.inner == other.inner
|
|
|
|
def __str__(self):
|
|
return self.inner.__str__() + "Array"
|
|
|
|
def nullable(self):
|
|
return False
|
|
|
|
def isPrimitive(self):
|
|
return False
|
|
|
|
def isString(self):
|
|
return False
|
|
|
|
def isByteString(self):
|
|
return False
|
|
|
|
def isDOMString(self):
|
|
return False
|
|
|
|
def isScalarValueString(self):
|
|
return False
|
|
|
|
def isVoid(self):
|
|
return False
|
|
|
|
def isSequence(self):
|
|
assert not self.inner.isSequence()
|
|
return False
|
|
|
|
def isArray(self):
|
|
return True
|
|
|
|
def isDictionary(self):
|
|
assert not self.inner.isDictionary()
|
|
return False
|
|
|
|
def isInterface(self):
|
|
return False
|
|
|
|
def isEnum(self):
|
|
return False
|
|
|
|
def tag(self):
|
|
return IDLType.Tags.array
|
|
|
|
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
|
|
|
|
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):
|
|
return self.inner.unroll()
|
|
|
|
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()
|
|
|
|
class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
|
|
def __init__(self, location, innerType, name):
|
|
IDLType.__init__(self, location, innerType.name)
|
|
|
|
identifier = IDLUnresolvedIdentifier(location, name)
|
|
|
|
IDLObjectWithIdentifier.__init__(self, location, None, identifier)
|
|
|
|
self.inner = innerType
|
|
self.name = name
|
|
self.builtin = False
|
|
|
|
def __eq__(self, other):
|
|
return isinstance(other, IDLTypedefType) and self.inner == other.inner
|
|
|
|
def __str__(self):
|
|
return self.identifier.name
|
|
|
|
def nullable(self):
|
|
return self.inner.nullable()
|
|
|
|
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 isScalarValueString(self):
|
|
return self.inner.isScalarValueString()
|
|
|
|
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()
|
|
|
|
def isDictionary(self):
|
|
return self.inner.isDictionary()
|
|
|
|
def isArrayBuffer(self):
|
|
return self.inner.isArrayBuffer()
|
|
|
|
def isArrayBufferView(self):
|
|
return self.inner.isArrayBufferView()
|
|
|
|
def isTypedArray(self):
|
|
return self.inner.isTypedArray()
|
|
|
|
def isInterface(self):
|
|
return self.inner.isInterface()
|
|
|
|
def isCallbackInterface(self):
|
|
return self.inner.isCallbackInterface()
|
|
|
|
def isNonCallbackInterface(self):
|
|
return self.inner.isNonCallbackInterface()
|
|
|
|
def isComplete(self):
|
|
return False
|
|
|
|
def complete(self, parentScope):
|
|
if not self.inner.isComplete():
|
|
self.inner = self.inner.complete(parentScope)
|
|
assert self.inner.isComplete()
|
|
return self.inner
|
|
|
|
def finish(self, parentScope):
|
|
# Maybe the IDLObjectWithIdentifier for the typedef should be
|
|
# 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):
|
|
pass
|
|
|
|
# Do we need a resolveType impl? I don't think it's particularly useful....
|
|
|
|
def tag(self):
|
|
return self.inner.tag()
|
|
|
|
def unroll(self):
|
|
return self.inner.unroll()
|
|
|
|
def isDistinguishableFrom(self, other):
|
|
return self.inner.isDistinguishableFrom(other)
|
|
|
|
def _getDependentObjects(self):
|
|
return self.inner._getDependentObjects()
|
|
|
|
class IDLWrapperType(IDLType):
|
|
def __init__(self, location, inner, promiseInnerType=None):
|
|
IDLType.__init__(self, location, inner.identifier.name)
|
|
self.inner = inner
|
|
self._identifier = inner.identifier
|
|
self.builtin = False
|
|
assert not promiseInnerType or inner.identifier.name == "Promise"
|
|
self._promiseInnerType = promiseInnerType
|
|
|
|
def __eq__(self, other):
|
|
return isinstance(other, IDLWrapperType) and \
|
|
self._identifier == other._identifier and \
|
|
self.builtin == other.builtin
|
|
|
|
def __str__(self):
|
|
return str(self.name) + " (Wrapper)"
|
|
|
|
def nullable(self):
|
|
return False
|
|
|
|
def isPrimitive(self):
|
|
return False
|
|
|
|
def isString(self):
|
|
return False
|
|
|
|
def isByteString(self):
|
|
return False
|
|
|
|
def isDOMString(self):
|
|
return False
|
|
|
|
def isScalarValueString(self):
|
|
return False
|
|
|
|
def isVoid(self):
|
|
return False
|
|
|
|
def isSequence(self):
|
|
return False
|
|
|
|
def isArray(self):
|
|
return False
|
|
|
|
def isDictionary(self):
|
|
return isinstance(self.inner, IDLDictionary)
|
|
|
|
def isInterface(self):
|
|
return isinstance(self.inner, IDLInterface) or \
|
|
isinstance(self.inner, IDLExternalInterface)
|
|
|
|
def isCallbackInterface(self):
|
|
return self.isInterface() and self.inner.isCallback()
|
|
|
|
def isNonCallbackInterface(self):
|
|
return self.isInterface() and not self.inner.isCallback()
|
|
|
|
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)
|
|
|
|
def isComplete(self):
|
|
return True
|
|
|
|
def tag(self):
|
|
if self.isInterface():
|
|
return IDLType.Tags.interface
|
|
elif self.isEnum():
|
|
return IDLType.Tags.enum
|
|
elif self.isDictionary():
|
|
return IDLType.Tags.dictionary
|
|
else:
|
|
assert False
|
|
|
|
def isDistinguishableFrom(self, other):
|
|
if other.isUnion():
|
|
# Just forward to the union; it'll deal
|
|
return other.isDistinguishableFrom(self)
|
|
assert self.isInterface() or self.isEnum() or self.isDictionary()
|
|
if self.isEnum():
|
|
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.isDictionary() and other.nullable():
|
|
return False
|
|
if other.isPrimitive() or other.isString() or other.isEnum() or other.isDate():
|
|
return True
|
|
if self.isDictionary():
|
|
return other.isNonCallbackInterface()
|
|
|
|
assert self.isInterface()
|
|
if other.isInterface():
|
|
if other.isSpiderMonkeyInterface():
|
|
# Just let |other| handle things
|
|
return other.isDistinguishableFrom(self)
|
|
assert self.isGeckoInterface() and other.isGeckoInterface()
|
|
if self.inner.isExternal() or other.unroll().inner.isExternal():
|
|
return self != other
|
|
return (len(self.inner.interfacesBasedOnSelf &
|
|
other.unroll().inner.interfacesBasedOnSelf) == 0 and
|
|
(self.isNonCallbackInterface() or
|
|
other.isNonCallbackInterface()))
|
|
if (other.isDictionary() or other.isCallback() or
|
|
other.isSequence() or other.isMozMap() or other.isArray()):
|
|
return self.isNonCallbackInterface()
|
|
|
|
# Not much else |other| can be
|
|
assert other.isObject()
|
|
return False
|
|
|
|
def isExposedInAllOf(self, exposureSet):
|
|
if not self.isInterface():
|
|
return True
|
|
iface = self.inner
|
|
if iface.isExternal():
|
|
# Let's say true, though ideally we'd only do this when
|
|
# exposureSet contains the primary global's name.
|
|
return True
|
|
if (iface.identifier.name == "Promise" and
|
|
# Check the internal type
|
|
not self._promiseInnerType.unroll().isExposedInAllOf(exposureSet)):
|
|
return False
|
|
return iface.exposureSet.issuperset(exposureSet)
|
|
|
|
def _getDependentObjects(self):
|
|
# NB: The codegen for an interface type depends on
|
|
# a) That the identifier is in fact an interface (as opposed to
|
|
# a dictionary or something else).
|
|
# b) The native type of the interface.
|
|
# If we depend on the interface object we will also depend on
|
|
# anything the interface depends on which is undesirable. We
|
|
# considered implementing a dependency just on the interface type
|
|
# file, but then every modification to an interface would cause this
|
|
# to be regenerated which is still undesirable. We decided not to
|
|
# depend on anything, reasoning that:
|
|
# 1) Changing the concrete type of the interface requires modifying
|
|
# 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.
|
|
return set()
|
|
|
|
class IDLBuiltinType(IDLType):
|
|
|
|
Types = enum(
|
|
# The integer types
|
|
'byte',
|
|
'octet',
|
|
'short',
|
|
'unsigned_short',
|
|
'long',
|
|
'unsigned_long',
|
|
'long_long',
|
|
'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',
|
|
'domstring',
|
|
'bytestring',
|
|
'scalarvaluestring',
|
|
'object',
|
|
'date',
|
|
'void',
|
|
# Funny stuff
|
|
'ArrayBuffer',
|
|
'ArrayBufferView',
|
|
'Int8Array',
|
|
'Uint8Array',
|
|
'Uint8ClampedArray',
|
|
'Int16Array',
|
|
'Uint16Array',
|
|
'Int32Array',
|
|
'Uint32Array',
|
|
'Float32Array',
|
|
'Float64Array'
|
|
)
|
|
|
|
TagLookup = {
|
|
Types.byte: IDLType.Tags.int8,
|
|
Types.octet: IDLType.Tags.uint8,
|
|
Types.short: IDLType.Tags.int16,
|
|
Types.unsigned_short: IDLType.Tags.uint16,
|
|
Types.long: IDLType.Tags.int32,
|
|
Types.unsigned_long: IDLType.Tags.uint32,
|
|
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,
|
|
Types.bytestring: IDLType.Tags.bytestring,
|
|
Types.scalarvaluestring: IDLType.Tags.scalarvaluestring,
|
|
Types.object: IDLType.Tags.object,
|
|
Types.date: IDLType.Tags.date,
|
|
Types.void: IDLType.Tags.void,
|
|
Types.ArrayBuffer: IDLType.Tags.interface,
|
|
Types.ArrayBufferView: IDLType.Tags.interface,
|
|
Types.Int8Array: IDLType.Tags.interface,
|
|
Types.Uint8Array: IDLType.Tags.interface,
|
|
Types.Uint8ClampedArray: IDLType.Tags.interface,
|
|
Types.Int16Array: IDLType.Tags.interface,
|
|
Types.Uint16Array: IDLType.Tags.interface,
|
|
Types.Int32Array: IDLType.Tags.interface,
|
|
Types.Uint32Array: IDLType.Tags.interface,
|
|
Types.Float32Array: IDLType.Tags.interface,
|
|
Types.Float64Array: IDLType.Tags.interface
|
|
}
|
|
|
|
def __init__(self, location, name, type):
|
|
IDLType.__init__(self, location, name)
|
|
self.builtin = True
|
|
self._typeTag = type
|
|
|
|
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 or \
|
|
self._typeTag == IDLBuiltinType.Types.scalarvaluestring
|
|
|
|
def isByteString(self):
|
|
return self._typeTag == IDLBuiltinType.Types.bytestring
|
|
|
|
def isDOMString(self):
|
|
return self._typeTag == IDLBuiltinType.Types.domstring
|
|
|
|
def isScalarValueString(self):
|
|
return self._typeTag == IDLBuiltinType.Types.scalarvaluestring
|
|
|
|
def isInteger(self):
|
|
return self._typeTag <= IDLBuiltinType.Types.unsigned_long_long
|
|
|
|
def isArrayBuffer(self):
|
|
return self._typeTag == IDLBuiltinType.Types.ArrayBuffer
|
|
|
|
def isArrayBufferView(self):
|
|
return self._typeTag == IDLBuiltinType.Types.ArrayBufferView
|
|
|
|
def isTypedArray(self):
|
|
return self._typeTag >= IDLBuiltinType.Types.Int8Array and \
|
|
self._typeTag <= IDLBuiltinType.Types.Float64Array
|
|
|
|
def isInterface(self):
|
|
# TypedArray things are interface types per the TypedArray spec,
|
|
# but we handle them as builtins because SpiderMonkey implements
|
|
# all of it internally.
|
|
return self.isArrayBuffer() or \
|
|
self.isArrayBufferView() or \
|
|
self.isTypedArray()
|
|
|
|
def isNonCallbackInterface(self):
|
|
# All the interfaces we can be are non-callback
|
|
return self.isInterface()
|
|
|
|
def isFloat(self):
|
|
return self._typeTag == IDLBuiltinType.Types.float or \
|
|
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]
|
|
|
|
def isDistinguishableFrom(self, other):
|
|
if other.isUnion():
|
|
# Just forward to the union; it'll deal
|
|
return other.isDistinguishableFrom(self)
|
|
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.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
|
|
return False
|
|
if self.isObject():
|
|
return other.isPrimitive() or other.isString() or other.isEnum()
|
|
if self.isDate():
|
|
return (other.isPrimitive() or other.isString() or other.isEnum() or
|
|
other.isInterface() or other.isCallback() or
|
|
other.isDictionary() or other.isSequence() or
|
|
other.isMozMap() or other.isArray())
|
|
if self.isVoid():
|
|
return not other.isVoid()
|
|
# Not much else we could be!
|
|
assert self.isSpiderMonkeyInterface()
|
|
# 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.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
|
|
(self.isArrayBuffer() and not other.isArrayBuffer()) or
|
|
# ArrayBufferView is distinguishable from everything
|
|
# that's not an ArrayBufferView or typed array.
|
|
(self.isArrayBufferView() and not other.isArrayBufferView() and
|
|
not other.isTypedArray()) or
|
|
# Typed arrays are distinguishable from everything
|
|
# except ArrayBufferView and the same type of typed
|
|
# array
|
|
(self.isTypedArray() and not other.isArrayBufferView() and not
|
|
(other.isTypedArray() and other.name == self.name)))))
|
|
|
|
def _getDependentObjects(self):
|
|
return set()
|
|
|
|
BuiltinTypes = {
|
|
IDLBuiltinType.Types.byte:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Byte",
|
|
IDLBuiltinType.Types.byte),
|
|
IDLBuiltinType.Types.octet:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Octet",
|
|
IDLBuiltinType.Types.octet),
|
|
IDLBuiltinType.Types.short:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Short",
|
|
IDLBuiltinType.Types.short),
|
|
IDLBuiltinType.Types.unsigned_short:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedShort",
|
|
IDLBuiltinType.Types.unsigned_short),
|
|
IDLBuiltinType.Types.long:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Long",
|
|
IDLBuiltinType.Types.long),
|
|
IDLBuiltinType.Types.unsigned_long:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedLong",
|
|
IDLBuiltinType.Types.unsigned_long),
|
|
IDLBuiltinType.Types.long_long:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "LongLong",
|
|
IDLBuiltinType.Types.long_long),
|
|
IDLBuiltinType.Types.unsigned_long_long:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "UnsignedLongLong",
|
|
IDLBuiltinType.Types.unsigned_long_long),
|
|
IDLBuiltinType.Types.boolean:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Boolean",
|
|
IDLBuiltinType.Types.boolean),
|
|
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),
|
|
IDLBuiltinType.Types.domstring:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "String",
|
|
IDLBuiltinType.Types.domstring),
|
|
IDLBuiltinType.Types.bytestring:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "ByteString",
|
|
IDLBuiltinType.Types.bytestring),
|
|
IDLBuiltinType.Types.scalarvaluestring:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "ScalarValueString",
|
|
IDLBuiltinType.Types.scalarvaluestring),
|
|
IDLBuiltinType.Types.object:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Object",
|
|
IDLBuiltinType.Types.object),
|
|
IDLBuiltinType.Types.date:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Date",
|
|
IDLBuiltinType.Types.date),
|
|
IDLBuiltinType.Types.void:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Void",
|
|
IDLBuiltinType.Types.void),
|
|
IDLBuiltinType.Types.ArrayBuffer:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "ArrayBuffer",
|
|
IDLBuiltinType.Types.ArrayBuffer),
|
|
IDLBuiltinType.Types.ArrayBufferView:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "ArrayBufferView",
|
|
IDLBuiltinType.Types.ArrayBufferView),
|
|
IDLBuiltinType.Types.Int8Array:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int8Array",
|
|
IDLBuiltinType.Types.Int8Array),
|
|
IDLBuiltinType.Types.Uint8Array:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint8Array",
|
|
IDLBuiltinType.Types.Uint8Array),
|
|
IDLBuiltinType.Types.Uint8ClampedArray:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint8ClampedArray",
|
|
IDLBuiltinType.Types.Uint8ClampedArray),
|
|
IDLBuiltinType.Types.Int16Array:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int16Array",
|
|
IDLBuiltinType.Types.Int16Array),
|
|
IDLBuiltinType.Types.Uint16Array:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint16Array",
|
|
IDLBuiltinType.Types.Uint16Array),
|
|
IDLBuiltinType.Types.Int32Array:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int32Array",
|
|
IDLBuiltinType.Types.Int32Array),
|
|
IDLBuiltinType.Types.Uint32Array:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Uint32Array",
|
|
IDLBuiltinType.Types.Uint32Array),
|
|
IDLBuiltinType.Types.Float32Array:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float32Array",
|
|
IDLBuiltinType.Types.Float32Array),
|
|
IDLBuiltinType.Types.Float64Array:
|
|
IDLBuiltinType(BuiltinLocation("<builtin type>"), "Float64Array",
|
|
IDLBuiltinType.Types.Float64Array)
|
|
}
|
|
|
|
|
|
integerTypeSizes = {
|
|
IDLBuiltinType.Types.byte: (-128, 127),
|
|
IDLBuiltinType.Types.octet: (0, 255),
|
|
IDLBuiltinType.Types.short: (-32768, 32767),
|
|
IDLBuiltinType.Types.unsigned_short: (0, 65535),
|
|
IDLBuiltinType.Types.long: (-2147483648, 2147483647),
|
|
IDLBuiltinType.Types.unsigned_long: (0, 4294967295),
|
|
IDLBuiltinType.Types.long_long: (-9223372036854775808,
|
|
9223372036854775807),
|
|
IDLBuiltinType.Types.unsigned_long_long: (0, 18446744073709551615)
|
|
}
|
|
|
|
def matchIntegerValueToType(value):
|
|
for type, extremes in integerTypeSizes.items():
|
|
(min, max) = extremes
|
|
if value <= max and value >= min:
|
|
return BuiltinTypes[type]
|
|
|
|
return None
|
|
|
|
class IDLValue(IDLObject):
|
|
def __init__(self, location, type, value):
|
|
IDLObject.__init__(self, location)
|
|
self.type = type
|
|
assert isinstance(type, IDLType)
|
|
|
|
self.value = value
|
|
|
|
def coerceToType(self, type, location):
|
|
if type == self.type:
|
|
return self # Nothing to do
|
|
|
|
# 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)
|
|
|
|
elif self.type.isInteger() and type.isInteger():
|
|
# We're both integer types. See if we fit.
|
|
|
|
(min, max) = integerTypeSizes[type._typeTag]
|
|
if self.value <= max and self.value >= min:
|
|
# Promote
|
|
return IDLValue(self.location, type, self.value)
|
|
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
|
|
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, enum.identifier.name),
|
|
[location, enum.location])
|
|
return self
|
|
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
|
|
elif self.type.isString() and type.isScalarValueString():
|
|
# Allow ScalarValueStrings 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
|
|
# extra normalization step.
|
|
assert self.type.isDOMString()
|
|
return self
|
|
raise WebIDLError("Cannot coerce type %s to type %s." %
|
|
(self.type, type), [location])
|
|
|
|
def _getDependentObjects(self):
|
|
return set()
|
|
|
|
class IDLNullValue(IDLObject):
|
|
def __init__(self, location):
|
|
IDLObject.__init__(self, location)
|
|
self.type = None
|
|
self.value = None
|
|
|
|
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 IDLEmptySequenceValue(IDLObject):
|
|
def __init__(self, location):
|
|
IDLObject.__init__(self, location)
|
|
self.type = None
|
|
self.value = None
|
|
|
|
def coerceToType(self, type, location):
|
|
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:
|
|
return self.coerceToType(subtype, location)
|
|
except:
|
|
pass
|
|
|
|
if not type.isSequence():
|
|
raise WebIDLError("Cannot coerce empty sequence value to type %s." % type,
|
|
[location])
|
|
|
|
emptySequenceValue = IDLEmptySequenceValue(self.location)
|
|
emptySequenceValue.type = type
|
|
return emptySequenceValue
|
|
|
|
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):
|
|
|
|
Tags = enum(
|
|
'Const',
|
|
'Attr',
|
|
'Method'
|
|
)
|
|
|
|
Special = enum(
|
|
'Static',
|
|
'Stringifier'
|
|
)
|
|
|
|
def __init__(self, location, identifier, tag):
|
|
IDLObjectWithIdentifier.__init__(self, location, None, identifier)
|
|
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
|
|
|
|
def isAttr(self):
|
|
return self.tag == IDLInterfaceMember.Tags.Attr
|
|
|
|
def isConst(self):
|
|
return self.tag == IDLInterfaceMember.Tags.Const
|
|
|
|
def addExtendedAttributes(self, attrs):
|
|
for attr in attrs:
|
|
self.handleExtendedAttribute(attr)
|
|
attrlist = attr.listValue()
|
|
self._extendedAttrDict[attr.identifier()] = attrlist if len(attrlist) else True
|
|
|
|
def handleExtendedAttribute(self, attr):
|
|
pass
|
|
|
|
def getExtendedAttribute(self, name):
|
|
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
|
|
|
|
def validate(self):
|
|
if (self.getExtendedAttribute("Pref") and
|
|
self.exposureSet != set([self._scope.primaryGlobalName])):
|
|
raise WebIDLError("[Pref] used on an interface member that is not "
|
|
"%s-only" % self._scope.primaryGlobalName,
|
|
[self.location])
|
|
|
|
if (self.getExtendedAttribute("CheckPermissions") and
|
|
self.exposureSet != set([self._scope.primaryGlobalName])):
|
|
raise WebIDLError("[CheckPermissions] used on an interface member "
|
|
"that is not %s-only" %
|
|
self._scope.primaryGlobalName,
|
|
[self.location])
|
|
|
|
class IDLConst(IDLInterfaceMember):
|
|
def __init__(self, location, identifier, type, value):
|
|
IDLInterfaceMember.__init__(self, location, identifier,
|
|
IDLInterfaceMember.Tags.Const)
|
|
|
|
assert isinstance(type, IDLType)
|
|
if type.isDictionary():
|
|
raise WebIDLError("A constant cannot be of a dictionary type",
|
|
[self.location])
|
|
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)
|
|
|
|
def finish(self, scope):
|
|
IDLInterfaceMember.finish(self, scope)
|
|
|
|
if not self.type.isComplete():
|
|
type = self.type.complete(scope)
|
|
if not type.isPrimitive() and not type.isString():
|
|
locations = [self.type.location, type.location]
|
|
try:
|
|
locations.append(type.inner.location)
|
|
except:
|
|
pass
|
|
raise WebIDLError("Incorrect type for constant", locations)
|
|
self.type = type
|
|
|
|
# The value might not match the type
|
|
coercedValue = self.value.coerceToType(self.type, self.location)
|
|
assert coercedValue
|
|
|
|
self.value = coercedValue
|
|
|
|
def validate(self):
|
|
IDLInterfaceMember.validate(self)
|
|
|
|
def handleExtendedAttribute(self, attr):
|
|
identifier = attr.identifier()
|
|
if identifier == "Exposed":
|
|
convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
|
|
elif (identifier == "Pref" or
|
|
identifier == "ChromeOnly" or
|
|
identifier == "Func" or
|
|
identifier == "AvailableIn" or
|
|
identifier == "CheckPermissions"):
|
|
# Known attributes that we don't need to do anything with here
|
|
pass
|
|
else:
|
|
raise WebIDLError("Unknown extended attribute %s on constant" % identifier,
|
|
[attr.location])
|
|
IDLInterfaceMember.handleExtendedAttribute(self, attr)
|
|
|
|
def _getDependentObjects(self):
|
|
return set([self.type, self.value])
|
|
|
|
class IDLAttribute(IDLInterfaceMember):
|
|
def __init__(self, location, identifier, type, readonly, inherit=False,
|
|
static=False, stringifier=False):
|
|
IDLInterfaceMember.__init__(self, location, identifier,
|
|
IDLInterfaceMember.Tags.Attr)
|
|
|
|
assert isinstance(type, IDLType)
|
|
self.type = type
|
|
self.readonly = readonly
|
|
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'",
|
|
[self.location])
|
|
|
|
def isStatic(self):
|
|
return self.static
|
|
|
|
def __str__(self):
|
|
return "'%s' attribute '%s'" % (self.type, self.identifier)
|
|
|
|
def finish(self, scope):
|
|
IDLInterfaceMember.finish(self, scope)
|
|
|
|
if not self.type.isComplete():
|
|
t = self.type.complete(scope)
|
|
|
|
assert not isinstance(t, IDLUnresolvedType)
|
|
assert not isinstance(t, IDLTypedefType)
|
|
assert not isinstance(t.name, IDLUnresolvedIdentifier)
|
|
self.type = t
|
|
|
|
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() 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.unroll().flatMemberTypes:
|
|
if f.isDictionary():
|
|
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 dictionary "
|
|
"type", [self.location, f.location])
|
|
if f.isSequence():
|
|
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 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):
|
|
IDLInterfaceMember.validate(self)
|
|
|
|
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])
|
|
if not self.type.unroll().isExposedInAllOf(self.exposureSet):
|
|
raise WebIDLError("Attribute returns a type that is not exposed "
|
|
"everywhere where the attribute is exposed",
|
|
[self.location])
|
|
|
|
def handleExtendedAttribute(self, attr):
|
|
identifier = attr.identifier()
|
|
if identifier == "SetterThrows" and self.readonly:
|
|
raise WebIDLError("Readonly attributes must not be flagged as "
|
|
"[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 [StoreInSlot]",
|
|
[attr.location])
|
|
elif identifier == "LenientThis":
|
|
if not attr.noArguments():
|
|
raise WebIDLError("[LenientThis] must take no arguments",
|
|
[attr.location])
|
|
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 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 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",
|
|
[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() and identifier == "CrossOriginReadable":
|
|
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 == "Exposed":
|
|
convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
|
|
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"):
|
|
# 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):
|
|
assert isinstance(parentScope, IDLScope)
|
|
self.type.resolveType(parentScope)
|
|
IDLObjectWithIdentifier.resolve(self, parentScope)
|
|
|
|
def addExtendedAttributes(self, attrs):
|
|
attrs = self.checkForStringHandlingExtendedAttributes(attrs)
|
|
IDLInterfaceMember.addExtendedAttributes(self, attrs)
|
|
|
|
def hasLenientThis(self):
|
|
return self.lenientThis
|
|
|
|
def isUnforgeable(self):
|
|
return self._unforgeable
|
|
|
|
def _getDependentObjects(self):
|
|
return set([self.type])
|
|
|
|
class IDLArgument(IDLObjectWithIdentifier):
|
|
def __init__(self, location, identifier, type, optional=False, defaultValue=None, variadic=False, dictionaryMember=False):
|
|
IDLObjectWithIdentifier.__init__(self, location, None, identifier)
|
|
|
|
assert isinstance(type, IDLType)
|
|
self.type = type
|
|
|
|
self.optional = optional
|
|
self.defaultValue = defaultValue
|
|
self.variadic = variadic
|
|
self.dictionaryMember = dictionaryMember
|
|
self._isComplete = False
|
|
self.enforceRange = False
|
|
self.clamp = False
|
|
self._allowTreatNonCallableAsNull = False
|
|
|
|
assert not variadic or optional
|
|
|
|
def addExtendedAttributes(self, attrs):
|
|
attrs = self.checkForStringHandlingExtendedAttributes(
|
|
attrs,
|
|
isDictionaryMember=self.dictionaryMember,
|
|
isOptional=self.optional)
|
|
for attribute in attrs:
|
|
identifier = attribute.identifier()
|
|
if identifier == "Clamp":
|
|
if not attribute.noArguments():
|
|
raise WebIDLError("[Clamp] must take no arguments",
|
|
[attribute.location])
|
|
if self.enforceRange:
|
|
raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
|
|
[self.location]);
|
|
self.clamp = True
|
|
elif identifier == "EnforceRange":
|
|
if not attribute.noArguments():
|
|
raise WebIDLError("[EnforceRange] must take no arguments",
|
|
[attribute.location])
|
|
if self.clamp:
|
|
raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive",
|
|
[self.location]);
|
|
self.enforceRange = True
|
|
elif identifier == "TreatNonCallableAsNull":
|
|
self._allowTreatNonCallableAsNull = True
|
|
else:
|
|
raise WebIDLError("Unhandled extended attribute on an argument",
|
|
[attribute.location])
|
|
|
|
def isComplete(self):
|
|
return self._isComplete
|
|
|
|
def complete(self, scope):
|
|
if self._isComplete:
|
|
return
|
|
|
|
self._isComplete = True
|
|
|
|
if not self.type.isComplete():
|
|
type = self.type.complete(scope)
|
|
assert not isinstance(type, IDLUnresolvedType)
|
|
assert not isinstance(type, IDLTypedefType)
|
|
assert not isinstance(type.name, IDLUnresolvedIdentifier)
|
|
self.type = type
|
|
|
|
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.
|
|
if self.defaultValue:
|
|
self.defaultValue = self.defaultValue.coerceToType(self.type,
|
|
self.location)
|
|
assert self.defaultValue
|
|
|
|
def allowTreatNonCallableAsNull(self):
|
|
return self._allowTreatNonCallableAsNull
|
|
|
|
def _getDependentObjects(self):
|
|
deps = set([self.type])
|
|
if self.defaultValue:
|
|
deps.add(self.defaultValue)
|
|
return deps
|
|
|
|
class IDLCallbackType(IDLType, IDLObjectWithScope):
|
|
def __init__(self, location, parentScope, identifier, returnType, arguments):
|
|
assert isinstance(returnType, IDLType)
|
|
|
|
IDLType.__init__(self, location, identifier.name)
|
|
|
|
self._returnType = returnType
|
|
# Clone the list
|
|
self._arguments = list(arguments)
|
|
|
|
IDLObjectWithScope.__init__(self, location, parentScope, identifier)
|
|
|
|
for (returnType, arguments) in self.signatures():
|
|
for argument in arguments:
|
|
argument.resolve(self)
|
|
|
|
self._treatNonCallableAsNull = False
|
|
self._treatNonObjectAsNull = False
|
|
|
|
def isCallback(self):
|
|
return True
|
|
|
|
def signatures(self):
|
|
return [(self._returnType, self._arguments)]
|
|
|
|
def tag(self):
|
|
return IDLType.Tags.callback
|
|
|
|
def finish(self, scope):
|
|
if not self._returnType.isComplete():
|
|
type = self._returnType.complete(scope)
|
|
|
|
assert not isinstance(type, IDLUnresolvedType)
|
|
assert not isinstance(type, IDLTypedefType)
|
|
assert not isinstance(type.name, IDLUnresolvedIdentifier)
|
|
self._returnType = type
|
|
|
|
for argument in self._arguments:
|
|
if argument.type.isComplete():
|
|
continue
|
|
|
|
type = argument.type.complete(scope)
|
|
|
|
assert not isinstance(type, IDLUnresolvedType)
|
|
assert not isinstance(type, IDLTypedefType)
|
|
assert not isinstance(type.name, IDLUnresolvedIdentifier)
|
|
argument.type = type
|
|
|
|
def validate(self):
|
|
pass
|
|
|
|
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.isNonCallbackInterface() or other.isDate())
|
|
|
|
def addExtendedAttributes(self, attrs):
|
|
unhandledAttrs = []
|
|
for attr in attrs:
|
|
if attr.identifier() == "TreatNonCallableAsNull":
|
|
self._treatNonCallableAsNull = True
|
|
elif attr.identifier() == "TreatNonObjectAsNull":
|
|
self._treatNonObjectAsNull = True
|
|
else:
|
|
unhandledAttrs.append(attr)
|
|
if self._treatNonCallableAsNull and self._treatNonObjectAsNull:
|
|
raise WebIDLError("Cannot specify both [TreatNonCallableAsNull] "
|
|
"and [TreatNonObjectAsNull]", [self.location])
|
|
if len(unhandledAttrs) != 0:
|
|
IDLType.addExtendedAttributes(self, unhandledAttrs)
|
|
|
|
def _getDependentObjects(self):
|
|
return set([self._returnType] + self._arguments)
|
|
|
|
class IDLMethodOverload:
|
|
"""
|
|
A class that represents a single overload of a WebIDL method. This is not
|
|
quite the same as an element of the "effective overload set" in the spec,
|
|
because separate IDLMethodOverloads are not created based on arguments being
|
|
optional. Rather, when multiple methods have the same name, there is an
|
|
IDLMethodOverload for each one, all hanging off an IDLMethod representing
|
|
the full set of overloads.
|
|
"""
|
|
def __init__(self, returnType, arguments, location):
|
|
self.returnType = returnType
|
|
# Clone the list of arguments, just in case
|
|
self.arguments = list(arguments)
|
|
self.location = location
|
|
|
|
def _getDependentObjects(self):
|
|
deps = set(self.arguments)
|
|
deps.add(self.returnType)
|
|
return deps
|
|
|
|
class IDLMethod(IDLInterfaceMember, IDLScope):
|
|
|
|
Special = enum(
|
|
'Getter',
|
|
'Setter',
|
|
'Creator',
|
|
'Deleter',
|
|
'LegacyCaller',
|
|
base=IDLInterfaceMember.Special
|
|
)
|
|
|
|
TypeSuffixModifier = enum(
|
|
'None',
|
|
'QMark',
|
|
'Brackets'
|
|
)
|
|
|
|
NamedOrIndexed = enum(
|
|
'Neither',
|
|
'Named',
|
|
'Indexed'
|
|
)
|
|
|
|
def __init__(self, location, identifier, returnType, arguments,
|
|
static=False, getter=False, setter=False, creator=False,
|
|
deleter=False, specialType=NamedOrIndexed.Neither,
|
|
legacycaller=False, stringifier=False, jsonifier=False):
|
|
# REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
|
|
IDLInterfaceMember.__init__(self, location, identifier,
|
|
IDLInterfaceMember.Tags.Method)
|
|
|
|
self._hasOverloads = False
|
|
|
|
assert isinstance(returnType, IDLType)
|
|
|
|
# self._overloads is a list of IDLMethodOverloads
|
|
self._overloads = [IDLMethodOverload(returnType, arguments, location)]
|
|
|
|
assert isinstance(static, bool)
|
|
self._static = static
|
|
assert isinstance(getter, bool)
|
|
self._getter = getter
|
|
assert isinstance(setter, bool)
|
|
self._setter = setter
|
|
assert isinstance(creator, bool)
|
|
self._creator = creator
|
|
assert isinstance(deleter, bool)
|
|
self._deleter = deleter
|
|
assert isinstance(legacycaller, bool)
|
|
self._legacycaller = legacycaller
|
|
assert isinstance(stringifier, bool)
|
|
self._stringifier = stringifier
|
|
assert isinstance(jsonifier, bool)
|
|
self._jsonifier = jsonifier
|
|
self._specialType = specialType
|
|
self._unforgeable = False
|
|
|
|
if static and identifier.name == "prototype":
|
|
raise WebIDLError("The identifier of a static operation must not be 'prototype'",
|
|
[location])
|
|
|
|
self.assertSignatureConstraints()
|
|
|
|
def __str__(self):
|
|
return "Method '%s'" % self.identifier
|
|
|
|
def assertSignatureConstraints(self):
|
|
if self._getter or self._deleter:
|
|
assert len(self._overloads) == 1
|
|
overload = self._overloads[0]
|
|
arguments = overload.arguments
|
|
assert len(arguments) == 1
|
|
assert arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] or \
|
|
arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]
|
|
assert not arguments[0].optional and not arguments[0].variadic
|
|
assert not self._getter or not overload.returnType.isVoid()
|
|
|
|
if self._setter or self._creator:
|
|
assert len(self._overloads) == 1
|
|
arguments = self._overloads[0].arguments
|
|
assert len(arguments) == 2
|
|
assert arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.domstring] or \
|
|
arguments[0].type == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]
|
|
assert not arguments[0].optional and not arguments[0].variadic
|
|
assert not arguments[1].optional and not arguments[1].variadic
|
|
|
|
if self._stringifier:
|
|
assert len(self._overloads) == 1
|
|
overload = self._overloads[0]
|
|
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
|
|
|
|
def isGetter(self):
|
|
return self._getter
|
|
|
|
def isSetter(self):
|
|
return self._setter
|
|
|
|
def isCreator(self):
|
|
return self._creator
|
|
|
|
def isDeleter(self):
|
|
return self._deleter
|
|
|
|
def isNamed(self):
|
|
assert self._specialType == IDLMethod.NamedOrIndexed.Named or \
|
|
self._specialType == IDLMethod.NamedOrIndexed.Indexed
|
|
return self._specialType == IDLMethod.NamedOrIndexed.Named
|
|
|
|
def isIndexed(self):
|
|
assert self._specialType == IDLMethod.NamedOrIndexed.Named or \
|
|
self._specialType == IDLMethod.NamedOrIndexed.Indexed
|
|
return self._specialType == IDLMethod.NamedOrIndexed.Indexed
|
|
|
|
def isLegacycaller(self):
|
|
return self._legacycaller
|
|
|
|
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] == "__" and self.identifier.name != "__noSuchMethod__"
|
|
|
|
def resolve(self, parentScope):
|
|
assert isinstance(parentScope, IDLScope)
|
|
IDLObjectWithIdentifier.resolve(self, parentScope)
|
|
IDLScope.__init__(self, self.location, parentScope, self.identifier)
|
|
for (returnType, arguments) in self.signatures():
|
|
for argument in arguments:
|
|
argument.resolve(self)
|
|
|
|
def addOverload(self, method):
|
|
assert len(method._overloads) == 1
|
|
|
|
if self._extendedAttrDict != method ._extendedAttrDict:
|
|
raise WebIDLError("Extended attributes differ on different "
|
|
"overloads of %s" % method.identifier,
|
|
[self.location, method.location])
|
|
|
|
self._overloads.extend(method._overloads)
|
|
|
|
self._hasOverloads = True
|
|
|
|
if self.isStatic() != method.isStatic():
|
|
raise WebIDLError("Overloaded identifier %s appears with different values of the 'static' attribute" % method.identifier,
|
|
[method.location])
|
|
|
|
if self.isLegacycaller() != method.isLegacycaller():
|
|
raise WebIDLError("Overloaded identifier %s appears with different values of the 'legacycaller' attribute" % method.identifier,
|
|
[method.location])
|
|
|
|
# Can't overload special things!
|
|
assert not self.isGetter()
|
|
assert not method.isGetter()
|
|
assert not self.isSetter()
|
|
assert not method.isSetter()
|
|
assert not self.isCreator()
|
|
assert not method.isCreator()
|
|
assert not self.isDeleter()
|
|
assert not method.isDeleter()
|
|
assert not self.isStringifier()
|
|
assert not method.isStringifier()
|
|
assert not self.isJsonifier()
|
|
assert not method.isJsonifier()
|
|
|
|
return self
|
|
|
|
def signatures(self):
|
|
return [(overload.returnType, overload.arguments) for overload in
|
|
self._overloads]
|
|
|
|
def finish(self, scope):
|
|
IDLInterfaceMember.finish(self, scope)
|
|
|
|
overloadWithPromiseReturnType = None
|
|
overloadWithoutPromiseReturnType = None
|
|
for overload in self._overloads:
|
|
variadicArgument = None
|
|
|
|
arguments = overload.arguments
|
|
for (idx, argument) in enumerate(arguments):
|
|
if not argument.isComplete():
|
|
argument.complete(scope)
|
|
assert argument.type.isComplete()
|
|
|
|
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
|
|
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])
|
|
if argument.variadic:
|
|
variadicArgument = argument
|
|
|
|
returnType = overload.returnType
|
|
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
|
|
|
|
# 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])
|
|
|
|
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.
|
|
self.maxArgCount = max(len(s[1]) for s in self.signatures())
|
|
self.allowedArgCounts = [ i for i in range(self.maxArgCount+1)
|
|
if len(self.signaturesForArgCount(i)) != 0 ]
|
|
|
|
def validate(self):
|
|
IDLInterfaceMember.validate(self)
|
|
|
|
# Make sure our overloads are properly distinguishable and don't have
|
|
# different argument types before the distinguishing args.
|
|
for argCount in self.allowedArgCounts:
|
|
possibleOverloads = self.overloadsForArgCount(argCount)
|
|
if len(possibleOverloads) == 1:
|
|
continue
|
|
distinguishingIndex = self.distinguishingIndexForArgCount(argCount)
|
|
for idx in range(distinguishingIndex):
|
|
firstSigType = possibleOverloads[0].arguments[idx].type
|
|
for overload in possibleOverloads[1:]:
|
|
if overload.arguments[idx].type != firstSigType:
|
|
raise WebIDLError(
|
|
"Signatures for method '%s' with %d arguments have "
|
|
"different types of arguments at index %d, which "
|
|
"is before distinguishing index %d" %
|
|
(self.identifier.name, argCount, idx,
|
|
distinguishingIndex),
|
|
[self.location, overload.location])
|
|
|
|
for overload in self._overloads:
|
|
if not overload.returnType.unroll().isExposedInAllOf(self.exposureSet):
|
|
raise WebIDLError("Overload returns a type that is not exposed "
|
|
"everywhere where the method is exposed",
|
|
[overload.location])
|
|
|
|
def overloadsForArgCount(self, argc):
|
|
return [overload for overload in self._overloads if
|
|
len(overload.arguments) == argc or
|
|
(len(overload.arguments) > argc and
|
|
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.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:]:
|
|
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
|
|
signatures = self.signaturesForArgCount(argc)
|
|
for idx in range(argc):
|
|
if isValidDistinguishingIndex(idx, signatures):
|
|
return idx
|
|
# No valid distinguishing index. Time to throw
|
|
locations = self.locationsForArgCount(argc)
|
|
raise WebIDLError("Signatures with %d arguments for method '%s' are not "
|
|
"distinguishable" % (argc, self.identifier.name),
|
|
locations)
|
|
|
|
def handleExtendedAttribute(self, attr):
|
|
identifier = attr.identifier()
|
|
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 "
|
|
"[SetterThrows]",
|
|
[attr.location, self.location])
|
|
elif identifier == "Unforgeable":
|
|
if self.isStatic():
|
|
raise WebIDLError("[Unforgeable] is only allowed on non-static "
|
|
"methods", [attr.location, self.location])
|
|
self._unforgeable = True
|
|
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 == "Exposed":
|
|
convertExposedAttrToGlobalNameSet(attr, self._exposureGlobalNames)
|
|
elif (identifier == "Pure" or
|
|
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 == "Throws" or
|
|
identifier == "NewObject" or
|
|
identifier == "ChromeOnly" or
|
|
identifier == "UnsafeInPrerendering" or
|
|
identifier == "Pref" or
|
|
identifier == "Func" or
|
|
identifier == "AvailableIn" 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 isUnforgeable(self):
|
|
return self._unforgeable
|
|
|
|
def _getDependentObjects(self):
|
|
deps = set()
|
|
for overload in self._overloads:
|
|
deps.union(overload._getDependentObjects())
|
|
return deps
|
|
|
|
class IDLImplementsStatement(IDLObject):
|
|
def __init__(self, location, implementor, implementee):
|
|
IDLObject.__init__(self, location)
|
|
self.implementor = implementor;
|
|
self.implementee = implementee
|
|
|
|
def finish(self, scope):
|
|
assert(isinstance(self.implementor, IDLIdentifierPlaceholder))
|
|
assert(isinstance(self.implementee, IDLIdentifierPlaceholder))
|
|
implementor = self.implementor.finish(scope)
|
|
implementee = self.implementee.finish(scope)
|
|
# NOTE: we depend on not setting self.implementor and
|
|
# self.implementee here to keep track of the original
|
|
# locations.
|
|
if not isinstance(implementor, IDLInterface):
|
|
raise WebIDLError("Left-hand side of 'implements' is not an "
|
|
"interface",
|
|
[self.implementor.location])
|
|
if implementor.isCallback():
|
|
raise WebIDLError("Left-hand side of 'implements' is a callback "
|
|
"interface",
|
|
[self.implementor.location])
|
|
if not isinstance(implementee, IDLInterface):
|
|
raise WebIDLError("Right-hand side of 'implements' is not an "
|
|
"interface",
|
|
[self.implementee.location])
|
|
if implementee.isCallback():
|
|
raise WebIDLError("Right-hand side of 'implements' is a callback "
|
|
"interface",
|
|
[self.implementee.location])
|
|
implementor.addImplementedInterface(implementee)
|
|
|
|
def validate(self):
|
|
pass
|
|
|
|
def addExtendedAttributes(self, attrs):
|
|
assert len(attrs) == 0
|
|
|
|
class IDLExtendedAttribute(IDLObject):
|
|
"""
|
|
A class to represent IDL extended attributes so we can give them locations
|
|
"""
|
|
def __init__(self, location, tuple):
|
|
IDLObject.__init__(self, location)
|
|
self._tuple = tuple
|
|
|
|
def identifier(self):
|
|
return self._tuple[0]
|
|
|
|
def noArguments(self):
|
|
return len(self._tuple) == 1
|
|
|
|
def hasValue(self):
|
|
return len(self._tuple) >= 2 and isinstance(self._tuple[1], str)
|
|
|
|
def value(self):
|
|
assert(self.hasValue())
|
|
return self._tuple[1]
|
|
|
|
def hasArgs(self):
|
|
return (len(self._tuple) == 2 and isinstance(self._tuple[1], list) or
|
|
len(self._tuple) == 3)
|
|
|
|
def args(self):
|
|
assert(self.hasArgs())
|
|
# Our args are our last element
|
|
return self._tuple[-1]
|
|
|
|
def listValue(self):
|
|
"""
|
|
Backdoor for storing random data in _extendedAttrDict
|
|
"""
|
|
return list(self._tuple)[1:]
|
|
|
|
# Parser
|
|
|
|
class Tokenizer(object):
|
|
tokens = [
|
|
"INTEGER",
|
|
"FLOATLITERAL",
|
|
"IDENTIFIER",
|
|
"STRING",
|
|
"WHITESPACE",
|
|
"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:
|
|
# Can't use int(), because that doesn't handle octal properly.
|
|
t.value = parseInt(t.value)
|
|
except:
|
|
raise WebIDLError("Invalid integer literal",
|
|
[Location(lexer=self.lexer,
|
|
lineno=self.lexer.lineno,
|
|
lexpos=self.lexer.lexpos,
|
|
filename=self._filename)])
|
|
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')
|
|
return t
|
|
|
|
def t_STRING(self, t):
|
|
r'"[^"]*"'
|
|
t.value = t.value[1:-1]
|
|
return t
|
|
|
|
def t_WHITESPACE(self, t):
|
|
r'[\t\n\r ]+|[\t\n\r ]*((//[^\n]*|/\*.*?\*/)[\t\n\r ]*)+'
|
|
pass
|
|
|
|
def t_ELLIPSIS(self, t):
|
|
r'\.\.\.'
|
|
t.type = self.keywords.get(t.value)
|
|
return t
|
|
|
|
def t_OTHER(self, t):
|
|
r'[^\t\n\r 0-9A-Z_a-z]'
|
|
t.type = self.keywords.get(t.value, 'OTHER')
|
|
return t
|
|
|
|
keywords = {
|
|
"module": "MODULE",
|
|
"interface": "INTERFACE",
|
|
"partial": "PARTIAL",
|
|
"dictionary": "DICTIONARY",
|
|
"exception": "EXCEPTION",
|
|
"enum": "ENUM",
|
|
"callback": "CALLBACK",
|
|
"typedef": "TYPEDEF",
|
|
"implements": "IMPLEMENTS",
|
|
"const": "CONST",
|
|
"null": "NULL",
|
|
"true": "TRUE",
|
|
"false": "FALSE",
|
|
"serializer": "SERIALIZER",
|
|
"stringifier": "STRINGIFIER",
|
|
"jsonifier": "JSONIFIER",
|
|
"unrestricted": "UNRESTRICTED",
|
|
"attribute": "ATTRIBUTE",
|
|
"readonly": "READONLY",
|
|
"inherit": "INHERIT",
|
|
"static": "STATIC",
|
|
"getter": "GETTER",
|
|
"setter": "SETTER",
|
|
"creator": "CREATOR",
|
|
"deleter": "DELETER",
|
|
"legacycaller": "LEGACYCALLER",
|
|
"optional": "OPTIONAL",
|
|
"...": "ELLIPSIS",
|
|
"::": "SCOPE",
|
|
"Date": "DATE",
|
|
"DOMString": "DOMSTRING",
|
|
"ByteString": "BYTESTRING",
|
|
"ScalarValueString": "SCALARVALUESTRING",
|
|
"any": "ANY",
|
|
"boolean": "BOOLEAN",
|
|
"byte": "BYTE",
|
|
"double": "DOUBLE",
|
|
"float": "FLOAT",
|
|
"long": "LONG",
|
|
"object": "OBJECT",
|
|
"octet": "OCTET",
|
|
"optional": "OPTIONAL",
|
|
"Promise": "PROMISE",
|
|
"sequence": "SEQUENCE",
|
|
"MozMap": "MOZMAP",
|
|
"short": "SHORT",
|
|
"unsigned": "UNSIGNED",
|
|
"void": "VOID",
|
|
":": "COLON",
|
|
";": "SEMICOLON",
|
|
"{": "LBRACE",
|
|
"}": "RBRACE",
|
|
"(": "LPAREN",
|
|
")": "RPAREN",
|
|
"[": "LBRACKET",
|
|
"]": "RBRACKET",
|
|
"?": "QUESTIONMARK",
|
|
",": "COMMA",
|
|
"=": "EQUALS",
|
|
"<": "LT",
|
|
">": "GT",
|
|
"ArrayBuffer": "ARRAYBUFFER",
|
|
"or": "OR"
|
|
}
|
|
|
|
tokens.extend(keywords.values())
|
|
|
|
def t_error(self, t):
|
|
raise WebIDLError("Unrecognized Input",
|
|
[Location(lexer=self.lexer,
|
|
lineno=self.lexer.lineno,
|
|
lexpos=self.lexer.lexpos,
|
|
filename = self.filename)])
|
|
|
|
def __init__(self, outputdir, lexer=None):
|
|
if lexer:
|
|
self.lexer = lexer
|
|
else:
|
|
self.lexer = lex.lex(object=self,
|
|
outputdir=outputdir,
|
|
lextab='webidllex',
|
|
reflags=re.DOTALL)
|
|
|
|
class SqueakyCleanLogger(object):
|
|
errorWhitelist = [
|
|
# Web IDL defines the WHITESPACE token, but doesn't actually
|
|
# use it ... so far.
|
|
"Token 'WHITESPACE' defined, but not used",
|
|
# And that means we have an unused token
|
|
"There is 1 unused token",
|
|
# Web IDL defines a OtherOrComma rule that's only used in
|
|
# ExtendedAttributeInner, which we don't use yet.
|
|
"Rule 'OtherOrComma' defined, but not used",
|
|
# And an unused rule
|
|
"There is 1 unused rule",
|
|
# And the OtherOrComma grammar symbol is unreachable.
|
|
"Symbol 'OtherOrComma' is unreachable",
|
|
# Which means the Other symbol is unreachable.
|
|
"Symbol 'Other' is unreachable",
|
|
]
|
|
def __init__(self):
|
|
self.errors = []
|
|
def debug(self, msg, *args, **kwargs):
|
|
pass
|
|
info = debug
|
|
def warning(self, msg, *args, **kwargs):
|
|
if msg == "%s:%d: Rule '%s' defined, but not used":
|
|
# Munge things so we don't have to hardcode filenames and
|
|
# line numbers in our whitelist.
|
|
whitelistmsg = "Rule '%s' defined, but not used"
|
|
whitelistargs = args[2:]
|
|
else:
|
|
whitelistmsg = msg
|
|
whitelistargs = args
|
|
if (whitelistmsg % whitelistargs) not in SqueakyCleanLogger.errorWhitelist:
|
|
self.errors.append(msg % args)
|
|
error = warning
|
|
|
|
def reportGrammarErrors(self):
|
|
if self.errors:
|
|
raise WebIDLError("\n".join(self.errors), [])
|
|
|
|
class Parser(Tokenizer):
|
|
def getLocation(self, p, i):
|
|
return Location(self.lexer, p.lineno(i), p.lexpos(i), self._filename)
|
|
|
|
def globalScope(self):
|
|
return self._globalScope
|
|
|
|
# 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]:
|
|
p[0] = [p[2]]
|
|
p[2].addExtendedAttributes(p[1])
|
|
else:
|
|
assert not p[1]
|
|
p[0] = []
|
|
|
|
p[0].extend(p[3])
|
|
|
|
def p_DefinitionsEmpty(self, p):
|
|
"""
|
|
Definitions :
|
|
"""
|
|
p[0] = []
|
|
|
|
def p_Definition(self, p):
|
|
"""
|
|
Definition : CallbackOrInterface
|
|
| PartialInterface
|
|
| Dictionary
|
|
| Exception
|
|
| Enum
|
|
| Typedef
|
|
| ImplementsStatement
|
|
"""
|
|
p[0] = p[1]
|
|
assert p[1] # We might not have implemented something ...
|
|
|
|
def p_CallbackOrInterfaceCallback(self, p):
|
|
"""
|
|
CallbackOrInterface : CALLBACK CallbackRestOrInterface
|
|
"""
|
|
if p[2].isInterface():
|
|
assert isinstance(p[2], IDLInterface)
|
|
p[2].setCallback(True)
|
|
|
|
p[0] = p[2]
|
|
|
|
def p_CallbackOrInterfaceInterface(self, p):
|
|
"""
|
|
CallbackOrInterface : Interface
|
|
"""
|
|
p[0] = p[1]
|
|
|
|
def p_CallbackRestOrInterface(self, p):
|
|
"""
|
|
CallbackRestOrInterface : CallbackRest
|
|
| Interface
|
|
"""
|
|
assert p[1]
|
|
p[0] = p[1]
|
|
|
|
def p_Interface(self, p):
|
|
"""
|
|
Interface : INTERFACE IDENTIFIER Inheritance LBRACE InterfaceMembers RBRACE SEMICOLON
|
|
"""
|
|
location = self.getLocation(p, 1)
|
|
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
|
|
members = p[5]
|
|
parent = p[3]
|
|
|
|
try:
|
|
existingObj = self.globalScope()._lookupIdentifier(identifier)
|
|
if existingObj:
|
|
p[0] = existingObj
|
|
if not isinstance(p[0], IDLInterface):
|
|
raise WebIDLError("Interface has the same name as "
|
|
"non-interface object",
|
|
[location, p[0].location])
|
|
p[0].setNonPartial(location, parent, members)
|
|
return
|
|
except Exception, ex:
|
|
if isinstance(ex, WebIDLError):
|
|
raise ex
|
|
pass
|
|
|
|
p[0] = IDLInterface(location, self.globalScope(), identifier, parent,
|
|
members, isKnownNonPartial=True)
|
|
|
|
def p_InterfaceForwardDecl(self, p):
|
|
"""
|
|
Interface : INTERFACE IDENTIFIER SEMICOLON
|
|
"""
|
|
location = self.getLocation(p, 1)
|
|
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
|
|
|
|
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 Exception, ex:
|
|
if isinstance(ex, WebIDLError):
|
|
raise ex
|
|
pass
|
|
|
|
p[0] = IDLExternalInterface(location, self.globalScope(), identifier)
|
|
|
|
def p_PartialInterface(self, p):
|
|
"""
|
|
PartialInterface : PARTIAL INTERFACE IDENTIFIER LBRACE InterfaceMembers RBRACE SEMICOLON
|
|
"""
|
|
location = self.getLocation(p, 2)
|
|
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
|
|
members = p[5]
|
|
|
|
nonPartialInterface = None
|
|
try:
|
|
nonPartialInterface = self.globalScope()._lookupIdentifier(identifier)
|
|
if nonPartialInterface:
|
|
if not isinstance(nonPartialInterface, IDLInterface):
|
|
raise WebIDLError("Partial interface has the same name as "
|
|
"non-interface object",
|
|
[location, nonPartialInterface.location])
|
|
except Exception, ex:
|
|
if isinstance(ex, WebIDLError):
|
|
raise ex
|
|
pass
|
|
|
|
if not nonPartialInterface:
|
|
nonPartialInterface = IDLInterface(location, self.globalScope(),
|
|
identifier, None,
|
|
[], isKnownNonPartial=False)
|
|
partialInterface = IDLPartialInterface(location, identifier, members,
|
|
nonPartialInterface)
|
|
p[0] = partialInterface
|
|
|
|
def p_Inheritance(self, p):
|
|
"""
|
|
Inheritance : COLON ScopedName
|
|
"""
|
|
p[0] = IDLIdentifierPlaceholder(self.getLocation(p, 2), p[2])
|
|
|
|
def p_InheritanceEmpty(self, p):
|
|
"""
|
|
Inheritance :
|
|
"""
|
|
pass
|
|
|
|
def p_InterfaceMembers(self, p):
|
|
"""
|
|
InterfaceMembers : ExtendedAttributeList InterfaceMember InterfaceMembers
|
|
"""
|
|
p[0] = [p[2]] if p[2] else []
|
|
|
|
assert not p[1] or p[2]
|
|
p[2].addExtendedAttributes(p[1])
|
|
|
|
p[0].extend(p[3])
|
|
|
|
def p_InterfaceMembersEmpty(self, p):
|
|
"""
|
|
InterfaceMembers :
|
|
"""
|
|
p[0] = []
|
|
|
|
def p_InterfaceMember(self, p):
|
|
"""
|
|
InterfaceMember : Const
|
|
| AttributeOrOperation
|
|
"""
|
|
p[0] = p[1]
|
|
|
|
def p_Dictionary(self, p):
|
|
"""
|
|
Dictionary : DICTIONARY IDENTIFIER Inheritance LBRACE DictionaryMembers RBRACE SEMICOLON
|
|
"""
|
|
location = self.getLocation(p, 1)
|
|
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
|
|
members = p[5]
|
|
p[0] = IDLDictionary(location, self.globalScope(), identifier, p[3], members)
|
|
|
|
def p_DictionaryMembers(self, p):
|
|
"""
|
|
DictionaryMembers : ExtendedAttributeList DictionaryMember DictionaryMembers
|
|
|
|
|
"""
|
|
if len(p) == 1:
|
|
# We're at the end of the list
|
|
p[0] = []
|
|
return
|
|
# Add our extended attributes
|
|
p[2].addExtendedAttributes(p[1])
|
|
p[0] = [p[2]]
|
|
p[0].extend(p[3])
|
|
|
|
def p_DictionaryMember(self, p):
|
|
"""
|
|
DictionaryMember : Type IDENTIFIER Default SEMICOLON
|
|
"""
|
|
# These quack a lot like optional arguments, so just treat them that way.
|
|
t = p[1]
|
|
assert isinstance(t, IDLType)
|
|
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
|
|
defaultValue = p[3]
|
|
|
|
p[0] = IDLArgument(self.getLocation(p, 2), identifier, t, optional=True,
|
|
defaultValue=defaultValue, variadic=False,
|
|
dictionaryMember=True)
|
|
|
|
def p_Default(self, p):
|
|
"""
|
|
Default : EQUALS DefaultValue
|
|
|
|
|
"""
|
|
if len(p) > 1:
|
|
p[0] = p[2]
|
|
else:
|
|
p[0] = None
|
|
|
|
def p_DefaultValue(self, p):
|
|
"""
|
|
DefaultValue : ConstValue
|
|
| LBRACKET RBRACKET
|
|
"""
|
|
if len(p) == 2:
|
|
p[0] = p[1]
|
|
else:
|
|
assert len(p) == 3 # Must be []
|
|
p[0] = IDLEmptySequenceValue(self.getLocation(p, 1))
|
|
|
|
def p_Exception(self, p):
|
|
"""
|
|
Exception : EXCEPTION IDENTIFIER Inheritance LBRACE ExceptionMembers RBRACE SEMICOLON
|
|
"""
|
|
pass
|
|
|
|
def p_Enum(self, p):
|
|
"""
|
|
Enum : ENUM IDENTIFIER LBRACE EnumValueList RBRACE SEMICOLON
|
|
"""
|
|
location = self.getLocation(p, 1)
|
|
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 2), p[2])
|
|
|
|
values = p[4]
|
|
assert values
|
|
p[0] = IDLEnum(location, self.globalScope(), identifier, values)
|
|
|
|
def p_EnumValueList(self, p):
|
|
"""
|
|
EnumValueList : STRING EnumValueListComma
|
|
"""
|
|
p[0] = [p[1]]
|
|
p[0].extend(p[2])
|
|
|
|
def p_EnumValueListComma(self, p):
|
|
"""
|
|
EnumValueListComma : COMMA EnumValueListString
|
|
"""
|
|
p[0] = p[2]
|
|
|
|
def p_EnumValueListCommaEmpty(self, p):
|
|
"""
|
|
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] = []
|
|
|
|
def p_CallbackRest(self, p):
|
|
"""
|
|
CallbackRest : IDENTIFIER EQUALS ReturnType LPAREN ArgumentList RPAREN SEMICOLON
|
|
"""
|
|
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
|
|
p[0] = IDLCallbackType(self.getLocation(p, 1), self.globalScope(),
|
|
identifier, p[3], p[5])
|
|
|
|
def p_ExceptionMembers(self, p):
|
|
"""
|
|
ExceptionMembers : ExtendedAttributeList ExceptionMember ExceptionMembers
|
|
|
|
|
"""
|
|
pass
|
|
|
|
def p_Typedef(self, p):
|
|
"""
|
|
Typedef : TYPEDEF Type IDENTIFIER SEMICOLON
|
|
"""
|
|
typedef = IDLTypedefType(self.getLocation(p, 1), p[2], p[3])
|
|
typedef.resolve(self.globalScope())
|
|
p[0] = typedef
|
|
|
|
def p_ImplementsStatement(self, p):
|
|
"""
|
|
ImplementsStatement : ScopedName IMPLEMENTS ScopedName SEMICOLON
|
|
"""
|
|
assert(p[2] == "implements")
|
|
implementor = IDLIdentifierPlaceholder(self.getLocation(p, 1), p[1])
|
|
implementee = IDLIdentifierPlaceholder(self.getLocation(p, 3), p[3])
|
|
p[0] = IDLImplementsStatement(self.getLocation(p, 1), implementor,
|
|
implementee)
|
|
|
|
def p_Const(self, p):
|
|
"""
|
|
Const : CONST ConstType IDENTIFIER EQUALS ConstValue SEMICOLON
|
|
"""
|
|
location = self.getLocation(p, 1)
|
|
type = p[2]
|
|
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 3), p[3])
|
|
value = p[5]
|
|
p[0] = IDLConst(location, identifier, type, value)
|
|
|
|
def p_ConstValueBoolean(self, p):
|
|
"""
|
|
ConstValue : BooleanLiteral
|
|
"""
|
|
location = self.getLocation(p, 1)
|
|
booleanType = BuiltinTypes[IDLBuiltinType.Types.boolean]
|
|
p[0] = IDLValue(location, booleanType, p[1])
|
|
|
|
def p_ConstValueInteger(self, p):
|
|
"""
|
|
ConstValue : INTEGER
|
|
"""
|
|
location = self.getLocation(p, 1)
|
|
|
|
# We don't know ahead of time what type the integer literal is.
|
|
# Determine the smallest type it could possibly fit in and use that.
|
|
integerType = matchIntegerValueToType(p[1])
|
|
if integerType == None:
|
|
raise WebIDLError("Integer literal out of range", [location])
|
|
|
|
p[0] = IDLValue(location, integerType, p[1])
|
|
|
|
def p_ConstValueFloat(self, p):
|
|
"""
|
|
ConstValue : FLOATLITERAL
|
|
"""
|
|
location = self.getLocation(p, 1)
|
|
p[0] = IDLValue(location, BuiltinTypes[IDLBuiltinType.Types.unrestricted_float], p[1])
|
|
|
|
def p_ConstValueString(self, p):
|
|
"""
|
|
ConstValue : STRING
|
|
"""
|
|
location = self.getLocation(p, 1)
|
|
stringType = BuiltinTypes[IDLBuiltinType.Types.domstring]
|
|
p[0] = IDLValue(location, stringType, p[1])
|
|
|
|
def p_ConstValueNull(self, p):
|
|
"""
|
|
ConstValue : NULL
|
|
"""
|
|
p[0] = IDLNullValue(self.getLocation(p, 1))
|
|
|
|
def p_BooleanLiteralTrue(self, p):
|
|
"""
|
|
BooleanLiteral : TRUE
|
|
"""
|
|
p[0] = True
|
|
|
|
def p_BooleanLiteralFalse(self, p):
|
|
"""
|
|
BooleanLiteral : FALSE
|
|
"""
|
|
p[0] = False
|
|
|
|
def p_AttributeOrOperation(self, p):
|
|
"""
|
|
AttributeOrOperation : Attribute
|
|
| Operation
|
|
"""
|
|
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 AttributeRest
|
|
"""
|
|
(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):
|
|
"""
|
|
ReadOnly : READONLY
|
|
"""
|
|
p[0] = True
|
|
|
|
def p_ReadOnlyEmpty(self, p):
|
|
"""
|
|
ReadOnly :
|
|
"""
|
|
p[0] = False
|
|
|
|
def p_Inherit(self, p):
|
|
"""
|
|
Inherit : INHERIT
|
|
"""
|
|
p[0] = True
|
|
|
|
def p_InheritEmpty(self, p):
|
|
"""
|
|
Inherit :
|
|
"""
|
|
p[0] = False
|
|
|
|
def p_Operation(self, p):
|
|
"""
|
|
Operation : Qualifiers OperationRest
|
|
"""
|
|
qualifiers = p[1]
|
|
|
|
# Disallow duplicates in the qualifier set
|
|
if not len(set(qualifiers)) == len(qualifiers):
|
|
raise WebIDLError("Duplicate qualifiers are not allowed",
|
|
[self.getLocation(p, 1)])
|
|
|
|
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
|
|
|
|
if getter or deleter:
|
|
if setter or creator:
|
|
raise WebIDLError("getter and deleter are incompatible with setter and creator",
|
|
[self.getLocation(p, 1)])
|
|
|
|
(returnType, identifier, arguments) = p[2]
|
|
|
|
assert isinstance(returnType, IDLType)
|
|
|
|
specialType = IDLMethod.NamedOrIndexed.Neither
|
|
|
|
if getter or deleter:
|
|
if len(arguments) != 1:
|
|
raise WebIDLError("%s has wrong number of arguments" %
|
|
("getter" if getter else "deleter"),
|
|
[self.getLocation(p, 2)])
|
|
argType = arguments[0].type
|
|
if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
|
|
specialType = IDLMethod.NamedOrIndexed.Named
|
|
elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
|
|
specialType = IDLMethod.NamedOrIndexed.Indexed
|
|
else:
|
|
raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" %
|
|
("getter" if getter else "deleter"),
|
|
[arguments[0].location])
|
|
if arguments[0].optional or arguments[0].variadic:
|
|
raise WebIDLError("%s cannot have %s argument" %
|
|
("getter" if getter else "deleter",
|
|
"optional" if arguments[0].optional else "variadic"),
|
|
[arguments[0].location])
|
|
if getter:
|
|
if returnType.isVoid():
|
|
raise WebIDLError("getter cannot have void return type",
|
|
[self.getLocation(p, 2)])
|
|
if setter or creator:
|
|
if len(arguments) != 2:
|
|
raise WebIDLError("%s has wrong number of arguments" %
|
|
("setter" if setter else "creator"),
|
|
[self.getLocation(p, 2)])
|
|
argType = arguments[0].type
|
|
if argType == BuiltinTypes[IDLBuiltinType.Types.domstring]:
|
|
specialType = IDLMethod.NamedOrIndexed.Named
|
|
elif argType == BuiltinTypes[IDLBuiltinType.Types.unsigned_long]:
|
|
specialType = IDLMethod.NamedOrIndexed.Indexed
|
|
else:
|
|
raise WebIDLError("%s has wrong argument type (must be DOMString or UnsignedLong)" %
|
|
("setter" if setter else "creator"),
|
|
[arguments[0].location])
|
|
if arguments[0].optional or arguments[0].variadic:
|
|
raise WebIDLError("%s cannot have %s argument" %
|
|
("setter" if setter else "creator",
|
|
"optional" if arguments[0].optional else "variadic"),
|
|
[arguments[0].location])
|
|
if arguments[1].optional or arguments[1].variadic:
|
|
raise WebIDLError("%s cannot have %s argument" %
|
|
("setter" if setter else "creator",
|
|
"optional" if arguments[1].optional else "variadic"),
|
|
[arguments[1].location])
|
|
|
|
if stringifier:
|
|
if len(arguments) != 0:
|
|
raise WebIDLError("stringifier has wrong number of arguments",
|
|
[self.getLocation(p, 2)])
|
|
if not returnType.isDOMString():
|
|
raise WebIDLError("stringifier must have DOMString return type",
|
|
[self.getLocation(p, 2)])
|
|
|
|
# identifier might be None. This is only permitted for special methods.
|
|
if not identifier:
|
|
if not getter and not setter and not creator and \
|
|
not deleter and not legacycaller and not stringifier:
|
|
raise WebIDLError("Identifier required for non-special methods",
|
|
[self.getLocation(p, 2)])
|
|
|
|
location = BuiltinLocation("<auto-generated-identifier>")
|
|
identifier = IDLUnresolvedIdentifier(location, "__%s%s%s%s%s%s%s" %
|
|
("named" if specialType == IDLMethod.NamedOrIndexed.Named else \
|
|
"indexed" if specialType == IDLMethod.NamedOrIndexed.Indexed else "",
|
|
"getter" if getter else "",
|
|
"setter" if setter else "",
|
|
"deleter" if deleter else "",
|
|
"creator" if creator else "",
|
|
"legacycaller" if legacycaller else "",
|
|
"stringifier" if stringifier else ""), allowDoubleUnderscore=True)
|
|
|
|
method = IDLMethod(self.getLocation(p, 2), identifier, returnType, arguments,
|
|
static=static, getter=getter, setter=setter, creator=creator,
|
|
deleter=deleter, specialType=specialType,
|
|
legacycaller=legacycaller, stringifier=stringifier)
|
|
p[0] = method
|
|
|
|
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):
|
|
"""
|
|
Qualifier : STATIC
|
|
"""
|
|
p[0] = [IDLInterfaceMember.Special.Static]
|
|
|
|
def p_QualifierStringifier(self, p):
|
|
"""
|
|
Qualifier : STRINGIFIER
|
|
"""
|
|
p[0] = [IDLInterfaceMember.Special.Stringifier]
|
|
|
|
def p_Qualifiers(self, p):
|
|
"""
|
|
Qualifiers : Qualifier
|
|
| Specials
|
|
"""
|
|
p[0] = p[1]
|
|
|
|
def p_Specials(self, p):
|
|
"""
|
|
Specials : Special Specials
|
|
"""
|
|
p[0] = [p[1]]
|
|
p[0].extend(p[2])
|
|
|
|
def p_SpecialsEmpty(self, p):
|
|
"""
|
|
Specials :
|
|
"""
|
|
p[0] = []
|
|
|
|
def p_SpecialGetter(self, p):
|
|
"""
|
|
Special : GETTER
|
|
"""
|
|
p[0] = IDLMethod.Special.Getter
|
|
|
|
def p_SpecialSetter(self, p):
|
|
"""
|
|
Special : SETTER
|
|
"""
|
|
p[0] = IDLMethod.Special.Setter
|
|
|
|
def p_SpecialCreator(self, p):
|
|
"""
|
|
Special : CREATOR
|
|
"""
|
|
p[0] = IDLMethod.Special.Creator
|
|
|
|
def p_SpecialDeleter(self, p):
|
|
"""
|
|
Special : DELETER
|
|
"""
|
|
p[0] = IDLMethod.Special.Deleter
|
|
|
|
def p_SpecialLegacyCaller(self, p):
|
|
"""
|
|
Special : LEGACYCALLER
|
|
"""
|
|
p[0] = IDLMethod.Special.LegacyCaller
|
|
|
|
def p_OperationRest(self, p):
|
|
"""
|
|
OperationRest : ReturnType OptionalIdentifier LPAREN ArgumentList RPAREN SEMICOLON
|
|
"""
|
|
p[0] = (p[1], p[2], p[4])
|
|
|
|
def p_OptionalIdentifier(self, p):
|
|
"""
|
|
OptionalIdentifier : IDENTIFIER
|
|
"""
|
|
p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
|
|
|
|
def p_OptionalIdentifierEmpty(self, p):
|
|
"""
|
|
OptionalIdentifier :
|
|
"""
|
|
pass
|
|
|
|
def p_ArgumentList(self, p):
|
|
"""
|
|
ArgumentList : Argument Arguments
|
|
"""
|
|
p[0] = [p[1]] if p[1] else []
|
|
p[0].extend(p[2])
|
|
|
|
def p_ArgumentListEmpty(self, p):
|
|
"""
|
|
ArgumentList :
|
|
"""
|
|
p[0] = []
|
|
|
|
def p_Arguments(self, p):
|
|
"""
|
|
Arguments : COMMA Argument Arguments
|
|
"""
|
|
p[0] = [p[2]] if p[2] else []
|
|
p[0].extend(p[3])
|
|
|
|
def p_ArgumentsEmpty(self, p):
|
|
"""
|
|
Arguments :
|
|
"""
|
|
p[0] = []
|
|
|
|
def p_Argument(self, p):
|
|
"""
|
|
Argument : ExtendedAttributeList Optional Type Ellipsis ArgumentName Default
|
|
"""
|
|
t = p[3]
|
|
assert isinstance(t, IDLType)
|
|
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 5), p[5])
|
|
|
|
optional = p[2]
|
|
variadic = p[4]
|
|
defaultValue = p[6]
|
|
|
|
if not optional and defaultValue:
|
|
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.",
|
|
[self.getLocation(p, 2)])
|
|
optional = variadic
|
|
|
|
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
|
|
"""
|
|
p[0] = True
|
|
|
|
def p_OptionalEmpty(self, p):
|
|
"""
|
|
Optional :
|
|
"""
|
|
p[0] = False
|
|
|
|
def p_Ellipsis(self, p):
|
|
"""
|
|
Ellipsis : ELLIPSIS
|
|
"""
|
|
p[0] = True
|
|
|
|
def p_EllipsisEmpty(self, p):
|
|
"""
|
|
Ellipsis :
|
|
"""
|
|
p[0] = False
|
|
|
|
def p_ExceptionMember(self, p):
|
|
"""
|
|
ExceptionMember : Const
|
|
| ExceptionField
|
|
"""
|
|
pass
|
|
|
|
def p_ExceptionField(self, p):
|
|
"""
|
|
ExceptionField : Type IDENTIFIER SEMICOLON
|
|
"""
|
|
pass
|
|
|
|
def p_ExtendedAttributeList(self, p):
|
|
"""
|
|
ExtendedAttributeList : LBRACKET ExtendedAttribute ExtendedAttributes RBRACKET
|
|
"""
|
|
p[0] = [p[2]]
|
|
if p[3]:
|
|
p[0].extend(p[3])
|
|
|
|
def p_ExtendedAttributeListEmpty(self, p):
|
|
"""
|
|
ExtendedAttributeList :
|
|
"""
|
|
p[0] = []
|
|
|
|
def p_ExtendedAttribute(self, p):
|
|
"""
|
|
ExtendedAttribute : ExtendedAttributeNoArgs
|
|
| ExtendedAttributeArgList
|
|
| ExtendedAttributeIdent
|
|
| ExtendedAttributeNamedArgList
|
|
| ExtendedAttributeIdentList
|
|
"""
|
|
p[0] = IDLExtendedAttribute(self.getLocation(p, 1), p[1])
|
|
|
|
def p_ExtendedAttributeEmpty(self, p):
|
|
"""
|
|
ExtendedAttribute :
|
|
"""
|
|
pass
|
|
|
|
def p_ExtendedAttributes(self, p):
|
|
"""
|
|
ExtendedAttributes : COMMA ExtendedAttribute ExtendedAttributes
|
|
"""
|
|
p[0] = [p[2]] if p[2] else []
|
|
p[0].extend(p[3])
|
|
|
|
def p_ExtendedAttributesEmpty(self, p):
|
|
"""
|
|
ExtendedAttributes :
|
|
"""
|
|
p[0] = []
|
|
|
|
def p_Other(self, p):
|
|
"""
|
|
Other : INTEGER
|
|
| FLOATLITERAL
|
|
| IDENTIFIER
|
|
| STRING
|
|
| OTHER
|
|
| ELLIPSIS
|
|
| COLON
|
|
| SCOPE
|
|
| SEMICOLON
|
|
| LT
|
|
| EQUALS
|
|
| GT
|
|
| QUESTIONMARK
|
|
| DATE
|
|
| DOMSTRING
|
|
| BYTESTRING
|
|
| SCALARVALUESTRING
|
|
| ANY
|
|
| ATTRIBUTE
|
|
| BOOLEAN
|
|
| BYTE
|
|
| LEGACYCALLER
|
|
| CONST
|
|
| CREATOR
|
|
| DELETER
|
|
| DOUBLE
|
|
| EXCEPTION
|
|
| FALSE
|
|
| FLOAT
|
|
| GETTER
|
|
| IMPLEMENTS
|
|
| INHERIT
|
|
| INTERFACE
|
|
| LONG
|
|
| MODULE
|
|
| NULL
|
|
| OBJECT
|
|
| OCTET
|
|
| OPTIONAL
|
|
| SEQUENCE
|
|
| MOZMAP
|
|
| SETTER
|
|
| SHORT
|
|
| STATIC
|
|
| STRINGIFIER
|
|
| JSONIFIER
|
|
| TRUE
|
|
| TYPEDEF
|
|
| UNSIGNED
|
|
| VOID
|
|
"""
|
|
pass
|
|
|
|
def p_OtherOrComma(self, p):
|
|
"""
|
|
OtherOrComma : Other
|
|
| COMMA
|
|
"""
|
|
pass
|
|
|
|
def p_TypeSingleType(self, p):
|
|
"""
|
|
Type : SingleType
|
|
"""
|
|
p[0] = p[1]
|
|
|
|
def p_TypeUnionType(self, p):
|
|
"""
|
|
Type : UnionType TypeSuffix
|
|
"""
|
|
p[0] = self.handleModifiers(p[1], p[2])
|
|
|
|
def p_SingleTypeNonAnyType(self, p):
|
|
"""
|
|
SingleType : NonAnyType
|
|
"""
|
|
p[0] = p[1]
|
|
|
|
def p_SingleTypeAnyType(self, p):
|
|
"""
|
|
SingleType : ANY TypeSuffixStartingWithArray
|
|
"""
|
|
p[0] = self.handleModifiers(BuiltinTypes[IDLBuiltinType.Types.any], p[2])
|
|
|
|
def p_UnionType(self, p):
|
|
"""
|
|
UnionType : LPAREN UnionMemberType OR UnionMemberType UnionMemberTypes RPAREN
|
|
"""
|
|
types = [p[2], p[4]]
|
|
types.extend(p[5])
|
|
p[0] = IDLUnionType(self.getLocation(p, 1), types)
|
|
|
|
def p_UnionMemberTypeNonAnyType(self, p):
|
|
"""
|
|
UnionMemberType : NonAnyType
|
|
"""
|
|
p[0] = p[1]
|
|
|
|
def p_UnionMemberTypeArrayOfAny(self, p):
|
|
"""
|
|
UnionMemberTypeArrayOfAny : ANY LBRACKET RBRACKET
|
|
"""
|
|
p[0] = IDLArrayType(self.getLocation(p, 2),
|
|
BuiltinTypes[IDLBuiltinType.Types.any])
|
|
|
|
def p_UnionMemberType(self, p):
|
|
"""
|
|
UnionMemberType : UnionType TypeSuffix
|
|
| UnionMemberTypeArrayOfAny TypeSuffix
|
|
"""
|
|
p[0] = self.handleModifiers(p[1], p[2])
|
|
|
|
def p_UnionMemberTypes(self, p):
|
|
"""
|
|
UnionMemberTypes : OR UnionMemberType UnionMemberTypes
|
|
"""
|
|
p[0] = [p[2]]
|
|
p[0].extend(p[3])
|
|
|
|
def p_UnionMemberTypesEmpty(self, p):
|
|
"""
|
|
UnionMemberTypes :
|
|
"""
|
|
p[0] = []
|
|
|
|
def p_NonAnyType(self, p):
|
|
"""
|
|
NonAnyType : PrimitiveOrStringType TypeSuffix
|
|
| ARRAYBUFFER TypeSuffix
|
|
| OBJECT TypeSuffix
|
|
"""
|
|
if p[1] == "object":
|
|
type = BuiltinTypes[IDLBuiltinType.Types.object]
|
|
elif p[1] == "ArrayBuffer":
|
|
type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer]
|
|
else:
|
|
type = BuiltinTypes[p[1]]
|
|
|
|
p[0] = self.handleModifiers(type, p[2])
|
|
|
|
def p_NonAnyTypeSequenceType(self, p):
|
|
"""
|
|
NonAnyType : SEQUENCE LT Type GT Null
|
|
"""
|
|
innerType = p[3]
|
|
type = IDLSequenceType(self.getLocation(p, 1), innerType)
|
|
if p[5]:
|
|
type = IDLNullableType(self.getLocation(p, 5), type)
|
|
p[0] = type
|
|
|
|
# Note: Promise<void> is allowed, so we want to parametrize on
|
|
# ReturnType, not Type. Also, we want this to end up picking up
|
|
# the Promise interface for now, hence the games with IDLUnresolvedType.
|
|
def p_NonAnyTypePromiseType(self, p):
|
|
"""
|
|
NonAnyType : PROMISE LT ReturnType GT Null
|
|
"""
|
|
innerType = p[3]
|
|
promiseIdent = IDLUnresolvedIdentifier(self.getLocation(p, 1), "Promise")
|
|
type = IDLUnresolvedType(self.getLocation(p, 1), promiseIdent, p[3])
|
|
if p[5]:
|
|
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
|
|
"""
|
|
assert isinstance(p[1], IDLUnresolvedIdentifier)
|
|
|
|
if p[1].name == "Promise":
|
|
raise WebIDLError("Promise used without saying what it's "
|
|
"parametrized over",
|
|
[self.getLocation(p, 1)])
|
|
|
|
type = None
|
|
|
|
try:
|
|
if self.globalScope()._lookupIdentifier(p[1]):
|
|
obj = self.globalScope()._lookupIdentifier(p[1])
|
|
if obj.isType():
|
|
type = obj
|
|
else:
|
|
type = IDLWrapperType(self.getLocation(p, 1), p[1])
|
|
p[0] = self.handleModifiers(type, p[2])
|
|
return
|
|
except:
|
|
pass
|
|
|
|
type = IDLUnresolvedType(self.getLocation(p, 1), p[1])
|
|
p[0] = self.handleModifiers(type, p[2])
|
|
|
|
def p_NonAnyTypeDate(self, p):
|
|
"""
|
|
NonAnyType : DATE TypeSuffix
|
|
"""
|
|
p[0] = self.handleModifiers(BuiltinTypes[IDLBuiltinType.Types.date],
|
|
p[2])
|
|
|
|
def p_ConstType(self, p):
|
|
"""
|
|
ConstType : PrimitiveOrStringType Null
|
|
"""
|
|
type = BuiltinTypes[p[1]]
|
|
if p[2]:
|
|
type = IDLNullableType(self.getLocation(p, 1), type)
|
|
p[0] = type
|
|
|
|
def p_ConstTypeIdentifier(self, p):
|
|
"""
|
|
ConstType : IDENTIFIER Null
|
|
"""
|
|
identifier = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
|
|
|
|
type = IDLUnresolvedType(self.getLocation(p, 1), identifier)
|
|
if p[2]:
|
|
type = IDLNullableType(self.getLocation(p, 1), type)
|
|
p[0] = type
|
|
|
|
def p_PrimitiveOrStringTypeUint(self, p):
|
|
"""
|
|
PrimitiveOrStringType : UnsignedIntegerType
|
|
"""
|
|
p[0] = p[1]
|
|
|
|
def p_PrimitiveOrStringTypeBoolean(self, p):
|
|
"""
|
|
PrimitiveOrStringType : BOOLEAN
|
|
"""
|
|
p[0] = IDLBuiltinType.Types.boolean
|
|
|
|
def p_PrimitiveOrStringTypeByte(self, p):
|
|
"""
|
|
PrimitiveOrStringType : BYTE
|
|
"""
|
|
p[0] = IDLBuiltinType.Types.byte
|
|
|
|
def p_PrimitiveOrStringTypeOctet(self, p):
|
|
"""
|
|
PrimitiveOrStringType : OCTET
|
|
"""
|
|
p[0] = IDLBuiltinType.Types.octet
|
|
|
|
def p_PrimitiveOrStringTypeFloat(self, p):
|
|
"""
|
|
PrimitiveOrStringType : FLOAT
|
|
"""
|
|
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
|
|
"""
|
|
p[0] = IDLBuiltinType.Types.domstring
|
|
|
|
def p_PrimitiveOrStringTypeBytestring(self, p):
|
|
"""
|
|
PrimitiveOrStringType : BYTESTRING
|
|
"""
|
|
p[0] = IDLBuiltinType.Types.bytestring
|
|
|
|
def p_PrimitiveOrStringTypeScalarValueString(self, p):
|
|
"""
|
|
PrimitiveOrStringType : SCALARVALUESTRING
|
|
"""
|
|
p[0] = IDLBuiltinType.Types.scalarvaluestring
|
|
|
|
def p_UnsignedIntegerTypeUnsigned(self, p):
|
|
"""
|
|
UnsignedIntegerType : UNSIGNED IntegerType
|
|
"""
|
|
p[0] = p[2] + 1 # Adding one to a given signed integer type
|
|
# gets you the unsigned type.
|
|
|
|
def p_UnsignedIntegerType(self, p):
|
|
"""
|
|
UnsignedIntegerType : IntegerType
|
|
"""
|
|
p[0] = p[1]
|
|
|
|
def p_IntegerTypeShort(self, p):
|
|
"""
|
|
IntegerType : SHORT
|
|
"""
|
|
p[0] = IDLBuiltinType.Types.short
|
|
|
|
def p_IntegerTypeLong(self, p):
|
|
"""
|
|
IntegerType : LONG OptionalLong
|
|
"""
|
|
if p[2]:
|
|
p[0] = IDLBuiltinType.Types.long_long
|
|
else:
|
|
p[0] = IDLBuiltinType.Types.long
|
|
|
|
def p_OptionalLong(self, p):
|
|
"""
|
|
OptionalLong : LONG
|
|
"""
|
|
p[0] = True
|
|
|
|
def p_OptionalLongEmpty(self, p):
|
|
"""
|
|
OptionalLong :
|
|
"""
|
|
p[0] = False
|
|
|
|
def p_TypeSuffixBrackets(self, p):
|
|
"""
|
|
TypeSuffix : LBRACKET RBRACKET TypeSuffix
|
|
"""
|
|
p[0] = [(IDLMethod.TypeSuffixModifier.Brackets, self.getLocation(p, 1))]
|
|
p[0].extend(p[3])
|
|
|
|
def p_TypeSuffixQMark(self, p):
|
|
"""
|
|
TypeSuffix : QUESTIONMARK TypeSuffixStartingWithArray
|
|
"""
|
|
p[0] = [(IDLMethod.TypeSuffixModifier.QMark, self.getLocation(p, 1))]
|
|
p[0].extend(p[2])
|
|
|
|
def p_TypeSuffixEmpty(self, p):
|
|
"""
|
|
TypeSuffix :
|
|
"""
|
|
p[0] = []
|
|
|
|
def p_TypeSuffixStartingWithArray(self, p):
|
|
"""
|
|
TypeSuffixStartingWithArray : LBRACKET RBRACKET TypeSuffix
|
|
"""
|
|
p[0] = [(IDLMethod.TypeSuffixModifier.Brackets, self.getLocation(p, 1))]
|
|
p[0].extend(p[3])
|
|
|
|
def p_TypeSuffixStartingWithArrayEmpty(self, p):
|
|
"""
|
|
TypeSuffixStartingWithArray :
|
|
"""
|
|
p[0] = []
|
|
|
|
def p_Null(self, p):
|
|
"""
|
|
Null : QUESTIONMARK
|
|
|
|
|
"""
|
|
if len(p) > 1:
|
|
p[0] = True
|
|
else:
|
|
p[0] = False
|
|
|
|
def p_ReturnTypeType(self, p):
|
|
"""
|
|
ReturnType : Type
|
|
"""
|
|
p[0] = p[1]
|
|
|
|
def p_ReturnTypeVoid(self, p):
|
|
"""
|
|
ReturnType : VOID
|
|
"""
|
|
p[0] = BuiltinTypes[IDLBuiltinType.Types.void]
|
|
|
|
def p_ScopedName(self, p):
|
|
"""
|
|
ScopedName : AbsoluteScopedName
|
|
| RelativeScopedName
|
|
"""
|
|
p[0] = p[1]
|
|
|
|
def p_AbsoluteScopedName(self, p):
|
|
"""
|
|
AbsoluteScopedName : SCOPE IDENTIFIER ScopedNameParts
|
|
"""
|
|
assert False
|
|
pass
|
|
|
|
def p_RelativeScopedName(self, p):
|
|
"""
|
|
RelativeScopedName : IDENTIFIER ScopedNameParts
|
|
"""
|
|
assert not p[2] # Not implemented!
|
|
|
|
p[0] = IDLUnresolvedIdentifier(self.getLocation(p, 1), p[1])
|
|
|
|
def p_ScopedNameParts(self, p):
|
|
"""
|
|
ScopedNameParts : SCOPE IDENTIFIER ScopedNameParts
|
|
"""
|
|
assert False
|
|
pass
|
|
|
|
def p_ScopedNamePartsEmpty(self, p):
|
|
"""
|
|
ScopedNameParts :
|
|
"""
|
|
p[0] = None
|
|
|
|
def p_ExtendedAttributeNoArgs(self, p):
|
|
"""
|
|
ExtendedAttributeNoArgs : IDENTIFIER
|
|
"""
|
|
p[0] = (p[1],)
|
|
|
|
def p_ExtendedAttributeArgList(self, p):
|
|
"""
|
|
ExtendedAttributeArgList : IDENTIFIER LPAREN ArgumentList RPAREN
|
|
"""
|
|
p[0] = (p[1], p[3])
|
|
|
|
def p_ExtendedAttributeIdent(self, p):
|
|
"""
|
|
ExtendedAttributeIdent : IDENTIFIER EQUALS STRING
|
|
| IDENTIFIER EQUALS IDENTIFIER
|
|
"""
|
|
p[0] = (p[1], p[3])
|
|
|
|
def p_ExtendedAttributeNamedArgList(self, p):
|
|
"""
|
|
ExtendedAttributeNamedArgList : IDENTIFIER EQUALS IDENTIFIER LPAREN ArgumentList RPAREN
|
|
"""
|
|
p[0] = (p[1], p[3], p[5])
|
|
|
|
def p_ExtendedAttributeIdentList(self, p):
|
|
"""
|
|
ExtendedAttributeIdentList : IDENTIFIER EQUALS LPAREN IdentifierList RPAREN
|
|
"""
|
|
p[0] = (p[1], p[4])
|
|
|
|
def p_IdentifierList(self, p):
|
|
"""
|
|
IdentifierList : IDENTIFIER Identifiers
|
|
"""
|
|
idents = list(p[2])
|
|
idents.insert(0, p[1])
|
|
p[0] = idents
|
|
|
|
def p_IdentifiersList(self, p):
|
|
"""
|
|
Identifiers : COMMA IDENTIFIER Identifiers
|
|
"""
|
|
idents = list(p[3])
|
|
idents.insert(0, p[2])
|
|
p[0] = idents
|
|
|
|
def p_IdentifiersEmpty(self, p):
|
|
"""
|
|
Identifiers :
|
|
"""
|
|
p[0] = []
|
|
|
|
def p_error(self, p):
|
|
if not p:
|
|
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)])
|
|
|
|
def __init__(self, outputdir='', lexer=None):
|
|
Tokenizer.__init__(self, outputdir, lexer)
|
|
|
|
logger = SqueakyCleanLogger()
|
|
self.parser = yacc.yacc(module=self,
|
|
outputdir=outputdir,
|
|
tabmodule='webidlyacc',
|
|
errorlog=logger
|
|
# Pickling the grammar is a speedup in
|
|
# some cases (older Python?) but a
|
|
# significant slowdown in others.
|
|
# We're not pickling for now, until it
|
|
# becomes a speedup again.
|
|
# , picklefile='WebIDLGrammar.pkl'
|
|
)
|
|
logger.reportGrammarErrors()
|
|
|
|
self._globalScope = IDLScope(BuiltinLocation("<Global Scope>"), None, None)
|
|
# To make our test harness work, pretend like we have a primary global already. Note that we _don't_ set _globalScope.primaryGlobalAttr, so we'll still be able to detect multiple PrimaryGlobal extended attributes.
|
|
self._globalScope.primaryGlobalName = "FakeTestPrimaryGlobal"
|
|
self._globalScope.globalNames.add("FakeTestPrimaryGlobal")
|
|
self._globalScope.globalNameMapping["FakeTestPrimaryGlobal"].add("FakeTestPrimaryGlobal")
|
|
self._installBuiltins(self._globalScope)
|
|
self._productions = []
|
|
|
|
self._filename = "<builtin>"
|
|
self.lexer.input(Parser._builtins)
|
|
self._filename = None
|
|
|
|
self.parser.parse(lexer=self.lexer,tracking=True)
|
|
|
|
def _installBuiltins(self, scope):
|
|
assert isinstance(scope, IDLScope)
|
|
|
|
# xrange omits the last value.
|
|
for x in xrange(IDLBuiltinType.Types.ArrayBuffer, IDLBuiltinType.Types.Float64Array + 1):
|
|
builtin = BuiltinTypes[x]
|
|
name = builtin.name
|
|
|
|
typedef = IDLTypedefType(BuiltinLocation("<builtin type>"), builtin, name)
|
|
typedef.resolve(scope)
|
|
|
|
@ staticmethod
|
|
def handleModifiers(type, modifiers):
|
|
for (modifier, modifierLocation) in modifiers:
|
|
assert modifier == IDLMethod.TypeSuffixModifier.QMark or \
|
|
modifier == IDLMethod.TypeSuffixModifier.Brackets
|
|
|
|
if modifier == IDLMethod.TypeSuffixModifier.QMark:
|
|
type = IDLNullableType(modifierLocation, type)
|
|
elif modifier == IDLMethod.TypeSuffixModifier.Brackets:
|
|
type = IDLArrayType(modifierLocation, type)
|
|
|
|
return type
|
|
|
|
def parse(self, t, filename=None):
|
|
self.lexer.input(t)
|
|
|
|
#for tok in iter(self.lexer.token, None):
|
|
# print tok
|
|
|
|
self._filename = filename
|
|
self._productions.extend(self.parser.parse(lexer=self.lexer,tracking=True))
|
|
self._filename = None
|
|
|
|
def finish(self):
|
|
# First, finish all the IDLImplementsStatements. In particular, we
|
|
# have to make sure we do those before we do the IDLInterfaces.
|
|
# XXX khuey hates this bit and wants to nuke it from orbit.
|
|
implementsStatements = [ p for p in self._productions if
|
|
isinstance(p, IDLImplementsStatement)]
|
|
otherStatements = [ p for p in self._productions if
|
|
not isinstance(p, IDLImplementsStatement)]
|
|
for production in implementsStatements:
|
|
production.finish(self.globalScope())
|
|
for production in otherStatements:
|
|
production.finish(self.globalScope())
|
|
|
|
# Do any post-finish validation we need to do
|
|
for production in self._productions:
|
|
production.validate()
|
|
|
|
# De-duplicate self._productions, without modifying its order.
|
|
seen = set()
|
|
result = []
|
|
for p in self._productions:
|
|
if p not in seen:
|
|
seen.add(p)
|
|
result.append(p)
|
|
return result
|
|
|
|
def reset(self):
|
|
return Parser(lexer=self.lexer)
|
|
|
|
# Builtin IDL defined by WebIDL
|
|
_builtins = """
|
|
typedef unsigned long long DOMTimeStamp;
|
|
"""
|
|
|
|
def main():
|
|
# Parse arguments.
|
|
from optparse import OptionParser
|
|
usageString = "usage: %prog [options] files"
|
|
o = OptionParser(usage=usageString)
|
|
o.add_option("--cachedir", dest='cachedir', default=None,
|
|
help="Directory in which to cache lex/parse tables.")
|
|
o.add_option("--verbose-errors", action='store_true', default=False,
|
|
help="When an error happens, display the Python traceback.")
|
|
(options, args) = o.parse_args()
|
|
|
|
if len(args) < 1:
|
|
o.error(usageString)
|
|
|
|
fileList = args
|
|
baseDir = os.getcwd()
|
|
|
|
# Parse the WebIDL.
|
|
parser = Parser(options.cachedir)
|
|
try:
|
|
for filename in fileList:
|
|
fullPath = os.path.normpath(os.path.join(baseDir, filename))
|
|
f = open(fullPath, 'rb')
|
|
lines = f.readlines()
|
|
f.close()
|
|
print fullPath
|
|
parser.parse(''.join(lines), fullPath)
|
|
parser.finish()
|
|
except WebIDLError, e:
|
|
if options.verbose_errors:
|
|
traceback.print_exc()
|
|
else:
|
|
print e
|
|
|
|
if __name__ == '__main__':
|
|
main()
|