michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: # You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: from WebIDL import IDLInterface, IDLExternalInterface michael@0: import os michael@0: michael@0: autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n" michael@0: michael@0: class Configuration: michael@0: """ michael@0: Represents global configuration state based on IDL parse data and michael@0: the configuration file. michael@0: """ michael@0: def __init__(self, filename, parseData): michael@0: michael@0: # Read the configuration file. michael@0: glbl = {} michael@0: execfile(filename, glbl) michael@0: config = glbl['DOMInterfaces'] michael@0: michael@0: # Build descriptors for all the interfaces we have in the parse data. michael@0: # This allows callers to specify a subset of interfaces by filtering michael@0: # |parseData|. michael@0: self.descriptors = [] michael@0: self.interfaces = {} michael@0: self.maxProtoChainLength = 0; michael@0: for thing in parseData: michael@0: # Some toplevel things are sadly types, and those have an michael@0: # isInterface that doesn't mean the same thing as IDLObject's michael@0: # isInterface()... michael@0: if (not isinstance(thing, IDLInterface) and michael@0: not isinstance(thing, IDLExternalInterface)): michael@0: continue michael@0: iface = thing michael@0: self.interfaces[iface.identifier.name] = iface michael@0: if iface.identifier.name not in config: michael@0: # Completely skip consequential interfaces with no descriptor michael@0: # if they have no interface object because chances are we michael@0: # don't need to do anything interesting with them. michael@0: if iface.isConsequential() and not iface.hasInterfaceObject(): michael@0: continue michael@0: entry = {} michael@0: else: michael@0: entry = config[iface.identifier.name] michael@0: if not isinstance(entry, list): michael@0: assert isinstance(entry, dict) michael@0: entry = [entry] michael@0: elif len(entry) == 1: michael@0: if entry[0].get("workers", False): michael@0: # List with only a workers descriptor means we should michael@0: # infer a mainthread descriptor. If you want only michael@0: # workers bindings, don't use a list here. michael@0: entry.append({}) michael@0: else: michael@0: raise TypeError("Don't use a single-element list for " michael@0: "non-worker-only interface " + iface.identifier.name + michael@0: " in Bindings.conf") michael@0: elif len(entry) == 2: michael@0: if entry[0].get("workers", False) == entry[1].get("workers", False): michael@0: raise TypeError("The two entries for interface " + iface.identifier.name + michael@0: " in Bindings.conf should not have the same value for 'workers'") michael@0: else: michael@0: raise TypeError("Interface " + iface.identifier.name + michael@0: " should have no more than two entries in Bindings.conf") michael@0: self.descriptors.extend([Descriptor(self, iface, x) for x in entry]) michael@0: michael@0: # Keep the descriptor list sorted for determinism. michael@0: self.descriptors.sort(lambda x,y: cmp(x.name, y.name)) michael@0: michael@0: self.descriptorsByName = {} michael@0: for d in self.descriptors: michael@0: self.descriptorsByName.setdefault(d.interface.identifier.name, michael@0: []).append(d) michael@0: michael@0: self.descriptorsByFile = {} michael@0: for d in self.descriptors: michael@0: self.descriptorsByFile.setdefault(d.interface.filename(), michael@0: []).append(d) michael@0: michael@0: self.enums = [e for e in parseData if e.isEnum()] michael@0: michael@0: # Figure out what our main-thread and worker dictionaries and callbacks michael@0: # are. michael@0: mainTypes = set() michael@0: for descriptor in ([self.getDescriptor("DummyInterface", workers=False)] + michael@0: self.getDescriptors(workers=False, isExternal=False, skipGen=False)): michael@0: mainTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor))) michael@0: (mainCallbacks, mainDictionaries) = findCallbacksAndDictionaries(mainTypes) michael@0: michael@0: workerTypes = set(); michael@0: for descriptor in ([self.getDescriptor("DummyInterfaceWorkers", workers=True)] + michael@0: self.getDescriptors(workers=True, isExternal=False, skipGen=False)): michael@0: workerTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor))) michael@0: (workerCallbacks, workerDictionaries) = findCallbacksAndDictionaries(workerTypes) michael@0: michael@0: self.dictionaries = [d for d in parseData if d.isDictionary()] michael@0: self.callbacks = [c for c in parseData if michael@0: c.isCallback() and not c.isInterface()] michael@0: michael@0: def flagWorkerOrMainThread(items, main, worker): michael@0: for item in items: michael@0: if item in main: michael@0: item.setUserData("mainThread", True) michael@0: if item in worker: michael@0: item.setUserData("workers", True) michael@0: flagWorkerOrMainThread(self.callbacks, mainCallbacks, workerCallbacks) michael@0: michael@0: def getInterface(self, ifname): michael@0: return self.interfaces[ifname] michael@0: def getDescriptors(self, **filters): michael@0: """Gets the descriptors that match the given filters.""" michael@0: curr = self.descriptors michael@0: # Collect up our filters, because we may have a webIDLFile filter that michael@0: # we always want to apply first. michael@0: tofilter = [] michael@0: for key, val in filters.iteritems(): michael@0: if key == 'webIDLFile': michael@0: # Special-case this part to make it fast, since most of our michael@0: # getDescriptors calls are conditioned on a webIDLFile. We may michael@0: # not have this key, in which case we have no descriptors michael@0: # either. michael@0: curr = self.descriptorsByFile.get(val, []) michael@0: continue michael@0: elif key == 'hasInterfaceObject': michael@0: getter = lambda x: (not x.interface.isExternal() and michael@0: x.interface.hasInterfaceObject()) michael@0: elif key == 'hasInterfacePrototypeObject': michael@0: getter = lambda x: (not x.interface.isExternal() and michael@0: x.interface.hasInterfacePrototypeObject()) michael@0: elif key == 'hasInterfaceOrInterfacePrototypeObject': michael@0: getter = lambda x: x.hasInterfaceOrInterfacePrototypeObject() michael@0: elif key == 'isCallback': michael@0: getter = lambda x: x.interface.isCallback() michael@0: elif key == 'isExternal': michael@0: getter = lambda x: x.interface.isExternal() michael@0: elif key == 'isJSImplemented': michael@0: getter = lambda x: x.interface.isJSImplemented() michael@0: elif key == 'isNavigatorProperty': michael@0: getter = lambda x: x.interface.getNavigatorProperty() != None michael@0: else: michael@0: # Have to watch out: just closing over "key" is not enough, michael@0: # since we're about to mutate its value michael@0: getter = (lambda attrName: lambda x: getattr(x, attrName))(key) michael@0: tofilter.append((getter, val)) michael@0: for f in tofilter: michael@0: curr = filter(lambda x: f[0](x) == f[1], curr) michael@0: return curr michael@0: def getEnums(self, webIDLFile): michael@0: return filter(lambda e: e.filename() == webIDLFile, self.enums) michael@0: michael@0: @staticmethod michael@0: def _filterForFileAndWorkers(items, filters): michael@0: """Gets the items that match the given filters.""" michael@0: for key, val in filters.iteritems(): michael@0: if key == 'webIDLFile': michael@0: items = filter(lambda x: x.filename() == val, items) michael@0: elif key == 'workers': michael@0: if val: michael@0: items = filter(lambda x: x.getUserData("workers", False), items) michael@0: else: michael@0: items = filter(lambda x: x.getUserData("mainThread", False), items) michael@0: else: michael@0: assert(0) # Unknown key michael@0: return items michael@0: def getDictionaries(self, **filters): michael@0: return self._filterForFileAndWorkers(self.dictionaries, filters) michael@0: def getCallbacks(self, **filters): michael@0: return self._filterForFileAndWorkers(self.callbacks, filters) michael@0: michael@0: def getDescriptor(self, interfaceName, workers): michael@0: """ michael@0: Gets the appropriate descriptor for the given interface name michael@0: and the given workers boolean. michael@0: """ michael@0: for d in self.descriptorsByName[interfaceName]: michael@0: if d.workers == workers: michael@0: return d michael@0: michael@0: if workers: michael@0: for d in self.descriptorsByName[interfaceName]: michael@0: return d michael@0: michael@0: raise NoSuchDescriptorError("For " + interfaceName + " found no matches"); michael@0: def getDescriptorProvider(self, workers): michael@0: """ michael@0: Gets a descriptor provider that can provide descriptors as needed, michael@0: for the given workers boolean michael@0: """ michael@0: return DescriptorProvider(self, workers) michael@0: michael@0: class NoSuchDescriptorError(TypeError): michael@0: def __init__(self, str): michael@0: TypeError.__init__(self, str) michael@0: michael@0: class DescriptorProvider: michael@0: """ michael@0: A way of getting descriptors for interface names michael@0: """ michael@0: def __init__(self, config, workers): michael@0: self.config = config michael@0: self.workers = workers michael@0: michael@0: def getDescriptor(self, interfaceName): michael@0: """ michael@0: Gets the appropriate descriptor for the given interface name given the michael@0: context of the current descriptor. This selects the appropriate michael@0: implementation for cases like workers. michael@0: """ michael@0: return self.config.getDescriptor(interfaceName, self.workers) michael@0: michael@0: class Descriptor(DescriptorProvider): michael@0: """ michael@0: Represents a single descriptor for an interface. See Bindings.conf. michael@0: """ michael@0: def __init__(self, config, interface, desc): michael@0: DescriptorProvider.__init__(self, config, desc.get('workers', False)) michael@0: self.interface = interface michael@0: michael@0: # Read the desc, and fill in the relevant defaults. michael@0: ifaceName = self.interface.identifier.name michael@0: if self.interface.isExternal(): michael@0: if self.workers: michael@0: nativeTypeDefault = "JSObject" michael@0: else: michael@0: nativeTypeDefault = "nsIDOM" + ifaceName michael@0: elif self.interface.isCallback(): michael@0: nativeTypeDefault = "mozilla::dom::" + ifaceName michael@0: else: michael@0: if self.workers: michael@0: nativeTypeDefault = "mozilla::dom::workers::" + ifaceName michael@0: else: michael@0: nativeTypeDefault = "mozilla::dom::" + ifaceName michael@0: michael@0: self.nativeType = desc.get('nativeType', nativeTypeDefault) michael@0: # Now create a version of nativeType that doesn't have extra michael@0: # mozilla::dom:: at the beginning. michael@0: prettyNativeType = self.nativeType.split("::") michael@0: if prettyNativeType[0] == "mozilla": michael@0: prettyNativeType.pop(0) michael@0: if prettyNativeType[0] == "dom": michael@0: prettyNativeType.pop(0) michael@0: self.prettyNativeType = "::".join(prettyNativeType) michael@0: michael@0: self.jsImplParent = desc.get('jsImplParent', self.nativeType) michael@0: michael@0: # Do something sane for JSObject michael@0: if self.nativeType == "JSObject": michael@0: headerDefault = "js/TypeDecls.h" michael@0: elif self.interface.isCallback() or self.interface.isJSImplemented(): michael@0: # A copy of CGHeaders.getDeclarationFilename; we can't michael@0: # import it here, sadly. michael@0: # Use our local version of the header, not the exported one, so that michael@0: # test bindings, which don't export, will work correctly. michael@0: basename = os.path.basename(self.interface.filename()) michael@0: headerDefault = basename.replace('.webidl', 'Binding.h') michael@0: else: michael@0: if self.workers: michael@0: headerDefault = "mozilla/dom/workers/bindings/%s.h" % ifaceName michael@0: elif not self.interface.isExternal() and self.interface.getExtendedAttribute("HeaderFile"): michael@0: headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0] michael@0: else: michael@0: headerDefault = self.nativeType michael@0: headerDefault = headerDefault.replace("::", "/") + ".h" michael@0: self.headerFile = desc.get('headerFile', headerDefault) michael@0: self.headerIsDefault = self.headerFile == headerDefault michael@0: if self.jsImplParent == self.nativeType: michael@0: self.jsImplParentHeader = self.headerFile michael@0: else: michael@0: self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h" michael@0: michael@0: self.skipGen = desc.get('skipGen', False) michael@0: michael@0: self.notflattened = desc.get('notflattened', False) michael@0: self.register = desc.get('register', True) michael@0: michael@0: self.hasXPConnectImpls = desc.get('hasXPConnectImpls', False) michael@0: michael@0: # If we're concrete, we need to crawl our ancestor interfaces and mark michael@0: # them as having a concrete descendant. michael@0: self.concrete = (not self.interface.isExternal() and michael@0: not self.interface.isCallback() and michael@0: desc.get('concrete', True)) michael@0: operations = { michael@0: 'IndexedGetter': None, michael@0: 'IndexedSetter': None, michael@0: 'IndexedCreator': None, michael@0: 'IndexedDeleter': None, michael@0: 'NamedGetter': None, michael@0: 'NamedSetter': None, michael@0: 'NamedCreator': None, michael@0: 'NamedDeleter': None, michael@0: 'Stringifier': None, michael@0: 'LegacyCaller': None, michael@0: 'Jsonifier': None michael@0: } michael@0: if self.concrete: michael@0: self.proxy = False michael@0: iface = self.interface michael@0: def addOperation(operation, m): michael@0: if not operations[operation]: michael@0: operations[operation] = m michael@0: # Since stringifiers go on the prototype, we only need to worry michael@0: # about our own stringifier, not those of our ancestor interfaces. michael@0: for m in iface.members: michael@0: if m.isMethod() and m.isStringifier(): michael@0: addOperation('Stringifier', m) michael@0: if m.isMethod() and m.isJsonifier(): michael@0: addOperation('Jsonifier', m) michael@0: # Don't worry about inheriting legacycallers either: in michael@0: # practice these are on most-derived prototypes. michael@0: if m.isMethod() and m.isLegacycaller(): michael@0: if not m.isIdentifierLess(): michael@0: raise TypeError("We don't support legacycaller with " michael@0: "identifier.\n%s" % m.location); michael@0: if len(m.signatures()) != 1: michael@0: raise TypeError("We don't support overloaded " michael@0: "legacycaller.\n%s" % m.location) michael@0: addOperation('LegacyCaller', m) michael@0: while iface: michael@0: for m in iface.members: michael@0: if not m.isMethod(): michael@0: continue michael@0: michael@0: def addIndexedOrNamedOperation(operation, m): michael@0: self.proxy = True michael@0: if m.isIndexed(): michael@0: operation = 'Indexed' + operation michael@0: else: michael@0: assert m.isNamed() michael@0: operation = 'Named' + operation michael@0: addOperation(operation, m) michael@0: michael@0: if m.isGetter(): michael@0: addIndexedOrNamedOperation('Getter', m) michael@0: if m.isSetter(): michael@0: addIndexedOrNamedOperation('Setter', m) michael@0: if m.isCreator(): michael@0: addIndexedOrNamedOperation('Creator', m) michael@0: if m.isDeleter(): michael@0: addIndexedOrNamedOperation('Deleter', m) michael@0: if m.isLegacycaller() and iface != self.interface: michael@0: raise TypeError("We don't support legacycaller on " michael@0: "non-leaf interface %s.\n%s" % michael@0: (iface, iface.location)) michael@0: michael@0: iface.setUserData('hasConcreteDescendant', True) michael@0: iface = iface.parent michael@0: michael@0: if self.proxy: michael@0: if (not operations['IndexedGetter'] and michael@0: (operations['IndexedSetter'] or michael@0: operations['IndexedDeleter'] or michael@0: operations['IndexedCreator'])): michael@0: raise SyntaxError("%s supports indexed properties but does " michael@0: "not have an indexed getter.\n%s" % michael@0: (self.interface, self.interface.location)) michael@0: if (not operations['NamedGetter'] and michael@0: (operations['NamedSetter'] or michael@0: operations['NamedDeleter'] or michael@0: operations['NamedCreator'])): michael@0: raise SyntaxError("%s supports named properties but does " michael@0: "not have a named getter.\n%s" % michael@0: (self.interface, self.interface.location)) michael@0: iface = self.interface michael@0: while iface: michael@0: iface.setUserData('hasProxyDescendant', True) michael@0: iface = iface.parent michael@0: self.operations = operations michael@0: michael@0: self.nativeOwnership = desc.get('nativeOwnership', 'refcounted') michael@0: if not self.nativeOwnership in ('owned', 'refcounted'): michael@0: raise TypeError("Descriptor for %s has unrecognized value (%s) " michael@0: "for nativeOwnership" % michael@0: (self.interface.identifier.name, self.nativeOwnership)) michael@0: if desc.get('wantsQI', None) != None: michael@0: self._wantsQI = desc.get('wantsQI', None) michael@0: self.wrapperCache = (not self.interface.isCallback() and michael@0: (self.nativeOwnership != 'owned' and michael@0: desc.get('wrapperCache', True))) michael@0: michael@0: def make_name(name): michael@0: return name + "_workers" if self.workers else name michael@0: self.name = make_name(interface.identifier.name) michael@0: michael@0: # self.extendedAttributes is a dict of dicts, keyed on michael@0: # all/getterOnly/setterOnly and then on member name. Values are an michael@0: # array of extended attributes. michael@0: self.extendedAttributes = { 'all': {}, 'getterOnly': {}, 'setterOnly': {} } michael@0: michael@0: def addExtendedAttribute(attribute, config): michael@0: def add(key, members, attribute): michael@0: for member in members: michael@0: self.extendedAttributes[key].setdefault(member, []).append(attribute) michael@0: michael@0: if isinstance(config, dict): michael@0: for key in ['all', 'getterOnly', 'setterOnly']: michael@0: add(key, config.get(key, []), attribute) michael@0: elif isinstance(config, list): michael@0: add('all', config, attribute) michael@0: else: michael@0: assert isinstance(config, str) michael@0: if config == '*': michael@0: iface = self.interface michael@0: while iface: michael@0: add('all', map(lambda m: m.name, iface.members), attribute) michael@0: iface = iface.parent michael@0: else: michael@0: add('all', [config], attribute) michael@0: michael@0: if self.interface.isJSImplemented(): michael@0: addExtendedAttribute('implicitJSContext', ['constructor']) michael@0: else: michael@0: for attribute in ['implicitJSContext', 'resultNotAddRefed']: michael@0: addExtendedAttribute(attribute, desc.get(attribute, {})) michael@0: michael@0: self.binaryNames = desc.get('binaryNames', {}) michael@0: if '__legacycaller' not in self.binaryNames: michael@0: self.binaryNames["__legacycaller"] = "LegacyCall" michael@0: if '__stringifier' not in self.binaryNames: michael@0: self.binaryNames["__stringifier"] = "Stringify" michael@0: michael@0: # Build the prototype chain. michael@0: self.prototypeChain = [] michael@0: parent = interface michael@0: while parent: michael@0: self.prototypeChain.insert(0, parent.identifier.name) michael@0: parent = parent.parent michael@0: config.maxProtoChainLength = max(config.maxProtoChainLength, michael@0: len(self.prototypeChain)) michael@0: michael@0: def hasInterfaceOrInterfacePrototypeObject(self): michael@0: michael@0: # Forward-declared interfaces don't need either interface object or michael@0: # interface prototype object as they're going to use QI (on main thread) michael@0: # or be passed as a JSObject (on worker threads). michael@0: if self.interface.isExternal(): michael@0: return False michael@0: michael@0: return self.interface.hasInterfaceObject() or self.interface.hasInterfacePrototypeObject() michael@0: michael@0: def getExtendedAttributes(self, member, getter=False, setter=False): michael@0: def ensureValidThrowsExtendedAttribute(attr): michael@0: assert(attr is None or attr is True or len(attr) == 1) michael@0: if (attr is not None and attr is not True and michael@0: 'Workers' not in attr and 'MainThread' not in attr): michael@0: raise TypeError("Unknown value for 'Throws': " + attr[0]) michael@0: michael@0: def maybeAppendInfallibleToAttrs(attrs, throws): michael@0: ensureValidThrowsExtendedAttribute(throws) michael@0: if (throws is None or michael@0: (throws is not True and michael@0: ('Workers' not in throws or not self.workers) and michael@0: ('MainThread' not in throws or self.workers))): michael@0: attrs.append("infallible") michael@0: michael@0: name = member.identifier.name michael@0: throws = self.interface.isJSImplemented() or member.getExtendedAttribute("Throws") michael@0: if member.isMethod(): michael@0: attrs = self.extendedAttributes['all'].get(name, []) michael@0: maybeAppendInfallibleToAttrs(attrs, throws) michael@0: return attrs michael@0: michael@0: assert member.isAttr() michael@0: assert bool(getter) != bool(setter) michael@0: key = 'getterOnly' if getter else 'setterOnly' michael@0: attrs = self.extendedAttributes['all'].get(name, []) + self.extendedAttributes[key].get(name, []) michael@0: if throws is None: michael@0: throwsAttr = "GetterThrows" if getter else "SetterThrows" michael@0: throws = member.getExtendedAttribute(throwsAttr) michael@0: maybeAppendInfallibleToAttrs(attrs, throws) michael@0: return attrs michael@0: michael@0: def supportsIndexedProperties(self): michael@0: return self.operations['IndexedGetter'] is not None michael@0: michael@0: def supportsNamedProperties(self): michael@0: return self.operations['NamedGetter'] is not None michael@0: michael@0: def needsConstructHookHolder(self): michael@0: assert self.interface.hasInterfaceObject() michael@0: return False michael@0: michael@0: def needsHeaderInclude(self): michael@0: """ michael@0: An interface doesn't need a header file if it is not concrete, michael@0: not pref-controlled, has no prototype object, and has no michael@0: static methods or attributes. michael@0: """ michael@0: return (self.interface.isExternal() or self.concrete or michael@0: self.interface.hasInterfacePrototypeObject() or michael@0: any((m.isAttr() or m.isMethod()) and m.isStatic() for m michael@0: in self.interface.members)) michael@0: michael@0: def isExposedConditionally(self): michael@0: return (self.interface.getExtendedAttribute("Pref") or michael@0: self.interface.getExtendedAttribute("ChromeOnly") or michael@0: self.interface.getExtendedAttribute("Func") or michael@0: self.interface.getExtendedAttribute("AvailableIn")) michael@0: michael@0: def needsXrayResolveHooks(self): michael@0: """ michael@0: Generally, any interface with NeedNewResolve needs Xray michael@0: resolveOwnProperty and enumerateOwnProperties hooks. But for michael@0: the special case of plugin-loading elements, we do NOT want michael@0: those, because we don't want to instantiate plug-ins simply michael@0: due to chrome touching them and that's all those hooks do on michael@0: those elements. So we special-case those here. michael@0: """ michael@0: return (self.interface.getExtendedAttribute("NeedNewResolve") and michael@0: self.interface.identifier.name not in ["HTMLObjectElement", michael@0: "HTMLEmbedElement", michael@0: "HTMLAppletElement"]) michael@0: michael@0: def needsSpecialGenericOps(self): michael@0: """ michael@0: Returns true if this descriptor requires generic ops other than michael@0: GenericBindingMethod/GenericBindingGetter/GenericBindingSetter. michael@0: michael@0: In practice we need to do this if our this value might be an XPConnect michael@0: object or if we need to coerce null/undefined to the global. michael@0: """ michael@0: return self.hasXPConnectImpls or self.interface.isOnGlobalProtoChain() michael@0: michael@0: # Some utility methods michael@0: def getTypesFromDescriptor(descriptor): michael@0: """ michael@0: Get all argument and return types for all members of the descriptor michael@0: """ michael@0: members = [m for m in descriptor.interface.members] michael@0: if descriptor.interface.ctor(): michael@0: members.append(descriptor.interface.ctor()) michael@0: members.extend(descriptor.interface.namedConstructors) michael@0: signatures = [s for m in members if m.isMethod() for s in m.signatures()] michael@0: types = [] michael@0: for s in signatures: michael@0: assert len(s) == 2 michael@0: (returnType, arguments) = s michael@0: types.append(returnType) michael@0: types.extend(a.type for a in arguments) michael@0: michael@0: types.extend(a.type for a in members if a.isAttr()) michael@0: return types michael@0: michael@0: def getFlatTypes(types): michael@0: retval = set() michael@0: for type in types: michael@0: type = type.unroll() michael@0: if type.isUnion(): michael@0: retval |= set(type.flatMemberTypes) michael@0: else: michael@0: retval.add(type) michael@0: return retval michael@0: michael@0: def getTypesFromDictionary(dictionary): michael@0: """ michael@0: Get all member types for this dictionary michael@0: """ michael@0: types = [] michael@0: curDict = dictionary michael@0: while curDict: michael@0: types.extend([m.type for m in curDict.members]) michael@0: curDict = curDict.parent michael@0: return types michael@0: michael@0: def getTypesFromCallback(callback): michael@0: """ michael@0: Get the types this callback depends on: its return type and the michael@0: types of its arguments. michael@0: """ michael@0: sig = callback.signatures()[0] michael@0: types = [sig[0]] # Return type michael@0: types.extend(arg.type for arg in sig[1]) # Arguments michael@0: return types michael@0: michael@0: def findCallbacksAndDictionaries(inputTypes): michael@0: """ michael@0: Ensure that all callbacks and dictionaries reachable from types end up in michael@0: the returned callbacks and dictionaries sets. michael@0: michael@0: Note that we assume that our initial invocation already includes all types michael@0: reachable via descriptors in "types", so we only have to deal with things michael@0: that are themeselves reachable via callbacks and dictionaries. michael@0: """ michael@0: def doFindCallbacksAndDictionaries(types, callbacks, dictionaries): michael@0: unhandledTypes = set() michael@0: for type in types: michael@0: if type.isCallback() and type not in callbacks: michael@0: unhandledTypes |= getFlatTypes(getTypesFromCallback(type)) michael@0: callbacks.add(type) michael@0: elif type.isDictionary() and type.inner not in dictionaries: michael@0: d = type.inner michael@0: unhandledTypes |= getFlatTypes(getTypesFromDictionary(d)) michael@0: while d: michael@0: dictionaries.add(d) michael@0: d = d.parent michael@0: if len(unhandledTypes) != 0: michael@0: doFindCallbacksAndDictionaries(unhandledTypes, callbacks, dictionaries) michael@0: michael@0: retCallbacks = set() michael@0: retDictionaries = set() michael@0: doFindCallbacksAndDictionaries(inputTypes, retCallbacks, retDictionaries) michael@0: return (retCallbacks, retDictionaries)