diff options
Diffstat (limited to 'dom/bindings/Configuration.py')
-rw-r--r-- | dom/bindings/Configuration.py | 791 |
1 files changed, 791 insertions, 0 deletions
diff --git a/dom/bindings/Configuration.py b/dom/bindings/Configuration.py new file mode 100644 index 000000000..5c96580a1 --- /dev/null +++ b/dom/bindings/Configuration.py @@ -0,0 +1,791 @@ +# 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/. + +from WebIDL import IDLImplementsStatement +import os +from collections import defaultdict + +autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n" + + +class DescriptorProvider: + """ + A way of getting descriptors for interface names. Subclasses must + have a getDescriptor method callable with the interface name only. + """ + def __init__(self): + pass + + +class Configuration(DescriptorProvider): + """ + Represents global configuration state based on IDL parse data and + the configuration file. + """ + def __init__(self, filename, parseData, generatedEvents=[]): + DescriptorProvider.__init__(self) + + # Read the configuration file. + glbl = {} + execfile(filename, glbl) + config = glbl['DOMInterfaces'] + + # Build descriptors for all the interfaces we have in the parse data. + # This allows callers to specify a subset of interfaces by filtering + # |parseData|. + self.descriptors = [] + self.interfaces = {} + self.descriptorsByName = {} + self.optimizedOutDescriptorNames = set() + self.generatedEvents = generatedEvents + self.maxProtoChainLength = 0 + for thing in parseData: + if isinstance(thing, IDLImplementsStatement): + # Our build system doesn't support dep build involving + # addition/removal of "implements" statements that appear in a + # different .webidl file than their LHS interface. Make sure we + # don't have any of those. + # + # But whitelist a RHS that is LegacyQueryInterface, + # since people shouldn't be adding any of those. + if (thing.implementor.filename() != thing.filename() and + thing.implementee.identifier.name != "LegacyQueryInterface"): + raise TypeError( + "The binding build system doesn't really support " + "'implements' statements which don't appear in the " + "file in which the left-hand side of the statement is " + "defined. Don't do this unless your right-hand side " + "is LegacyQueryInterface.\n" + "%s\n" + "%s" % + (thing.location, thing.implementor.location)) + + assert not thing.isType() + + if not thing.isInterface() and not thing.isNamespace(): + continue + iface = thing + self.interfaces[iface.identifier.name] = iface + if iface.identifier.name not in config: + # Completely skip consequential interfaces with no descriptor + # if they have no interface object because chances are we + # don't need to do anything interesting with them. + if iface.isConsequential() and not iface.hasInterfaceObject(): + self.optimizedOutDescriptorNames.add(iface.identifier.name) + continue + entry = {} + else: + entry = config[iface.identifier.name] + assert not isinstance(entry, list) + desc = Descriptor(self, iface, entry) + self.descriptors.append(desc) + # Setting up descriptorsByName while iterating through interfaces + # means we can get the nativeType of iterable interfaces without + # having to do multiple loops. + assert desc.interface.identifier.name not in self.descriptorsByName + self.descriptorsByName[desc.interface.identifier.name] = desc + + # Keep the descriptor list sorted for determinism. + self.descriptors.sort(lambda x, y: cmp(x.name, y.name)) + + + self.descriptorsByFile = {} + for d in self.descriptors: + self.descriptorsByFile.setdefault(d.interface.filename(), + []).append(d) + + self.enums = [e for e in parseData if e.isEnum()] + + self.dictionaries = [d for d in parseData if d.isDictionary()] + self.callbacks = [c for c in parseData if + c.isCallback() and not c.isInterface()] + + # Dictionary mapping from a union type name to a set of filenames where + # union types with that name are used. + self.filenamesPerUnion = defaultdict(set) + + # Dictionary mapping from a filename to a list of types for + # the union types used in that file. If a union type is used + # in multiple files then it will be added to the list for the + # None key. Note that the list contains a type for every use + # of a union type, so there can be multiple entries with union + # types that have the same name. + self.unionsPerFilename = defaultdict(list) + + for (t, _) in getAllTypes(self.descriptors, self.dictionaries, self.callbacks): + while True: + if t.isMozMap(): + t = t.inner + elif t.unroll() != t: + t = t.unroll() + elif t.isPromise(): + t = t.promiseInnerType() + else: + break + if t.isUnion(): + filenamesForUnion = self.filenamesPerUnion[t.name] + if t.filename() not in filenamesForUnion: + # We have a to be a bit careful: some of our built-in + # typedefs are for unions, and those unions end up with + # "<unknown>" as the filename. If that happens, we don't + # want to try associating this union with one particular + # filename, since there isn't one to associate it with, + # really. + if t.filename() == "<unknown>": + uniqueFilenameForUnion = None + elif len(filenamesForUnion) == 0: + # This is the first file that we found a union with this + # name in, record the union as part of the file. + uniqueFilenameForUnion = t.filename() + else: + # We already found a file that contains a union with + # this name. + if len(filenamesForUnion) == 1: + # This is the first time we found a union with this + # name in another file. + for f in filenamesForUnion: + # Filter out unions with this name from the + # unions for the file where we previously found + # them. + unionsForFilename = self.unionsPerFilename[f] + unionsForFilename = filter(lambda u: u.name != t.name, + unionsForFilename) + if len(unionsForFilename) == 0: + del self.unionsPerFilename[f] + else: + self.unionsPerFilename[f] = unionsForFilename + # Unions with this name appear in multiple files, record + # the filename as None, so that we can detect that. + uniqueFilenameForUnion = None + self.unionsPerFilename[uniqueFilenameForUnion].append(t) + filenamesForUnion.add(t.filename()) + + def getInterface(self, ifname): + return self.interfaces[ifname] + + def getDescriptors(self, **filters): + """Gets the descriptors that match the given filters.""" + curr = self.descriptors + # Collect up our filters, because we may have a webIDLFile filter that + # we always want to apply first. + tofilter = [] + for key, val in filters.iteritems(): + if key == 'webIDLFile': + # Special-case this part to make it fast, since most of our + # getDescriptors calls are conditioned on a webIDLFile. We may + # not have this key, in which case we have no descriptors + # either. + curr = self.descriptorsByFile.get(val, []) + continue + elif key == 'hasInterfaceObject': + getter = lambda x: (not x.interface.isExternal() and + x.interface.hasInterfaceObject()) + elif key == 'hasInterfacePrototypeObject': + getter = lambda x: (not x.interface.isExternal() and + x.interface.hasInterfacePrototypeObject()) + elif key == 'hasInterfaceOrInterfacePrototypeObject': + getter = lambda x: x.hasInterfaceOrInterfacePrototypeObject() + elif key == 'isCallback': + getter = lambda x: x.interface.isCallback() + elif key == 'isExternal': + getter = lambda x: x.interface.isExternal() + elif key == 'isJSImplemented': + getter = lambda x: x.interface.isJSImplemented() + elif key == 'isNavigatorProperty': + getter = lambda x: x.interface.isNavigatorProperty() + elif key == 'isExposedInAnyWorker': + getter = lambda x: x.interface.isExposedInAnyWorker() + elif key == 'isExposedInWorkerDebugger': + getter = lambda x: x.interface.isExposedInWorkerDebugger() + elif key == 'isExposedInAnyWorklet': + getter = lambda x: x.interface.isExposedInAnyWorklet() + elif key == 'isExposedInSystemGlobals': + getter = lambda x: x.interface.isExposedInSystemGlobals() + elif key == 'isExposedInWindow': + getter = lambda x: x.interface.isExposedInWindow() + else: + # Have to watch out: just closing over "key" is not enough, + # since we're about to mutate its value + getter = (lambda attrName: lambda x: getattr(x, attrName))(key) + tofilter.append((getter, val)) + for f in tofilter: + curr = filter(lambda x: f[0](x) == f[1], curr) + return curr + + def getEnums(self, webIDLFile): + return filter(lambda e: e.filename() == webIDLFile, self.enums) + + def getDictionaries(self, webIDLFile): + return filter(lambda d: d.filename() == webIDLFile, self.dictionaries) + + def getCallbacks(self, webIDLFile): + return filter(lambda c: c.filename() == webIDLFile, self.callbacks) + + def getDescriptor(self, interfaceName): + """ + Gets the appropriate descriptor for the given interface name. + """ + # We may have optimized out this descriptor, but the chances of anyone + # asking about it are then slim. Put the check for that _after_ we've + # done our normal lookup. But that means we have to do our normal + # lookup in a way that will not throw if it fails. + d = self.descriptorsByName.get(interfaceName, None) + if d: + return d + + if interfaceName in self.optimizedOutDescriptorNames: + raise NoSuchDescriptorError( + "No descriptor for '%s', which is a mixin ([NoInterfaceObject] " + "and a consequential interface) without an explicit " + "Bindings.conf annotation." % interfaceName) + + raise NoSuchDescriptorError("For " + interfaceName + " found no matches") + + +class NoSuchDescriptorError(TypeError): + def __init__(self, str): + TypeError.__init__(self, str) + + +def methodReturnsJSObject(method): + assert method.isMethod() + if method.returnsPromise(): + return True + + for signature in method.signatures(): + returnType = signature[0] + if returnType.isObject() or returnType.isSpiderMonkeyInterface(): + return True + + return False + + +def MemberIsUnforgeable(member, descriptor): + # Note: "or" and "and" return either their LHS or RHS, not + # necessarily booleans. Make sure to return a boolean from this + # method, because callers will compare its return value to + # booleans. + return bool((member.isAttr() or member.isMethod()) and + not member.isStatic() and + (member.isUnforgeable() or + descriptor.interface.getExtendedAttribute("Unforgeable"))) + + +class Descriptor(DescriptorProvider): + """ + Represents a single descriptor for an interface. See Bindings.conf. + """ + def __init__(self, config, interface, desc): + DescriptorProvider.__init__(self) + self.config = config + self.interface = interface + + self.wantsXrays = (not interface.isExternal() and + interface.isExposedInWindow()) + + if self.wantsXrays: + # We could try to restrict self.wantsXrayExpandoClass further. For + # example, we could set it to false if all of our slots store + # Gecko-interface-typed things, because we don't use Xray expando + # slots for those. But note that we would need to check the types + # of not only the members of "interface" but also of all its + # ancestors, because those can have members living in our slots too. + # For now, do the simple thing. + self.wantsXrayExpandoClass = (interface.totalMembersInSlots != 0) + + # Read the desc, and fill in the relevant defaults. + ifaceName = self.interface.identifier.name + # For generated iterator interfaces for other iterable interfaces, we + # just use IterableIterator as the native type, templated on the + # nativeType of the iterable interface. That way we can have a + # templated implementation for all the duplicated iterator + # functionality. + if self.interface.isIteratorInterface(): + itrName = self.interface.iterableInterface.identifier.name + itrDesc = self.getDescriptor(itrName) + nativeTypeDefault = iteratorNativeType(itrDesc) + + elif self.interface.isExternal(): + nativeTypeDefault = "nsIDOM" + ifaceName + else: + nativeTypeDefault = "mozilla::dom::" + ifaceName + + self.nativeType = desc.get('nativeType', nativeTypeDefault) + # Now create a version of nativeType that doesn't have extra + # mozilla::dom:: at the beginning. + prettyNativeType = self.nativeType.split("::") + if prettyNativeType[0] == "mozilla": + prettyNativeType.pop(0) + if prettyNativeType[0] == "dom": + prettyNativeType.pop(0) + self.prettyNativeType = "::".join(prettyNativeType) + + self.jsImplParent = desc.get('jsImplParent', self.nativeType) + + # Do something sane for JSObject + if self.nativeType == "JSObject": + headerDefault = "js/TypeDecls.h" + elif self.interface.isCallback() or self.interface.isJSImplemented(): + # A copy of CGHeaders.getDeclarationFilename; we can't + # import it here, sadly. + # Use our local version of the header, not the exported one, so that + # test bindings, which don't export, will work correctly. + basename = os.path.basename(self.interface.filename()) + headerDefault = basename.replace('.webidl', 'Binding.h') + else: + if not self.interface.isExternal() and self.interface.getExtendedAttribute("HeaderFile"): + headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0] + elif self.interface.isIteratorInterface(): + headerDefault = "mozilla/dom/IterableIterator.h" + else: + headerDefault = self.nativeType + headerDefault = headerDefault.replace("::", "/") + ".h" + self.headerFile = desc.get('headerFile', headerDefault) + self.headerIsDefault = self.headerFile == headerDefault + if self.jsImplParent == self.nativeType: + self.jsImplParentHeader = self.headerFile + else: + self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h" + + self.notflattened = desc.get('notflattened', False) + self.register = desc.get('register', True) + + self.hasXPConnectImpls = desc.get('hasXPConnectImpls', False) + + # If we're concrete, we need to crawl our ancestor interfaces and mark + # them as having a concrete descendant. + self.concrete = (not self.interface.isExternal() and + not self.interface.isCallback() and + not self.interface.isNamespace() and + desc.get('concrete', True)) + self.hasUnforgeableMembers = (self.concrete and + any(MemberIsUnforgeable(m, self) for m in + self.interface.members)) + self.operations = { + 'IndexedGetter': None, + 'IndexedSetter': None, + 'IndexedCreator': None, + 'IndexedDeleter': None, + 'NamedGetter': None, + 'NamedSetter': None, + 'NamedCreator': None, + 'NamedDeleter': None, + 'Stringifier': None, + 'LegacyCaller': None, + 'Jsonifier': None + } + + # Stringifiers and jsonifiers need to be set up whether an interface is + # concrete or not, because they're actually prototype methods and hence + # can apply to instances of descendant interfaces. Legacy callers and + # named/indexed operations only need to be set up on concrete + # interfaces, since they affect the JSClass we end up using, not the + # prototype object. + def addOperation(operation, m): + if not self.operations[operation]: + self.operations[operation] = m + + # Since stringifiers go on the prototype, we only need to worry + # about our own stringifier, not those of our ancestor interfaces. + if not self.interface.isExternal(): + for m in self.interface.members: + if m.isMethod() and m.isStringifier(): + addOperation('Stringifier', m) + if m.isMethod() and m.isJsonifier(): + addOperation('Jsonifier', m) + + if self.concrete: + self.proxy = False + iface = self.interface + for m in iface.members: + # Don't worry about inheriting legacycallers either: in + # practice these are on most-derived prototypes. + if m.isMethod() and m.isLegacycaller(): + if not m.isIdentifierLess(): + raise TypeError("We don't support legacycaller with " + "identifier.\n%s" % m.location) + if len(m.signatures()) != 1: + raise TypeError("We don't support overloaded " + "legacycaller.\n%s" % m.location) + addOperation('LegacyCaller', m) + while iface: + for m in iface.members: + if not m.isMethod(): + continue + + def addIndexedOrNamedOperation(operation, m): + if m.isIndexed(): + operation = 'Indexed' + operation + else: + assert m.isNamed() + operation = 'Named' + operation + addOperation(operation, m) + + if m.isGetter(): + addIndexedOrNamedOperation('Getter', m) + if m.isSetter(): + addIndexedOrNamedOperation('Setter', m) + if m.isCreator(): + addIndexedOrNamedOperation('Creator', m) + if m.isDeleter(): + addIndexedOrNamedOperation('Deleter', m) + if m.isLegacycaller() and iface != self.interface: + raise TypeError("We don't support legacycaller on " + "non-leaf interface %s.\n%s" % + (iface, iface.location)) + + iface.setUserData('hasConcreteDescendant', True) + iface = iface.parent + + self.proxy = (self.supportsIndexedProperties() or + (self.supportsNamedProperties() and + not self.hasNamedPropertiesObject) or + self.hasNonOrdinaryGetPrototypeOf()) + + if self.proxy: + if (not self.operations['IndexedGetter'] and + (self.operations['IndexedSetter'] or + self.operations['IndexedDeleter'] or + self.operations['IndexedCreator'])): + raise SyntaxError("%s supports indexed properties but does " + "not have an indexed getter.\n%s" % + (self.interface, self.interface.location)) + if (not self.operations['NamedGetter'] and + (self.operations['NamedSetter'] or + self.operations['NamedDeleter'] or + self.operations['NamedCreator'])): + raise SyntaxError("%s supports named properties but does " + "not have a named getter.\n%s" % + (self.interface, self.interface.location)) + iface = self.interface + while iface: + iface.setUserData('hasProxyDescendant', True) + iface = iface.parent + + if desc.get('wantsQI', None) is not None: + self._wantsQI = desc.get('wantsQI', None) + self.wrapperCache = (not self.interface.isCallback() and + not self.interface.isIteratorInterface() and + desc.get('wrapperCache', True)) + # Nasty temporary hack for supporting both DOM and SpiderMonkey promises + # without too much pain + if self.interface.identifier.name == "Promise": + assert self.wrapperCache + # But really, we're only wrappercached if we have an interface + # object (that is, when we're not using SpiderMonkey promises). + self.wrapperCache = self.interface.hasInterfaceObject() + + self.name = interface.identifier.name + + # self.extendedAttributes is a dict of dicts, keyed on + # all/getterOnly/setterOnly and then on member name. Values are an + # array of extended attributes. + self.extendedAttributes = {'all': {}, 'getterOnly': {}, 'setterOnly': {}} + + def addExtendedAttribute(attribute, config): + def add(key, members, attribute): + for member in members: + self.extendedAttributes[key].setdefault(member, []).append(attribute) + + if isinstance(config, dict): + for key in ['all', 'getterOnly', 'setterOnly']: + add(key, config.get(key, []), attribute) + elif isinstance(config, list): + add('all', config, attribute) + else: + assert isinstance(config, str) + if config == '*': + iface = self.interface + while iface: + add('all', map(lambda m: m.name, iface.members), attribute) + iface = iface.parent + else: + add('all', [config], attribute) + + if self.interface.isJSImplemented(): + addExtendedAttribute('implicitJSContext', ['constructor']) + else: + for attribute in ['implicitJSContext']: + addExtendedAttribute(attribute, desc.get(attribute, {})) + + if self.interface.identifier.name == 'Navigator': + for m in self.interface.members: + if m.isAttr() and m.navigatorObjectGetter: + # These getters call ConstructNavigatorObject to construct + # the value, and ConstructNavigatorObject needs a JSContext. + self.extendedAttributes['all'].setdefault(m.identifier.name, []).append('implicitJSContext') + + self._binaryNames = desc.get('binaryNames', {}) + self._binaryNames.setdefault('__legacycaller', 'LegacyCall') + self._binaryNames.setdefault('__stringifier', 'Stringify') + + if not self.interface.isExternal(): + def isTestInterface(iface): + return (iface.identifier.name in ["TestInterface", + "TestJSImplInterface", + "TestRenamedInterface"]) + + for member in self.interface.members: + if not member.isAttr() and not member.isMethod(): + continue + binaryName = member.getExtendedAttribute("BinaryName") + if binaryName: + assert isinstance(binaryName, list) + assert len(binaryName) == 1 + self._binaryNames.setdefault(member.identifier.name, + binaryName[0]) + + # Build the prototype chain. + self.prototypeChain = [] + parent = interface + while parent: + self.prototypeChain.insert(0, parent.identifier.name) + parent = parent.parent + config.maxProtoChainLength = max(config.maxProtoChainLength, + len(self.prototypeChain)) + + def binaryNameFor(self, name): + return self._binaryNames.get(name, name) + + @property + def prototypeNameChain(self): + return map(lambda p: self.getDescriptor(p).name, self.prototypeChain) + + @property + def parentPrototypeName(self): + if len(self.prototypeChain) == 1: + return None + return self.getDescriptor(self.prototypeChain[-2]).name + + def hasInterfaceOrInterfacePrototypeObject(self): + + # Forward-declared interfaces don't need either interface object or + # interface prototype object as they're going to use QI. + if self.interface.isExternal(): + return False + + return self.interface.hasInterfaceObject() or self.interface.hasInterfacePrototypeObject() + + @property + def hasNamedPropertiesObject(self): + if self.interface.isExternal(): + return False + + return self.isGlobal() and self.supportsNamedProperties() + + def getExtendedAttributes(self, member, getter=False, setter=False): + def ensureValidThrowsExtendedAttribute(attr): + if (attr is not None and attr is not True): + raise TypeError("Unknown value for 'Throws': " + attr[0]) + + def maybeAppendInfallibleToAttrs(attrs, throws): + ensureValidThrowsExtendedAttribute(throws) + if throws is None: + attrs.append("infallible") + + name = member.identifier.name + throws = self.interface.isJSImplemented() or member.getExtendedAttribute("Throws") + if member.isMethod(): + # JSObject-returning [NewObject] methods must be fallible, + # since they have to (fallibly) allocate the new JSObject. + if (member.getExtendedAttribute("NewObject") and + methodReturnsJSObject(member)): + throws = True + attrs = self.extendedAttributes['all'].get(name, []) + maybeAppendInfallibleToAttrs(attrs, throws) + return attrs + + assert member.isAttr() + assert bool(getter) != bool(setter) + key = 'getterOnly' if getter else 'setterOnly' + attrs = self.extendedAttributes['all'].get(name, []) + self.extendedAttributes[key].get(name, []) + if throws is None: + throwsAttr = "GetterThrows" if getter else "SetterThrows" + throws = member.getExtendedAttribute(throwsAttr) + maybeAppendInfallibleToAttrs(attrs, throws) + return attrs + + def supportsIndexedProperties(self): + return self.operations['IndexedGetter'] is not None + + def supportsNamedProperties(self): + return self.operations['NamedGetter'] is not None + + def hasNonOrdinaryGetPrototypeOf(self): + return self.interface.getExtendedAttribute("NonOrdinaryGetPrototypeOf") + + def needsHeaderInclude(self): + """ + An interface doesn't need a header file if it is not concrete, not + pref-controlled, has no prototype object, has no static methods or + attributes and has no parent. The parent matters because we assert + things about refcounting that depend on the actual underlying type if we + have a parent. + + """ + return (self.interface.isExternal() or self.concrete or + self.interface.hasInterfacePrototypeObject() or + any((m.isAttr() or m.isMethod()) and m.isStatic() for m in self.interface.members) or + self.interface.parent) + + def hasThreadChecks(self): + # isExposedConditionally does not necessarily imply thread checks + # (since at least [SecureContext] is independent of them), but we're + # only used to decide whether to include nsThreadUtils.h, so we don't + # worry about that. + return ((self.isExposedConditionally() and + not self.interface.isExposedInWindow()) or + self.interface.isExposedInSomeButNotAllWorkers()) + + def isExposedConditionally(self): + return (self.interface.isExposedConditionally() or + self.interface.isExposedInSomeButNotAllWorkers()) + + def needsXrayResolveHooks(self): + """ + Generally, any interface with NeedResolve needs Xray + resolveOwnProperty and enumerateOwnProperties hooks. But for + the special case of plugin-loading elements, we do NOT want + those, because we don't want to instantiate plug-ins simply + due to chrome touching them and that's all those hooks do on + those elements. So we special-case those here. + """ + return (self.interface.getExtendedAttribute("NeedResolve") and + self.interface.identifier.name not in ["HTMLObjectElement", + "HTMLEmbedElement", + "HTMLAppletElement"]) + def needsXrayNamedDeleterHook(self): + return self.operations["NamedDeleter"] is not None + + def needsSpecialGenericOps(self): + """ + Returns true if this descriptor requires generic ops other than + GenericBindingMethod/GenericBindingGetter/GenericBindingSetter. + + In practice we need to do this if our this value might be an XPConnect + object or if we need to coerce null/undefined to the global. + """ + return self.hasXPConnectImpls or self.interface.isOnGlobalProtoChain() + + def isGlobal(self): + """ + Returns true if this is the primary interface for a global object + of some sort. + """ + return (self.interface.getExtendedAttribute("Global") or + self.interface.getExtendedAttribute("PrimaryGlobal")) + + @property + def namedPropertiesEnumerable(self): + """ + Returns whether this interface should have enumerable named properties + """ + assert self.proxy + assert self.supportsNamedProperties() + iface = self.interface + while iface: + if iface.getExtendedAttribute("LegacyUnenumerableNamedProperties"): + return False + iface = iface.parent + return True + + @property + def registersGlobalNamesOnWindow(self): + return (not self.interface.isExternal() and + self.interface.hasInterfaceObject() and + self.interface.isExposedInWindow() and + self.register) + + def getDescriptor(self, interfaceName): + """ + Gets the appropriate descriptor for the given interface name. + """ + return self.config.getDescriptor(interfaceName) + + +# Some utility methods +def getTypesFromDescriptor(descriptor): + """ + Get all argument and return types for all members of the descriptor + """ + members = [m for m in descriptor.interface.members] + if descriptor.interface.ctor(): + members.append(descriptor.interface.ctor()) + members.extend(descriptor.interface.namedConstructors) + signatures = [s for m in members if m.isMethod() for s in m.signatures()] + types = [] + for s in signatures: + assert len(s) == 2 + (returnType, arguments) = s + types.append(returnType) + types.extend(a.type for a in arguments) + + types.extend(a.type for a in members if a.isAttr()) + + if descriptor.interface.maplikeOrSetlikeOrIterable: + maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable + if maplikeOrSetlikeOrIterable.hasKeyType(): + types.append(maplikeOrSetlikeOrIterable.keyType) + if maplikeOrSetlikeOrIterable.hasValueType(): + types.append(maplikeOrSetlikeOrIterable.valueType) + return types + + +def getFlatTypes(types): + retval = set() + for type in types: + type = type.unroll() + if type.isUnion(): + retval |= set(type.flatMemberTypes) + else: + retval.add(type) + return retval + + +def getTypesFromDictionary(dictionary): + """ + Get all member types for this dictionary + """ + types = [] + curDict = dictionary + while curDict: + types.extend([m.type for m in curDict.members]) + curDict = curDict.parent + return types + + +def getTypesFromCallback(callback): + """ + Get the types this callback depends on: its return type and the + types of its arguments. + """ + sig = callback.signatures()[0] + types = [sig[0]] # Return type + types.extend(arg.type for arg in sig[1]) # Arguments + return types + + +def getAllTypes(descriptors, dictionaries, callbacks): + """ + Generate all the types we're dealing with. For each type, a tuple + containing type, dictionary is yielded. The dictionary can be None if the + type does not come from a dictionary. + """ + for d in descriptors: + if d.interface.isExternal(): + continue + for t in getTypesFromDescriptor(d): + yield (t, None) + for dictionary in dictionaries: + for t in getTypesFromDictionary(dictionary): + yield (t, dictionary) + for callback in callbacks: + for t in getTypesFromCallback(callback): + yield (t, None) + +def iteratorNativeType(descriptor): + assert descriptor.interface.isIterable() + iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable + assert iterableDecl.isPairIterator() + return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType |