Bumping manifests a=b2g-bump
[gecko.git] / dom / bindings / Configuration.py
blob8e28f16b94ed5f839b8a6370b18da7c352fd6865
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, IDLImplementsStatement
6 import os
7 from collections import defaultdict
9 autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
11 class Configuration:
12 """
13 Represents global configuration state based on IDL parse data and
14 the configuration file.
15 """
16 def __init__(self, filename, parseData, generatedEvents=[]):
18 # Read the configuration file.
19 glbl = {}
20 execfile(filename, glbl)
21 config = glbl['DOMInterfaces']
23 # Build descriptors for all the interfaces we have in the parse data.
24 # This allows callers to specify a subset of interfaces by filtering
25 # |parseData|.
26 self.descriptors = []
27 self.interfaces = {}
28 self.generatedEvents = generatedEvents;
29 self.maxProtoChainLength = 0;
30 for thing in parseData:
31 if isinstance(thing, IDLImplementsStatement):
32 # Our build system doesn't support dep build involving
33 # addition/removal of "implements" statements that appear in a
34 # different .webidl file than their LHS interface. Make sure we
35 # don't have any of those.
37 # But whitelist a RHS that is LegacyQueryInterface,
38 # since people shouldn't be adding any of those.
39 if (thing.implementor.filename() != thing.filename() and
40 thing.implementee.identifier.name != "LegacyQueryInterface"):
41 raise TypeError(
42 "The binding build system doesn't really support "
43 "'implements' statements which don't appear in the "
44 "file in which the left-hand side of the statement is "
45 "defined. Don't do this unless your right-hand side "
46 "is LegacyQueryInterface.\n"
47 "%s\n"
48 "%s" %
49 (thing.location, thing.implementor.location))
50 # Some toplevel things are sadly types, and those have an
51 # isInterface that doesn't mean the same thing as IDLObject's
52 # isInterface()...
53 if (not isinstance(thing, IDLInterface) and
54 not isinstance(thing, IDLExternalInterface)):
55 continue
56 iface = thing
57 self.interfaces[iface.identifier.name] = iface
58 if iface.identifier.name not in config:
59 # Completely skip consequential interfaces with no descriptor
60 # if they have no interface object because chances are we
61 # don't need to do anything interesting with them.
62 if iface.isConsequential() and not iface.hasInterfaceObject():
63 continue
64 entry = {}
65 else:
66 entry = config[iface.identifier.name]
67 if not isinstance(entry, list):
68 assert isinstance(entry, dict)
69 entry = [entry]
70 elif len(entry) == 1:
71 if entry[0].get("workers", False):
72 # List with only a workers descriptor means we should
73 # infer a mainthread descriptor. If you want only
74 # workers bindings, don't use a list here.
75 entry.append({})
76 else:
77 raise TypeError("Don't use a single-element list for "
78 "non-worker-only interface " + iface.identifier.name +
79 " in Bindings.conf")
80 elif len(entry) == 2:
81 if entry[0].get("workers", False) == entry[1].get("workers", False):
82 raise TypeError("The two entries for interface " + iface.identifier.name +
83 " in Bindings.conf should not have the same value for 'workers'")
84 else:
85 raise TypeError("Interface " + iface.identifier.name +
86 " should have no more than two entries in Bindings.conf")
87 self.descriptors.extend([Descriptor(self, iface, x) for x in entry])
89 # Keep the descriptor list sorted for determinism.
90 self.descriptors.sort(lambda x,y: cmp(x.name, y.name))
92 self.descriptorsByName = {}
93 for d in self.descriptors:
94 self.descriptorsByName.setdefault(d.interface.identifier.name,
95 []).append(d)
97 self.descriptorsByFile = {}
98 for d in self.descriptors:
99 self.descriptorsByFile.setdefault(d.interface.filename(),
100 []).append(d)
102 self.enums = [e for e in parseData if e.isEnum()]
104 # Figure out what our main-thread and worker dictionaries and callbacks
105 # are.
106 mainTypes = set()
107 for descriptor in ([self.getDescriptor("DummyInterface", workers=False)] +
108 self.getDescriptors(workers=False, isExternal=False, skipGen=False)):
109 mainTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor)))
110 (mainCallbacks, mainDictionaries) = findCallbacksAndDictionaries(mainTypes)
112 workerTypes = set();
113 for descriptor in ([self.getDescriptor("DummyInterfaceWorkers", workers=True)] +
114 self.getDescriptors(workers=True, isExternal=False, skipGen=False)):
115 workerTypes |= set(getFlatTypes(getTypesFromDescriptor(descriptor)))
116 (workerCallbacks, workerDictionaries) = findCallbacksAndDictionaries(workerTypes)
118 self.dictionaries = [d for d in parseData if d.isDictionary()]
119 self.callbacks = [c for c in parseData if
120 c.isCallback() and not c.isInterface()]
122 # Dictionary mapping from a union type name to a set of filenames where
123 # union types with that name are used.
124 self.filenamesPerUnion = defaultdict(set)
126 # Dictionary mapping from a filename to a list of tuples containing a
127 # type and descriptor for the union types used in that file. If a union
128 # type is used in multiple files then it will be added to the list
129 # for the None key. Note that the list contains a tuple for every use of
130 # a union type, so there can be multiple tuples with union types that
131 # have the same name.
132 self.unionsPerFilename = defaultdict(list)
134 for (t, descriptor, _) in getAllTypes(self.descriptors, self.dictionaries, self.callbacks):
135 if t.isMozMap():
136 t = t.inner
137 t = t.unroll()
138 if t.isUnion():
139 filenamesForUnion = self.filenamesPerUnion[t.name]
140 if t.filename() not in filenamesForUnion:
141 if len(filenamesForUnion) == 0:
142 # This is the first file that we found a union with this
143 # name in, record the union as part of the file.
144 uniqueFilenameForUnion = t.filename()
145 else:
146 # We already found a file that contains a union with
147 # this name.
148 if len(filenamesForUnion) == 1:
149 # This is the first time we found a union with this
150 # name in another file.
151 for f in filenamesForUnion:
152 # Filter out unions with this name from the
153 # unions for the file where we previously found
154 # them.
155 unionsForFilename = self.unionsPerFilename[f]
156 unionsForFilename = filter(lambda u: u[0].name != t.name,
157 unionsForFilename)
158 if len(unionsForFilename) == 0:
159 del self.unionsPerFilename[f]
160 else:
161 self.unionsPerFilename[f] = unionsForFilename
162 # Unions with this name appear in multiple files, record
163 # the filename as None, so that we can detect that.
164 uniqueFilenameForUnion = None
165 self.unionsPerFilename[uniqueFilenameForUnion].append((t, descriptor))
166 filenamesForUnion.add(t.filename())
168 def flagWorkerOrMainThread(items, main, worker):
169 for item in items:
170 if item in main:
171 item.setUserData("mainThread", True)
172 if item in worker:
173 item.setUserData("workers", True)
174 flagWorkerOrMainThread(self.callbacks, mainCallbacks, workerCallbacks)
176 def getInterface(self, ifname):
177 return self.interfaces[ifname]
178 def getDescriptors(self, **filters):
179 """Gets the descriptors that match the given filters."""
180 curr = self.descriptors
181 # Collect up our filters, because we may have a webIDLFile filter that
182 # we always want to apply first.
183 tofilter = []
184 for key, val in filters.iteritems():
185 if key == 'webIDLFile':
186 # Special-case this part to make it fast, since most of our
187 # getDescriptors calls are conditioned on a webIDLFile. We may
188 # not have this key, in which case we have no descriptors
189 # either.
190 curr = self.descriptorsByFile.get(val, [])
191 continue
192 elif key == 'hasInterfaceObject':
193 getter = lambda x: (not x.interface.isExternal() and
194 x.interface.hasInterfaceObject())
195 elif key == 'hasInterfacePrototypeObject':
196 getter = lambda x: (not x.interface.isExternal() and
197 x.interface.hasInterfacePrototypeObject())
198 elif key == 'hasInterfaceOrInterfacePrototypeObject':
199 getter = lambda x: x.hasInterfaceOrInterfacePrototypeObject()
200 elif key == 'isCallback':
201 getter = lambda x: x.interface.isCallback()
202 elif key == 'isExternal':
203 getter = lambda x: x.interface.isExternal()
204 elif key == 'isJSImplemented':
205 getter = lambda x: x.interface.isJSImplemented()
206 elif key == 'isNavigatorProperty':
207 getter = lambda x: x.interface.getNavigatorProperty() != None
208 elif key == 'isExposedInAnyWorker':
209 getter = lambda x: (not x.interface.isExternal() and
210 x.interface.isExposedInAnyWorker())
211 elif key == 'isExposedInSystemGlobals':
212 getter = lambda x: (not x.interface.isExternal() and
213 x.interface.isExposedInSystemGlobals())
214 elif key == 'isExposedInWindow':
215 getter = lambda x: (not x.interface.isExternal() and
216 x.interface.isExposedInWindow())
217 else:
218 # Have to watch out: just closing over "key" is not enough,
219 # since we're about to mutate its value
220 getter = (lambda attrName: lambda x: getattr(x, attrName))(key)
221 tofilter.append((getter, val))
222 for f in tofilter:
223 curr = filter(lambda x: f[0](x) == f[1], curr)
224 return curr
225 def getEnums(self, webIDLFile):
226 return filter(lambda e: e.filename() == webIDLFile, self.enums)
228 @staticmethod
229 def _filterForFileAndWorkers(items, filters):
230 """Gets the items that match the given filters."""
231 for key, val in filters.iteritems():
232 if key == 'webIDLFile':
233 items = filter(lambda x: x.filename() == val, items)
234 elif key == 'workers':
235 if val:
236 items = filter(lambda x: x.getUserData("workers", False), items)
237 else:
238 items = filter(lambda x: x.getUserData("mainThread", False), items)
239 else:
240 assert(0) # Unknown key
241 return items
242 def getDictionaries(self, **filters):
243 return self._filterForFileAndWorkers(self.dictionaries, filters)
244 def getCallbacks(self, **filters):
245 return self._filterForFileAndWorkers(self.callbacks, filters)
247 def getDescriptor(self, interfaceName, workers):
249 Gets the appropriate descriptor for the given interface name
250 and the given workers boolean.
252 for d in self.descriptorsByName[interfaceName]:
253 if d.workers == workers:
254 return d
256 if workers:
257 for d in self.descriptorsByName[interfaceName]:
258 return d
260 raise NoSuchDescriptorError("For " + interfaceName + " found no matches");
261 def getDescriptorProvider(self, workers):
263 Gets a descriptor provider that can provide descriptors as needed,
264 for the given workers boolean
266 return DescriptorProvider(self, workers)
268 class NoSuchDescriptorError(TypeError):
269 def __init__(self, str):
270 TypeError.__init__(self, str)
272 class DescriptorProvider:
274 A way of getting descriptors for interface names
276 def __init__(self, config, workers):
277 self.config = config
278 self.workers = workers
280 def getDescriptor(self, interfaceName):
282 Gets the appropriate descriptor for the given interface name given the
283 context of the current descriptor. This selects the appropriate
284 implementation for cases like workers.
286 return self.config.getDescriptor(interfaceName, self.workers)
288 def methodReturnsJSObject(method):
289 assert method.isMethod()
290 if method.returnsPromise():
291 return True
293 for signature in method.signatures():
294 returnType = signature[0]
295 if returnType.isObject() or returnType.isSpiderMonkeyInterface():
296 return True
298 return False
300 class Descriptor(DescriptorProvider):
302 Represents a single descriptor for an interface. See Bindings.conf.
304 def __init__(self, config, interface, desc):
305 DescriptorProvider.__init__(self, config, desc.get('workers', False))
306 self.interface = interface
308 # Read the desc, and fill in the relevant defaults.
309 ifaceName = self.interface.identifier.name
310 if self.interface.isExternal():
311 if self.workers:
312 nativeTypeDefault = "JSObject"
313 else:
314 nativeTypeDefault = "nsIDOM" + ifaceName
315 elif self.interface.isCallback():
316 nativeTypeDefault = "mozilla::dom::" + ifaceName
317 else:
318 if self.workers:
319 nativeTypeDefault = "mozilla::dom::workers::" + ifaceName
320 else:
321 nativeTypeDefault = "mozilla::dom::" + ifaceName
323 self.nativeType = desc.get('nativeType', nativeTypeDefault)
324 # Now create a version of nativeType that doesn't have extra
325 # mozilla::dom:: at the beginning.
326 prettyNativeType = self.nativeType.split("::")
327 if prettyNativeType[0] == "mozilla":
328 prettyNativeType.pop(0)
329 if prettyNativeType[0] == "dom":
330 prettyNativeType.pop(0)
331 self.prettyNativeType = "::".join(prettyNativeType)
333 self.jsImplParent = desc.get('jsImplParent', self.nativeType)
335 # Do something sane for JSObject
336 if self.nativeType == "JSObject":
337 headerDefault = "js/TypeDecls.h"
338 elif self.interface.isCallback() or self.interface.isJSImplemented():
339 # A copy of CGHeaders.getDeclarationFilename; we can't
340 # import it here, sadly.
341 # Use our local version of the header, not the exported one, so that
342 # test bindings, which don't export, will work correctly.
343 basename = os.path.basename(self.interface.filename())
344 headerDefault = basename.replace('.webidl', 'Binding.h')
345 else:
346 if self.workers:
347 headerDefault = "mozilla/dom/workers/bindings/%s.h" % ifaceName
348 elif not self.interface.isExternal() and self.interface.getExtendedAttribute("HeaderFile"):
349 headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0]
350 else:
351 headerDefault = self.nativeType
352 headerDefault = headerDefault.replace("::", "/") + ".h"
353 self.headerFile = desc.get('headerFile', headerDefault)
354 self.headerIsDefault = self.headerFile == headerDefault
355 if self.jsImplParent == self.nativeType:
356 self.jsImplParentHeader = self.headerFile
357 else:
358 self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h"
360 self.skipGen = desc.get('skipGen', False)
362 self.notflattened = desc.get('notflattened', False)
363 self.register = desc.get('register', True)
365 self.hasXPConnectImpls = desc.get('hasXPConnectImpls', False)
367 # If we're concrete, we need to crawl our ancestor interfaces and mark
368 # them as having a concrete descendant.
369 self.concrete = (not self.interface.isExternal() and
370 not self.interface.isCallback() and
371 desc.get('concrete', True))
372 self.operations = {
373 'IndexedGetter': None,
374 'IndexedSetter': None,
375 'IndexedCreator': None,
376 'IndexedDeleter': None,
377 'NamedGetter': None,
378 'NamedSetter': None,
379 'NamedCreator': None,
380 'NamedDeleter': None,
381 'Stringifier': None,
382 'LegacyCaller': None,
383 'Jsonifier': None
385 # Stringifiers and jsonifiers need to be set up whether an interface is
386 # concrete or not, because they're actually prototype methods and hence
387 # can apply to instances of descendant interfaces. Legacy callers and
388 # named/indexed operations only need to be set up on concrete
389 # interfaces, since they affect the JSClass we end up using, not the
390 # prototype object.
391 def addOperation(operation, m):
392 if not self.operations[operation]:
393 self.operations[operation] = m
395 # Since stringifiers go on the prototype, we only need to worry
396 # about our own stringifier, not those of our ancestor interfaces.
397 if not self.interface.isExternal():
398 for m in self.interface.members:
399 if m.isMethod() and m.isStringifier():
400 addOperation('Stringifier', m)
401 if m.isMethod() and m.isJsonifier():
402 addOperation('Jsonifier', m)
404 if self.concrete:
405 self.proxy = False
406 iface = self.interface
407 for m in iface.members:
408 # Don't worry about inheriting legacycallers either: in
409 # practice these are on most-derived prototypes.
410 if m.isMethod() and m.isLegacycaller():
411 if not m.isIdentifierLess():
412 raise TypeError("We don't support legacycaller with "
413 "identifier.\n%s" % m.location);
414 if len(m.signatures()) != 1:
415 raise TypeError("We don't support overloaded "
416 "legacycaller.\n%s" % m.location)
417 addOperation('LegacyCaller', m)
418 while iface:
419 for m in iface.members:
420 if not m.isMethod():
421 continue
423 def addIndexedOrNamedOperation(operation, m):
424 if m.isIndexed():
425 operation = 'Indexed' + operation
426 else:
427 assert m.isNamed()
428 operation = 'Named' + operation
429 addOperation(operation, m)
431 if m.isGetter():
432 addIndexedOrNamedOperation('Getter', m)
433 if m.isSetter():
434 addIndexedOrNamedOperation('Setter', m)
435 if m.isCreator():
436 addIndexedOrNamedOperation('Creator', m)
437 if m.isDeleter():
438 addIndexedOrNamedOperation('Deleter', m)
439 if m.isLegacycaller() and iface != self.interface:
440 raise TypeError("We don't support legacycaller on "
441 "non-leaf interface %s.\n%s" %
442 (iface, iface.location))
444 iface.setUserData('hasConcreteDescendant', True)
445 iface = iface.parent
447 self.proxy = (self.supportsIndexedProperties() or
448 (self.supportsNamedProperties() and
449 not self.hasNamedPropertiesObject))
451 if self.proxy:
452 if (not self.operations['IndexedGetter'] and
453 (self.operations['IndexedSetter'] or
454 self.operations['IndexedDeleter'] or
455 self.operations['IndexedCreator'])):
456 raise SyntaxError("%s supports indexed properties but does "
457 "not have an indexed getter.\n%s" %
458 (self.interface, self.interface.location))
459 if (not self.operations['NamedGetter'] and
460 (self.operations['NamedSetter'] or
461 self.operations['NamedDeleter'] or
462 self.operations['NamedCreator'])):
463 raise SyntaxError("%s supports named properties but does "
464 "not have a named getter.\n%s" %
465 (self.interface, self.interface.location))
466 iface = self.interface
467 while iface:
468 iface.setUserData('hasProxyDescendant', True)
469 iface = iface.parent
471 self.nativeOwnership = desc.get('nativeOwnership', 'refcounted')
472 if not self.nativeOwnership in ('owned', 'refcounted'):
473 raise TypeError("Descriptor for %s has unrecognized value (%s) "
474 "for nativeOwnership" %
475 (self.interface.identifier.name, self.nativeOwnership))
476 if desc.get('wantsQI', None) != None:
477 self._wantsQI = desc.get('wantsQI', None)
478 self.wrapperCache = (not self.interface.isCallback() and
479 (self.nativeOwnership != 'owned' and
480 desc.get('wrapperCache', True)))
482 def make_name(name):
483 return name + "_workers" if self.workers else name
484 self.name = make_name(interface.identifier.name)
486 # self.extendedAttributes is a dict of dicts, keyed on
487 # all/getterOnly/setterOnly and then on member name. Values are an
488 # array of extended attributes.
489 self.extendedAttributes = { 'all': {}, 'getterOnly': {}, 'setterOnly': {} }
491 def addExtendedAttribute(attribute, config):
492 def add(key, members, attribute):
493 for member in members:
494 self.extendedAttributes[key].setdefault(member, []).append(attribute)
496 if isinstance(config, dict):
497 for key in ['all', 'getterOnly', 'setterOnly']:
498 add(key, config.get(key, []), attribute)
499 elif isinstance(config, list):
500 add('all', config, attribute)
501 else:
502 assert isinstance(config, str)
503 if config == '*':
504 iface = self.interface
505 while iface:
506 add('all', map(lambda m: m.name, iface.members), attribute)
507 iface = iface.parent
508 else:
509 add('all', [config], attribute)
511 if self.interface.isJSImplemented():
512 addExtendedAttribute('implicitJSContext', ['constructor'])
513 else:
514 for attribute in ['implicitJSContext']:
515 addExtendedAttribute(attribute, desc.get(attribute, {}))
517 self._binaryNames = desc.get('binaryNames', {})
518 self._binaryNames.setdefault('__legacycaller', 'LegacyCall')
519 self._binaryNames.setdefault('__stringifier', 'Stringify')
521 if not self.interface.isExternal():
522 self.permissions = dict()
524 # Adds a permission list to this descriptor and returns the index to use.
525 def addPermissions(ifaceOrMember):
526 checkPermissions = ifaceOrMember.getExtendedAttribute("CheckPermissions")
527 if checkPermissions is None:
528 return None
530 # It's a list of whitespace-separated strings
531 assert(len(checkPermissions) is 1)
532 assert(checkPermissions[0] is not None)
533 checkPermissions = checkPermissions[0]
534 permissionsList = checkPermissions.split()
535 if len(permissionsList) == 0:
536 raise TypeError("Need at least one permission name for CheckPermissions")
538 permissionsList = tuple(sorted(set(permissionsList)))
539 return self.permissions.setdefault(permissionsList, len(self.permissions))
541 self.checkPermissionsIndex = addPermissions(self.interface)
542 self.checkPermissionsIndicesForMembers = dict()
543 for m in self.interface.members:
544 permissionsIndex = addPermissions(m)
545 if permissionsIndex is not None:
546 self.checkPermissionsIndicesForMembers[m.identifier.name] = permissionsIndex
548 def isTestInterface(iface):
549 return (iface.identifier.name in ["TestInterface",
550 "TestJSImplInterface",
551 "TestRenamedInterface"])
553 self.featureDetectibleThings = set()
554 if not isTestInterface(self.interface):
555 if (self.interface.getExtendedAttribute("CheckPermissions") or
556 self.interface.getExtendedAttribute("AvailableIn") == "PrivilegedApps"):
557 if self.interface.getNavigatorProperty():
558 self.featureDetectibleThings.add("Navigator.%s" % self.interface.getNavigatorProperty())
559 else:
560 iface = self.interface.identifier.name
561 self.featureDetectibleThings.add(iface)
562 for m in self.interface.members:
563 self.featureDetectibleThings.add("%s.%s" % (iface, m.identifier.name))
565 for m in self.interface.members:
566 if (m.getExtendedAttribute("CheckPermissions") or
567 m.getExtendedAttribute("AvailableIn") == "PrivilegedApps"):
568 self.featureDetectibleThings.add("%s.%s" % (self.interface.identifier.name, m.identifier.name))
570 for member in self.interface.members:
571 if not member.isAttr() and not member.isMethod():
572 continue
573 binaryName = member.getExtendedAttribute("BinaryName")
574 if binaryName:
575 assert isinstance(binaryName, list)
576 assert len(binaryName) == 1
577 self._binaryNames.setdefault(member.identifier.name,
578 binaryName[0])
580 # Build the prototype chain.
581 self.prototypeChain = []
582 parent = interface
583 while parent:
584 self.prototypeChain.insert(0, parent.identifier.name)
585 parent = parent.parent
586 config.maxProtoChainLength = max(config.maxProtoChainLength,
587 len(self.prototypeChain))
589 def binaryNameFor(self, name):
590 return self._binaryNames.get(name, name)
592 @property
593 def prototypeNameChain(self):
594 return map(lambda p: self.getDescriptor(p).name, self.prototypeChain)
596 @property
597 def parentPrototypeName(self):
598 if len(self.prototypeChain) == 1:
599 return None
600 return self.getDescriptor(self.prototypeChain[-2]).name
602 def hasInterfaceOrInterfacePrototypeObject(self):
604 # Forward-declared interfaces don't need either interface object or
605 # interface prototype object as they're going to use QI (on main thread)
606 # or be passed as a JSObject (on worker threads).
607 if self.interface.isExternal():
608 return False
610 return self.interface.hasInterfaceObject() or self.interface.hasInterfacePrototypeObject()
612 @property
613 def hasNamedPropertiesObject(self):
614 if self.interface.isExternal():
615 return False
617 return self.isGlobal() and self.supportsNamedProperties()
619 def getExtendedAttributes(self, member, getter=False, setter=False):
620 def ensureValidThrowsExtendedAttribute(attr):
621 assert(attr is None or attr is True or len(attr) == 1)
622 if (attr is not None and attr is not True and
623 'Workers' not in attr and 'MainThread' not in attr):
624 raise TypeError("Unknown value for 'Throws': " + attr[0])
626 def maybeAppendInfallibleToAttrs(attrs, throws):
627 ensureValidThrowsExtendedAttribute(throws)
628 if (throws is None or
629 (throws is not True and
630 ('Workers' not in throws or not self.workers) and
631 ('MainThread' not in throws or self.workers))):
632 attrs.append("infallible")
634 name = member.identifier.name
635 throws = self.interface.isJSImplemented() or member.getExtendedAttribute("Throws")
636 if member.isMethod():
637 # JSObject-returning [NewObject] methods must be fallible,
638 # since they have to (fallibly) allocate the new JSObject.
639 if (member.getExtendedAttribute("NewObject") and
640 methodReturnsJSObject(member)):
641 throws = True
642 attrs = self.extendedAttributes['all'].get(name, [])
643 maybeAppendInfallibleToAttrs(attrs, throws)
644 return attrs
646 assert member.isAttr()
647 assert bool(getter) != bool(setter)
648 key = 'getterOnly' if getter else 'setterOnly'
649 attrs = self.extendedAttributes['all'].get(name, []) + self.extendedAttributes[key].get(name, [])
650 if throws is None:
651 throwsAttr = "GetterThrows" if getter else "SetterThrows"
652 throws = member.getExtendedAttribute(throwsAttr)
653 maybeAppendInfallibleToAttrs(attrs, throws)
654 return attrs
656 def supportsIndexedProperties(self):
657 return self.operations['IndexedGetter'] is not None
659 def supportsNamedProperties(self):
660 return self.operations['NamedGetter'] is not None
662 def needsConstructHookHolder(self):
663 assert self.interface.hasInterfaceObject()
664 return False
666 def needsHeaderInclude(self):
668 An interface doesn't need a header file if it is not concrete,
669 not pref-controlled, has no prototype object, and has no
670 static methods or attributes.
672 return (self.interface.isExternal() or self.concrete or
673 self.interface.hasInterfacePrototypeObject() or
674 any((m.isAttr() or m.isMethod()) and m.isStatic() for m
675 in self.interface.members))
677 def hasThreadChecks(self):
678 return ((self.isExposedConditionally() and
679 not self.interface.isExposedInWindow()) or
680 self.interface.isExposedInSomeButNotAllWorkers())
682 def isExposedConditionally(self):
683 return (self.interface.isExposedConditionally() or
684 self.interface.isExposedInSomeButNotAllWorkers())
686 def needsXrayResolveHooks(self):
688 Generally, any interface with NeedResolve needs Xray
689 resolveOwnProperty and enumerateOwnProperties hooks. But for
690 the special case of plugin-loading elements, we do NOT want
691 those, because we don't want to instantiate plug-ins simply
692 due to chrome touching them and that's all those hooks do on
693 those elements. So we special-case those here.
695 return (self.interface.getExtendedAttribute("NeedResolve") and
696 self.interface.identifier.name not in ["HTMLObjectElement",
697 "HTMLEmbedElement",
698 "HTMLAppletElement"])
700 def needsSpecialGenericOps(self):
702 Returns true if this descriptor requires generic ops other than
703 GenericBindingMethod/GenericBindingGetter/GenericBindingSetter.
705 In practice we need to do this if our this value might be an XPConnect
706 object or if we need to coerce null/undefined to the global.
708 return self.hasXPConnectImpls or self.interface.isOnGlobalProtoChain()
710 def isGlobal(self):
712 Returns true if this is the primary interface for a global object
713 of some sort.
715 return (self.interface.getExtendedAttribute("Global") or
716 self.interface.getExtendedAttribute("PrimaryGlobal"))
718 # Some utility methods
719 def getTypesFromDescriptor(descriptor):
721 Get all argument and return types for all members of the descriptor
723 members = [m for m in descriptor.interface.members]
724 if descriptor.interface.ctor():
725 members.append(descriptor.interface.ctor())
726 members.extend(descriptor.interface.namedConstructors)
727 signatures = [s for m in members if m.isMethod() for s in m.signatures()]
728 types = []
729 for s in signatures:
730 assert len(s) == 2
731 (returnType, arguments) = s
732 types.append(returnType)
733 types.extend(a.type for a in arguments)
735 types.extend(a.type for a in members if a.isAttr())
736 return types
738 def getFlatTypes(types):
739 retval = set()
740 for type in types:
741 type = type.unroll()
742 if type.isUnion():
743 retval |= set(type.flatMemberTypes)
744 else:
745 retval.add(type)
746 return retval
748 def getTypesFromDictionary(dictionary):
750 Get all member types for this dictionary
752 types = []
753 curDict = dictionary
754 while curDict:
755 types.extend([m.type for m in curDict.members])
756 curDict = curDict.parent
757 return types
759 def getTypesFromCallback(callback):
761 Get the types this callback depends on: its return type and the
762 types of its arguments.
764 sig = callback.signatures()[0]
765 types = [sig[0]] # Return type
766 types.extend(arg.type for arg in sig[1]) # Arguments
767 return types
769 def findCallbacksAndDictionaries(inputTypes):
771 Ensure that all callbacks and dictionaries reachable from types end up in
772 the returned callbacks and dictionaries sets.
774 Note that we assume that our initial invocation already includes all types
775 reachable via descriptors in "types", so we only have to deal with things
776 that are themeselves reachable via callbacks and dictionaries.
778 def doFindCallbacksAndDictionaries(types, callbacks, dictionaries):
779 unhandledTypes = set()
780 for type in types:
781 if type.isCallback() and type not in callbacks:
782 unhandledTypes |= getFlatTypes(getTypesFromCallback(type))
783 callbacks.add(type)
784 elif type.isDictionary() and type.inner not in dictionaries:
785 d = type.inner
786 unhandledTypes |= getFlatTypes(getTypesFromDictionary(d))
787 while d:
788 dictionaries.add(d)
789 d = d.parent
790 if len(unhandledTypes) != 0:
791 doFindCallbacksAndDictionaries(unhandledTypes, callbacks, dictionaries)
793 retCallbacks = set()
794 retDictionaries = set()
795 doFindCallbacksAndDictionaries(inputTypes, retCallbacks, retDictionaries)
796 return (retCallbacks, retDictionaries)
798 def getAllTypes(descriptors, dictionaries, callbacks):
800 Generate all the types we're dealing with. For each type, a tuple
801 containing type, descriptor, dictionary is yielded. The
802 descriptor and dictionary can be None if the type does not come
803 from a descriptor or dictionary; they will never both be non-None.
805 for d in descriptors:
806 if d.interface.isExternal():
807 continue
808 for t in getTypesFromDescriptor(d):
809 yield (t, d, None)
810 for dictionary in dictionaries:
811 for t in getTypesFromDictionary(dictionary):
812 yield (t, None, dictionary)
813 for callback in callbacks:
814 for t in getTypesFromCallback(callback):
815 yield (t, None, None)