diff options
author | Alon Zakai <alonzakai@gmail.com> | 2014-04-23 17:52:20 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2014-04-23 17:52:20 -0700 |
commit | 28ed391308c78e8cda92bfbe22da88ce9767cd25 (patch) | |
tree | 41fde9dc11818abd67b836b054f90dc47cffdd0e /third_party | |
parent | 55c1dfd2c1548ebfd023e4f7d1c2416a11a534f8 (diff) |
webidl binder
Diffstat (limited to 'third_party')
-rw-r--r-- | third_party/WebIDL.py | 5030 | ||||
-rw-r--r-- | third_party/__init__.py | 2 |
2 files changed, 5032 insertions, 0 deletions
diff --git a/third_party/WebIDL.py b/third_party/WebIDL.py new file mode 100644 index 00000000..867a7cbc --- /dev/null +++ b/third_party/WebIDL.py @@ -0,0 +1,5030 @@ +# from http://mxr.mozilla.org/mozilla-central/source/dom/bindings/parser/WebIDL.py +# rev 501baeb3a034 + +# 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 + +# 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 = {} + + 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), []) + + # 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 IDLInterface(IDLObjectWithScope): + def __init__(self, location, parentScope, name, parent, members, + isPartial): + assert isinstance(parentScope, IDLScope) + assert isinstance(name, IDLUnresolvedIdentifier) + assert not isPartial or not parent + + self.parent = None + self._callback = False + self._finished = False + self.members = [] + # 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._isPartial = True + # 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 + + IDLObjectWithScope.__init__(self, location, parentScope, name) + + if not isPartial: + self.setNonPartial(location, parent, members) + else: + # Just remember our members for now + self.members = 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 self._isPartial: + raise WebIDLError("Interface %s does not have a non-partial " + "declaration" % self.identifier.name, + [self.location]) + + 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] must not have anything inherit from them + if self.parent.getExtendedAttribute("Global"): + # Note: This is not a self.parent.isOnGlobalProtoChain() check + # because ancestors of a [Global] interface can have other + # descendants. + raise WebIDLError("[Global] interface has another interface " + "inheriting from it", + [self.location, self.parent.location]) + + # Callbacks must not inherit from non-callbacks or inherit from + # anything that has consequential interfaces. + # XXXbz Can non-callbacks inherit from callbacks? Spec issue pending. + # 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) + + 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) + 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) + + for member in self.members: + if (member.isAttr() and member.isUnforgeable() and + not hasattr(member, "originatingInterface")): + member.originatingInterface = self + + # Compute slot indices for our members before we pull in + # unforgeable members from our parent. + for member in self.members: + if (member.isAttr() and + (member.getExtendedAttribute("StoreInSlot") or + member.getExtendedAttribute("Cached"))): + member.slotIndex = self.totalMembersInSlots + self.totalMembersInSlots += 1 + if member.getExtendedAttribute("StoreInSlot"): + self._ownMembersInSlots += 1 + + if self.parent: + # Make sure we don't shadow any of the [Unforgeable] attributes on + # our ancestor interfaces. We don't have to worry about + # consequential interfaces here, because those have already been + # imported into the relevant .members lists. And we don't have to + # worry about anything other than our parent, because it has already + # imported its ancestors unforgeable attributes into its member + # list. + for unforgeableAttr in (attr for attr in self.parent.members if + attr.isAttr() and not attr.isStatic() and + attr.isUnforgeable()): + shadows = [ m for m in self.members if + (m.isAttr() or m.isMethod()) and + not m.isStatic() and + m.identifier.name == unforgeableAttr.identifier.name ] + if len(shadows) != 0: + locs = [unforgeableAttr.location] + [ s.location for s + in shadows ] + raise WebIDLError("Interface %s shadows [Unforgeable] " + "members of %s" % + (self.identifier.name, + ancestor.identifier.name), + locs) + # And now just stick it in our members, since we won't be + # inheriting this down the proto chain. If we really cared we + # could try to do something where we set up the unforgeable + # attributes of ancestor interfaces, with their corresponding + # getters, on our interface, but that gets pretty complicated + # and seems unnecessary. + self.members.append(unforgeableAttr) + + # Ensure that there's at most one of each {named,indexed} + # {getter,setter,creator,deleter}, 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): + for member in self.members: + member.validate() + + # Check that PutForwards refers to another attribute and that no + # cycles exist in forwarded assignments. + if member.isAttr(): + iface = self + attr = member + putForwards = attr.getExtendedAttribute("PutForwards") + if putForwards and self.isCallback(): + raise WebIDLError("[PutForwards] used on an attribute " + "on interface %s which is a callback " + "interface" % self.identifier.name, + [self.location, member.location]) + + while putForwards is not None: + forwardIface = attr.type.unroll().inner + fowardAttr = None + + for forwardedMember in forwardIface.members: + if (not forwardedMember.isAttr() or + forwardedMember.identifier.name != putForwards[0]): + continue + if forwardedMember == member: + raise WebIDLError("Cycle detected in forwarded " + "assignments for attribute %s on " + "%s" % + (member.identifier.name, self), + [member.location]) + fowardAttr = forwardedMember + break + + if fowardAttr is None: + raise WebIDLError("Attribute %s on %s forwards to " + "missing attribute %s" % + (attr.identifier.name, iface, putForwards), + [attr.location]) + + iface = forwardIface + attr = fowardAttr + putForwards = attr.getExtendedAttribute("PutForwards") + + + def isInterface(self): + return True + + 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 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): + self._extendedAttrDict = {} + 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 NamedCon |