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.

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

mercurial