dom/bindings/Configuration.py

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 # This Source Code Form is subject to the terms of the Mozilla Public
     2 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3 # You can obtain one at http://mozilla.org/MPL/2.0/.
     5 from WebIDL import IDLInterface, IDLExternalInterface
     6 import os
     8 autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
    10 class Configuration:
    11     """
    12     Represents global configuration state based on IDL parse data and
    13     the configuration file.
    14     """
    15     def __init__(self, filename, parseData):
    17         # Read the configuration file.
    18         glbl = {}
    19         execfile(filename, glbl)
    20         config = glbl['DOMInterfaces']
    22         # Build descriptors for all the interfaces we have in the parse data.
    23         # This allows callers to specify a subset of interfaces by filtering
    24         # |parseData|.
    25         self.descriptors = []
    26         self.interfaces = {}
    27         self.maxProtoChainLength = 0;
    28         for thing in parseData:
    29             # Some toplevel things are sadly types, and those have an
    30             # isInterface that doesn't mean the same thing as IDLObject's
    31             # isInterface()...
    32             if (not isinstance(thing, IDLInterface) and
    33                 not isinstance(thing, IDLExternalInterface)):
    34                 continue
    35             iface = thing
    36             self.interfaces[iface.identifier.name] = iface
    37             if iface.identifier.name not in config:
    38                 # Completely skip consequential interfaces with no descriptor
    39                 # if they have no interface object because chances are we
    40                 # don't need to do anything interesting with them.
    41                 if iface.isConsequential() and not iface.hasInterfaceObject():
    42                     continue
    43                 entry = {}
    44             else:
    45                 entry = config[iface.identifier.name]
    46             if not isinstance(entry, list):
    47                 assert isinstance(entry, dict)
    48                 entry = [entry]
    49             elif len(entry) == 1:
    50                 if entry[0].get("workers", False):
    51                     # List with only a workers descriptor means we should
    52                     # infer a mainthread descriptor.  If you want only
    53                     # workers bindings, don't use a list here.
    54                     entry.append({})
    55                 else:
    56                     raise TypeError("Don't use a single-element list for "
    57                                     "non-worker-only interface " + iface.identifier.name +
    58                                     " in Bindings.conf")
    59             elif len(entry) == 2:
    60                 if entry[0].get("workers", False) == entry[1].get("workers", False):
    61                     raise TypeError("The two entries for interface " + iface.identifier.name +
    62                                     " in Bindings.conf should not have the same value for 'workers'")
    63             else:
    64                 raise TypeError("Interface " + iface.identifier.name +
    65                                 " should have no more than two entries in Bindings.conf")
    66             self.descriptors.extend([Descriptor(self, iface, x) for x in entry])
    68         # Keep the descriptor list sorted for determinism.
    69         self.descriptors.sort(lambda x,y: cmp(x.name, y.name))
    71         self.descriptorsByName = {}
    72         for d in self.descriptors:
    73             self.descriptorsByName.setdefault(d.interface.identifier.name,
    74                                               []).append(d)
    76         self.descriptorsByFile = {}
    77         for d in self.descriptors:
    78             self.descriptorsByFile.setdefault(d.interface.filename(),
    79                                               []).append(d)
    81         self.enums = [e for e in parseData if e.isEnum()]
    83         # Figure out what our main-thread and worker dictionaries and callbacks
    84         # are.
    85         mainTypes = set()
    86         for descriptor in ([self.getDescriptor("DummyInterface", workers=False)] +
    87                            self.getDescriptors(workers=False, isExternal=False, skipGen=False)):
    88             mainTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor)))
    89         (mainCallbacks, mainDictionaries) = findCallbacksAndDictionaries(mainTypes)
    91         workerTypes = set();
    92         for descriptor in ([self.getDescriptor("DummyInterfaceWorkers", workers=True)] +
    93                            self.getDescriptors(workers=True, isExternal=False, skipGen=False)):
    94             workerTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor)))
    95         (workerCallbacks, workerDictionaries) = findCallbacksAndDictionaries(workerTypes)
    97         self.dictionaries = [d for d in parseData if d.isDictionary()]
    98         self.callbacks = [c for c in parseData if
    99                           c.isCallback() and not c.isInterface()]
   101         def flagWorkerOrMainThread(items, main, worker):
   102             for item in items:
   103                 if item in main:
   104                     item.setUserData("mainThread", True)
   105                 if item in worker:
   106                     item.setUserData("workers", True)
   107         flagWorkerOrMainThread(self.callbacks, mainCallbacks, workerCallbacks)
   109     def getInterface(self, ifname):
   110         return self.interfaces[ifname]
   111     def getDescriptors(self, **filters):
   112         """Gets the descriptors that match the given filters."""
   113         curr = self.descriptors
   114         # Collect up our filters, because we may have a webIDLFile filter that
   115         # we always want to apply first.
   116         tofilter = []
   117         for key, val in filters.iteritems():
   118             if key == 'webIDLFile':
   119                 # Special-case this part to make it fast, since most of our
   120                 # getDescriptors calls are conditioned on a webIDLFile.  We may
   121                 # not have this key, in which case we have no descriptors
   122                 # either.
   123                 curr = self.descriptorsByFile.get(val, [])
   124                 continue
   125             elif key == 'hasInterfaceObject':
   126                 getter = lambda x: (not x.interface.isExternal() and
   127                                     x.interface.hasInterfaceObject())
   128             elif key == 'hasInterfacePrototypeObject':
   129                 getter = lambda x: (not x.interface.isExternal() and
   130                                     x.interface.hasInterfacePrototypeObject())
   131             elif key == 'hasInterfaceOrInterfacePrototypeObject':
   132                 getter = lambda x: x.hasInterfaceOrInterfacePrototypeObject()
   133             elif key == 'isCallback':
   134                 getter = lambda x: x.interface.isCallback()
   135             elif key == 'isExternal':
   136                 getter = lambda x: x.interface.isExternal()
   137             elif key == 'isJSImplemented':
   138                 getter = lambda x: x.interface.isJSImplemented()
   139             elif key == 'isNavigatorProperty':
   140                 getter = lambda x: x.interface.getNavigatorProperty() != None
   141             else:
   142                 # Have to watch out: just closing over "key" is not enough,
   143                 # since we're about to mutate its value
   144                 getter = (lambda attrName: lambda x: getattr(x, attrName))(key)
   145             tofilter.append((getter, val))
   146         for f in tofilter:
   147             curr = filter(lambda x: f[0](x) == f[1], curr)
   148         return curr
   149     def getEnums(self, webIDLFile):
   150         return filter(lambda e: e.filename() == webIDLFile, self.enums)
   152     @staticmethod
   153     def _filterForFileAndWorkers(items, filters):
   154         """Gets the items that match the given filters."""
   155         for key, val in filters.iteritems():
   156             if key == 'webIDLFile':
   157                 items = filter(lambda x: x.filename() == val, items)
   158             elif key == 'workers':
   159                 if val:
   160                     items = filter(lambda x: x.getUserData("workers", False), items)
   161                 else:
   162                     items = filter(lambda x: x.getUserData("mainThread", False), items)
   163             else:
   164                 assert(0) # Unknown key
   165         return items
   166     def getDictionaries(self, **filters):
   167         return self._filterForFileAndWorkers(self.dictionaries, filters)
   168     def getCallbacks(self, **filters):
   169         return self._filterForFileAndWorkers(self.callbacks, filters)
   171     def getDescriptor(self, interfaceName, workers):
   172         """
   173         Gets the appropriate descriptor for the given interface name
   174         and the given workers boolean.
   175         """
   176         for d in self.descriptorsByName[interfaceName]:
   177             if d.workers == workers:
   178                 return d
   180         if workers:
   181             for d in self.descriptorsByName[interfaceName]:
   182                 return d
   184         raise NoSuchDescriptorError("For " + interfaceName + " found no matches");
   185     def getDescriptorProvider(self, workers):
   186         """
   187         Gets a descriptor provider that can provide descriptors as needed,
   188         for the given workers boolean
   189         """
   190         return DescriptorProvider(self, workers)
   192 class NoSuchDescriptorError(TypeError):
   193     def __init__(self, str):
   194         TypeError.__init__(self, str)
   196 class DescriptorProvider:
   197     """
   198     A way of getting descriptors for interface names
   199     """
   200     def __init__(self, config, workers):
   201         self.config = config
   202         self.workers = workers
   204     def getDescriptor(self, interfaceName):
   205         """
   206         Gets the appropriate descriptor for the given interface name given the
   207         context of the current descriptor. This selects the appropriate
   208         implementation for cases like workers.
   209         """
   210         return self.config.getDescriptor(interfaceName, self.workers)
   212 class Descriptor(DescriptorProvider):
   213     """
   214     Represents a single descriptor for an interface. See Bindings.conf.
   215     """
   216     def __init__(self, config, interface, desc):
   217         DescriptorProvider.__init__(self, config, desc.get('workers', False))
   218         self.interface = interface
   220         # Read the desc, and fill in the relevant defaults.
   221         ifaceName = self.interface.identifier.name
   222         if self.interface.isExternal():
   223             if self.workers:
   224                 nativeTypeDefault = "JSObject"
   225             else:
   226                 nativeTypeDefault = "nsIDOM" + ifaceName
   227         elif self.interface.isCallback():
   228             nativeTypeDefault = "mozilla::dom::" + ifaceName
   229         else:
   230             if self.workers:
   231                 nativeTypeDefault = "mozilla::dom::workers::" + ifaceName
   232             else:
   233                 nativeTypeDefault = "mozilla::dom::" + ifaceName
   235         self.nativeType = desc.get('nativeType', nativeTypeDefault)
   236         # Now create a version of nativeType that doesn't have extra
   237         # mozilla::dom:: at the beginning.
   238         prettyNativeType = self.nativeType.split("::")
   239         if prettyNativeType[0] == "mozilla":
   240             prettyNativeType.pop(0)
   241             if prettyNativeType[0] == "dom":
   242                 prettyNativeType.pop(0)
   243         self.prettyNativeType = "::".join(prettyNativeType)
   245         self.jsImplParent = desc.get('jsImplParent', self.nativeType)
   247         # Do something sane for JSObject
   248         if self.nativeType == "JSObject":
   249             headerDefault = "js/TypeDecls.h"
   250         elif self.interface.isCallback() or self.interface.isJSImplemented():
   251             # A copy of CGHeaders.getDeclarationFilename; we can't
   252             # import it here, sadly.
   253             # Use our local version of the header, not the exported one, so that
   254             # test bindings, which don't export, will work correctly.
   255             basename = os.path.basename(self.interface.filename())
   256             headerDefault = basename.replace('.webidl', 'Binding.h')
   257         else:
   258             if self.workers:
   259                 headerDefault = "mozilla/dom/workers/bindings/%s.h" % ifaceName
   260             elif not self.interface.isExternal() and self.interface.getExtendedAttribute("HeaderFile"):
   261                 headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0]
   262             else:
   263                 headerDefault = self.nativeType
   264                 headerDefault = headerDefault.replace("::", "/") + ".h"
   265         self.headerFile = desc.get('headerFile', headerDefault)
   266         self.headerIsDefault = self.headerFile == headerDefault
   267         if self.jsImplParent == self.nativeType:
   268             self.jsImplParentHeader = self.headerFile
   269         else:
   270             self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h"
   272         self.skipGen = desc.get('skipGen', False)
   274         self.notflattened = desc.get('notflattened', False)
   275         self.register = desc.get('register', True)
   277         self.hasXPConnectImpls = desc.get('hasXPConnectImpls', False)
   279         # If we're concrete, we need to crawl our ancestor interfaces and mark
   280         # them as having a concrete descendant.
   281         self.concrete = (not self.interface.isExternal() and
   282                          not self.interface.isCallback() and
   283                          desc.get('concrete', True))
   284         operations = {
   285             'IndexedGetter': None,
   286             'IndexedSetter': None,
   287             'IndexedCreator': None,
   288             'IndexedDeleter': None,
   289             'NamedGetter': None,
   290             'NamedSetter': None,
   291             'NamedCreator': None,
   292             'NamedDeleter': None,
   293             'Stringifier': None,
   294             'LegacyCaller': None,
   295             'Jsonifier': None
   296             }
   297         if self.concrete:
   298             self.proxy = False
   299             iface = self.interface
   300             def addOperation(operation, m):
   301                 if not operations[operation]:
   302                     operations[operation] = m
   303             # Since stringifiers go on the prototype, we only need to worry
   304             # about our own stringifier, not those of our ancestor interfaces.
   305             for m in iface.members:
   306                 if m.isMethod() and m.isStringifier():
   307                     addOperation('Stringifier', m)
   308                 if m.isMethod() and m.isJsonifier():
   309                     addOperation('Jsonifier', m)
   310                 # Don't worry about inheriting legacycallers either: in
   311                 # practice these are on most-derived prototypes.
   312                 if m.isMethod() and m.isLegacycaller():
   313                     if not m.isIdentifierLess():
   314                         raise TypeError("We don't support legacycaller with "
   315                                         "identifier.\n%s" % m.location);
   316                     if len(m.signatures()) != 1:
   317                         raise TypeError("We don't support overloaded "
   318                                         "legacycaller.\n%s" % m.location)
   319                     addOperation('LegacyCaller', m)
   320             while iface:
   321                 for m in iface.members:
   322                     if not m.isMethod():
   323                         continue
   325                     def addIndexedOrNamedOperation(operation, m):
   326                         self.proxy = True
   327                         if m.isIndexed():
   328                             operation = 'Indexed' + operation
   329                         else:
   330                             assert m.isNamed()
   331                             operation = 'Named' + operation
   332                         addOperation(operation, m)
   334                     if m.isGetter():
   335                         addIndexedOrNamedOperation('Getter', m)
   336                     if m.isSetter():
   337                         addIndexedOrNamedOperation('Setter', m)
   338                     if m.isCreator():
   339                         addIndexedOrNamedOperation('Creator', m)
   340                     if m.isDeleter():
   341                         addIndexedOrNamedOperation('Deleter', m)
   342                     if m.isLegacycaller() and iface != self.interface:
   343                         raise TypeError("We don't support legacycaller on "
   344                                         "non-leaf interface %s.\n%s" %
   345                                         (iface, iface.location))
   347                 iface.setUserData('hasConcreteDescendant', True)
   348                 iface = iface.parent
   350             if self.proxy:
   351                 if (not operations['IndexedGetter'] and
   352                     (operations['IndexedSetter'] or
   353                      operations['IndexedDeleter'] or
   354                      operations['IndexedCreator'])):
   355                     raise SyntaxError("%s supports indexed properties but does "
   356                                       "not have an indexed getter.\n%s" %
   357                                       (self.interface, self.interface.location))
   358                 if (not operations['NamedGetter'] and
   359                     (operations['NamedSetter'] or
   360                      operations['NamedDeleter'] or
   361                      operations['NamedCreator'])):
   362                     raise SyntaxError("%s supports named properties but does "
   363                                       "not have a named getter.\n%s" %
   364                                       (self.interface, self.interface.location))
   365                 iface = self.interface
   366                 while iface:
   367                     iface.setUserData('hasProxyDescendant', True)
   368                     iface = iface.parent
   369         self.operations = operations
   371         self.nativeOwnership = desc.get('nativeOwnership', 'refcounted')
   372         if not self.nativeOwnership in ('owned', 'refcounted'):
   373             raise TypeError("Descriptor for %s has unrecognized value (%s) "
   374                             "for nativeOwnership" %
   375                             (self.interface.identifier.name, self.nativeOwnership))
   376         if desc.get('wantsQI', None) != None:
   377             self._wantsQI = desc.get('wantsQI', None)
   378         self.wrapperCache = (not self.interface.isCallback() and
   379                              (self.nativeOwnership != 'owned' and
   380                               desc.get('wrapperCache', True)))
   382         def make_name(name):
   383             return name + "_workers" if self.workers else name
   384         self.name = make_name(interface.identifier.name)
   386         # self.extendedAttributes is a dict of dicts, keyed on
   387         # all/getterOnly/setterOnly and then on member name. Values are an
   388         # array of extended attributes.
   389         self.extendedAttributes = { 'all': {}, 'getterOnly': {}, 'setterOnly': {} }
   391         def addExtendedAttribute(attribute, config):
   392             def add(key, members, attribute):
   393                 for member in members:
   394                     self.extendedAttributes[key].setdefault(member, []).append(attribute)
   396             if isinstance(config, dict):
   397                 for key in ['all', 'getterOnly', 'setterOnly']:
   398                     add(key, config.get(key, []), attribute)
   399             elif isinstance(config, list):
   400                 add('all', config, attribute)
   401             else:
   402                 assert isinstance(config, str)
   403                 if config == '*':
   404                     iface = self.interface
   405                     while iface:
   406                         add('all', map(lambda m: m.name, iface.members), attribute)
   407                         iface = iface.parent
   408                 else:
   409                     add('all', [config], attribute)
   411         if self.interface.isJSImplemented():
   412             addExtendedAttribute('implicitJSContext', ['constructor'])
   413         else:
   414             for attribute in ['implicitJSContext', 'resultNotAddRefed']:
   415                 addExtendedAttribute(attribute, desc.get(attribute, {}))
   417         self.binaryNames = desc.get('binaryNames', {})
   418         if '__legacycaller' not in self.binaryNames:
   419             self.binaryNames["__legacycaller"] = "LegacyCall"
   420         if '__stringifier' not in self.binaryNames:
   421             self.binaryNames["__stringifier"] = "Stringify"
   423         # Build the prototype chain.
   424         self.prototypeChain = []
   425         parent = interface
   426         while parent:
   427             self.prototypeChain.insert(0, parent.identifier.name)
   428             parent = parent.parent
   429         config.maxProtoChainLength = max(config.maxProtoChainLength,
   430                                          len(self.prototypeChain))
   432     def hasInterfaceOrInterfacePrototypeObject(self):
   434         # Forward-declared interfaces don't need either interface object or
   435         # interface prototype object as they're going to use QI (on main thread)
   436         # or be passed as a JSObject (on worker threads).
   437         if self.interface.isExternal():
   438             return False
   440         return self.interface.hasInterfaceObject() or self.interface.hasInterfacePrototypeObject()
   442     def getExtendedAttributes(self, member, getter=False, setter=False):
   443         def ensureValidThrowsExtendedAttribute(attr):
   444             assert(attr is None or attr is True or len(attr) == 1)
   445             if (attr is not None and attr is not True and
   446                 'Workers' not in attr and 'MainThread' not in attr):
   447                 raise TypeError("Unknown value for 'Throws': " + attr[0])
   449         def maybeAppendInfallibleToAttrs(attrs, throws):
   450             ensureValidThrowsExtendedAttribute(throws)
   451             if (throws is None or
   452                 (throws is not True and
   453                  ('Workers' not in throws or not self.workers) and
   454                  ('MainThread' not in throws or self.workers))):
   455                 attrs.append("infallible")
   457         name = member.identifier.name
   458         throws = self.interface.isJSImplemented() or member.getExtendedAttribute("Throws")
   459         if member.isMethod():
   460             attrs = self.extendedAttributes['all'].get(name, [])
   461             maybeAppendInfallibleToAttrs(attrs, throws)
   462             return attrs
   464         assert member.isAttr()
   465         assert bool(getter) != bool(setter)
   466         key = 'getterOnly' if getter else 'setterOnly'
   467         attrs = self.extendedAttributes['all'].get(name, []) + self.extendedAttributes[key].get(name, [])
   468         if throws is None:
   469             throwsAttr = "GetterThrows" if getter else "SetterThrows"
   470             throws = member.getExtendedAttribute(throwsAttr)
   471         maybeAppendInfallibleToAttrs(attrs, throws)
   472         return attrs
   474     def supportsIndexedProperties(self):
   475         return self.operations['IndexedGetter'] is not None
   477     def supportsNamedProperties(self):
   478         return self.operations['NamedGetter'] is not None
   480     def needsConstructHookHolder(self):
   481         assert self.interface.hasInterfaceObject()
   482         return False
   484     def needsHeaderInclude(self):
   485         """
   486         An interface doesn't need a header file if it is not concrete,
   487         not pref-controlled, has no prototype object, and has no
   488         static methods or attributes.
   489         """
   490         return (self.interface.isExternal() or self.concrete or
   491             self.interface.hasInterfacePrototypeObject() or
   492             any((m.isAttr() or m.isMethod()) and m.isStatic() for m
   493                 in self.interface.members))
   495     def isExposedConditionally(self):
   496         return (self.interface.getExtendedAttribute("Pref") or
   497                 self.interface.getExtendedAttribute("ChromeOnly") or
   498                 self.interface.getExtendedAttribute("Func") or
   499                 self.interface.getExtendedAttribute("AvailableIn"))
   501     def needsXrayResolveHooks(self):
   502         """
   503         Generally, any interface with NeedNewResolve needs Xray
   504         resolveOwnProperty and enumerateOwnProperties hooks.  But for
   505         the special case of plugin-loading elements, we do NOT want
   506         those, because we don't want to instantiate plug-ins simply
   507         due to chrome touching them and that's all those hooks do on
   508         those elements.  So we special-case those here.
   509         """
   510         return (self.interface.getExtendedAttribute("NeedNewResolve") and
   511                 self.interface.identifier.name not in ["HTMLObjectElement",
   512                                                        "HTMLEmbedElement",
   513                                                        "HTMLAppletElement"])
   515     def needsSpecialGenericOps(self):
   516         """
   517         Returns true if this descriptor requires generic ops other than
   518         GenericBindingMethod/GenericBindingGetter/GenericBindingSetter.
   520         In practice we need to do this if our this value might be an XPConnect
   521         object or if we need to coerce null/undefined to the global.
   522         """
   523         return self.hasXPConnectImpls or self.interface.isOnGlobalProtoChain()
   525 # Some utility methods
   526 def getTypesFromDescriptor(descriptor):
   527     """
   528     Get all argument and return types for all members of the descriptor
   529     """
   530     members = [m for m in descriptor.interface.members]
   531     if descriptor.interface.ctor():
   532         members.append(descriptor.interface.ctor())
   533     members.extend(descriptor.interface.namedConstructors)
   534     signatures = [s for m in members if m.isMethod() for s in m.signatures()]
   535     types = []
   536     for s in signatures:
   537         assert len(s) == 2
   538         (returnType, arguments) = s
   539         types.append(returnType)
   540         types.extend(a.type for a in arguments)
   542     types.extend(a.type for a in members if a.isAttr())
   543     return types
   545 def getFlatTypes(types):
   546     retval = set()
   547     for type in types:
   548         type = type.unroll()
   549         if type.isUnion():
   550             retval |= set(type.flatMemberTypes)
   551         else:
   552             retval.add(type)
   553     return retval
   555 def getTypesFromDictionary(dictionary):
   556     """
   557     Get all member types for this dictionary
   558     """
   559     types = []
   560     curDict = dictionary
   561     while curDict:
   562         types.extend([m.type for m in curDict.members])
   563         curDict = curDict.parent
   564     return types
   566 def getTypesFromCallback(callback):
   567     """
   568     Get the types this callback depends on: its return type and the
   569     types of its arguments.
   570     """
   571     sig = callback.signatures()[0]
   572     types = [sig[0]] # Return type
   573     types.extend(arg.type for arg in sig[1]) # Arguments
   574     return types
   576 def findCallbacksAndDictionaries(inputTypes):
   577     """
   578     Ensure that all callbacks and dictionaries reachable from types end up in
   579     the returned callbacks and dictionaries sets.
   581     Note that we assume that our initial invocation already includes all types
   582     reachable via descriptors in "types", so we only have to deal with things
   583     that are themeselves reachable via callbacks and dictionaries.
   584     """
   585     def doFindCallbacksAndDictionaries(types, callbacks, dictionaries):
   586         unhandledTypes = set()
   587         for type in types:
   588             if type.isCallback() and type not in callbacks:
   589                 unhandledTypes |= getFlatTypes(getTypesFromCallback(type))
   590                 callbacks.add(type)
   591             elif type.isDictionary() and type.inner not in dictionaries:
   592                 d = type.inner
   593                 unhandledTypes |= getFlatTypes(getTypesFromDictionary(d))
   594                 while d:
   595                     dictionaries.add(d)
   596                     d = d.parent
   597         if len(unhandledTypes) != 0:
   598             doFindCallbacksAndDictionaries(unhandledTypes, callbacks, dictionaries)
   600     retCallbacks = set()
   601     retDictionaries = set()
   602     doFindCallbacksAndDictionaries(inputTypes, retCallbacks, retDictionaries)
   603     return (retCallbacks, retDictionaries)

mercurial