Bug 1885602 - Part 5: Implement navigating to the SUMO help topic from the menu heade...
[gecko.git] / dom / bindings / Configuration.py
blobcfcb2dcf3974c4339b752b0ce9a116a4506acf7b
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 import io
6 import itertools
7 import os
8 from collections import defaultdict
10 from WebIDL import IDLIncludesStatement
12 autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
15 def toStringBool(arg):
16 """
17 Converts IDL/Python Boolean (True/False) to C++ Boolean (true/false)
18 """
19 return str(not not arg).lower()
22 class DescriptorProvider:
23 """
24 A way of getting descriptors for interface names. Subclasses must
25 have a getDescriptor method callable with the interface name only.
27 Subclasses must also have a getConfig() method that returns a
28 Configuration.
29 """
31 def __init__(self):
32 pass
35 def isChildPath(path, basePath):
36 path = os.path.normpath(path)
37 return os.path.commonprefix((path, basePath)) == basePath
40 class Configuration(DescriptorProvider):
41 """
42 Represents global configuration state based on IDL parse data and
43 the configuration file.
44 """
46 class IDLAttrGetterOrSetterTemplate:
47 class TemplateAdditionalArg:
48 def __init__(self, type, name, value=None):
49 self.type = type
50 self.name = name
51 self.value = value
53 def __init__(self, template, getter, setter, argument, attrName):
54 self.descriptor = None
55 self.usedInOtherInterfaces = False
56 self.getter = getter
57 self.setter = setter
58 self.argument = (
59 Configuration.IDLAttrGetterOrSetterTemplate.TemplateAdditionalArg(
60 *argument
63 self.attrNameString = attrName
64 self.attr = None
66 class TemplateIDLAttribute:
67 def __init__(self, attr):
68 assert attr.isAttr()
69 assert not attr.isMaplikeOrSetlikeAttr()
70 assert not attr.slotIndices
72 self.identifier = attr.identifier
73 self.type = attr.type
74 self.extendedAttributes = attr.getExtendedAttributes()
75 self.slotIndices = None
77 def getExtendedAttribute(self, name):
78 return self.extendedAttributes.get(name)
80 def isAttr(self):
81 return True
83 def isMaplikeOrSetlikeAttr(self):
84 return False
86 def isMethod(self):
87 return False
89 def isStatic(self):
90 return False
92 def __init__(self, filename, webRoots, parseData, generatedEvents=[]):
93 DescriptorProvider.__init__(self)
95 # Read the configuration file.
96 glbl = {}
97 exec(io.open(filename, encoding="utf-8").read(), glbl)
98 config = glbl["DOMInterfaces"]
100 self.attributeTemplates = dict()
101 attributeTemplatesByInterface = dict()
102 for interface, templates in glbl["TemplatedAttributes"].items():
103 for template in templates:
104 name = template.get("template")
105 t = Configuration.IDLAttrGetterOrSetterTemplate(**template)
106 self.attributeTemplates[name] = t
107 attributeTemplatesByInterface.setdefault(interface, list()).append(t)
109 webRoots = tuple(map(os.path.normpath, webRoots))
111 def isInWebIDLRoot(path):
112 return any(isChildPath(path, root) for root in webRoots)
114 # Build descriptors for all the interfaces we have in the parse data.
115 # This allows callers to specify a subset of interfaces by filtering
116 # |parseData|.
117 self.descriptors = []
118 self.interfaces = {}
119 self.descriptorsByName = {}
120 self.dictionariesByName = {}
121 self.generatedEvents = generatedEvents
122 self.maxProtoChainLength = 0
123 for thing in parseData:
124 if isinstance(thing, IDLIncludesStatement):
125 # Our build system doesn't support dep build involving
126 # addition/removal of "includes" statements that appear in a
127 # different .webidl file than their LHS interface. Make sure we
128 # don't have any of those. See similar block below for partial
129 # interfaces!
130 if thing.interface.filename != thing.filename:
131 raise TypeError(
132 "The binding build system doesn't really support "
133 "'includes' statements which don't appear in the "
134 "file in which the left-hand side of the statement is "
135 "defined.\n"
136 "%s\n"
137 "%s" % (thing.location, thing.interface.location)
140 assert not thing.isType()
142 if (
143 not thing.isInterface()
144 and not thing.isNamespace()
145 and not thing.isInterfaceMixin()
147 continue
148 # Our build system doesn't support dep builds involving
149 # addition/removal of partial interfaces/namespaces/mixins that
150 # appear in a different .webidl file than the
151 # interface/namespace/mixin they are extending. Make sure we don't
152 # have any of those. See similar block above for "includes"
153 # statements!
154 if not thing.isExternal():
155 for partial in thing.getPartials():
156 if partial.filename != thing.filename:
157 raise TypeError(
158 "The binding build system doesn't really support "
159 "partial interfaces/namespaces/mixins which don't "
160 "appear in the file in which the "
161 "interface/namespace/mixin they are extending is "
162 "defined. Don't do this.\n"
163 "%s\n"
164 "%s" % (partial.location, thing.location)
167 # The rest of the logic doesn't apply to mixins.
168 if thing.isInterfaceMixin():
169 continue
171 iface = thing
172 if not iface.isExternal():
173 if not (
174 iface.getExtendedAttribute("ChromeOnly")
175 or iface.getExtendedAttribute("Func")
176 == ["nsContentUtils::IsCallerChromeOrFuzzingEnabled"]
177 or not iface.hasInterfaceObject()
178 or isInWebIDLRoot(iface.filename)
180 raise TypeError(
181 "Interfaces which are exposed to the web may only be "
182 "defined in a DOM WebIDL root %r. Consider marking "
183 "the interface [ChromeOnly] or "
184 "[Func='nsContentUtils::IsCallerChromeOrFuzzingEnabled'] "
185 "if you do not want it exposed to the web.\n"
186 "%s" % (webRoots, iface.location)
189 self.interfaces[iface.identifier.name] = iface
191 entry = config.pop(iface.identifier.name, {})
192 assert not isinstance(entry, list)
194 desc = Descriptor(
195 self,
196 iface,
197 entry,
198 attributeTemplatesByInterface.get(iface.identifier.name),
200 self.descriptors.append(desc)
201 # Setting up descriptorsByName while iterating through interfaces
202 # means we can get the nativeType of iterable interfaces without
203 # having to do multiple loops.
204 assert desc.interface.identifier.name not in self.descriptorsByName
205 self.descriptorsByName[desc.interface.identifier.name] = desc
207 if len(config) > 0:
208 raise NoSuchDescriptorError(
209 "Bindings.conf contains entries for "
210 + str(list(config))
211 + " that aren't declared as interfaces in the .webidl files."
214 # Keep the descriptor list sorted for determinism.
215 self.descriptors.sort(key=lambda x: x.name)
217 self.descriptorsByFile = {}
218 for d in self.descriptors:
219 self.descriptorsByFile.setdefault(d.interface.filename, []).append(d)
221 self.enums = [e for e in parseData if e.isEnum()]
223 self.dictionaries = [d for d in parseData if d.isDictionary()]
224 self.dictionariesByName = {d.identifier.name: d for d in self.dictionaries}
226 self.callbacks = [
227 c for c in parseData if c.isCallback() and not c.isInterface()
230 # Dictionary mapping from a union type name to a set of filenames where
231 # union types with that name are used.
232 self.filenamesPerUnion = defaultdict(set)
234 # Dictionary mapping from a filename to a list of types for
235 # the union types used in that file. If a union type is used
236 # in multiple files then it will be added to the list for the
237 # None key. Note that the list contains a type for every use
238 # of a union type, so there can be multiple entries with union
239 # types that have the same name.
240 self.unionsPerFilename = defaultdict(list)
242 def addUnion(t):
243 filenamesForUnion = self.filenamesPerUnion[t.name]
244 if t.filename not in filenamesForUnion:
245 # We have a to be a bit careful: some of our built-in
246 # typedefs are for unions, and those unions end up with
247 # "<unknown>" as the filename. If that happens, we don't
248 # want to try associating this union with one particular
249 # filename, since there isn't one to associate it with,
250 # really.
251 if t.filename == "<unknown>":
252 uniqueFilenameForUnion = None
253 elif len(filenamesForUnion) == 0:
254 # This is the first file that we found a union with this
255 # name in, record the union as part of the file.
256 uniqueFilenameForUnion = t.filename
257 else:
258 # We already found a file that contains a union with
259 # this name.
260 if len(filenamesForUnion) == 1:
261 # This is the first time we found a union with this
262 # name in another file.
263 for f in filenamesForUnion:
264 # Filter out unions with this name from the
265 # unions for the file where we previously found
266 # them.
267 unionsForFilename = [
268 u for u in self.unionsPerFilename[f] if u.name != t.name
270 if len(unionsForFilename) == 0:
271 del self.unionsPerFilename[f]
272 else:
273 self.unionsPerFilename[f] = unionsForFilename
274 # Unions with this name appear in multiple files, record
275 # the filename as None, so that we can detect that.
276 uniqueFilenameForUnion = None
277 self.unionsPerFilename[uniqueFilenameForUnion].append(t)
278 filenamesForUnion.add(t.filename)
280 def addUnions(t):
281 t = findInnermostType(t)
282 if t.isUnion():
283 addUnion(t)
284 for m in t.flatMemberTypes:
285 addUnions(m)
287 for t, _ in getAllTypes(self.descriptors, self.dictionaries, self.callbacks):
288 addUnions(t)
290 for d in getDictionariesConvertedToJS(
291 self.descriptors, self.dictionaries, self.callbacks
293 d.needsConversionToJS = True
295 for d in getDictionariesConvertedFromJS(
296 self.descriptors, self.dictionaries, self.callbacks
298 d.needsConversionFromJS = True
300 # Collect all the global names exposed on a Window object (to implement
301 # the hash for looking up these names when resolving a property).
302 self.windowGlobalNames = []
303 for desc in self.getDescriptors(registersGlobalNamesOnWindow=True):
304 self.windowGlobalNames.append((desc.name, desc))
305 self.windowGlobalNames.extend(
306 (n.identifier.name, desc) for n in desc.interface.legacyFactoryFunctions
308 self.windowGlobalNames.extend(
309 (n, desc) for n in desc.interface.legacyWindowAliases
312 # Collect a sorted list of strings that we want to concatenate into
313 # one big string and a dict mapping each string to its offset in the
314 # concatenated string.
316 # We want the names of all the interfaces with a prototype (for
317 # implementing @@toStringTag).
318 names = set(
319 d.interface.getClassName()
320 for d in self.getDescriptors(hasInterfaceOrInterfacePrototypeObject=True)
323 # Now also add the names from windowGlobalNames, we need them for the
324 # perfect hash that we build for these.
325 names.update(n[0] for n in self.windowGlobalNames)
327 # Sorting is not strictly necessary, but makes the generated code a bit
328 # more readable.
329 names = sorted(names)
331 # We can't rely on being able to pass initial=0 to itertools.accumulate
332 # because it was only added in version 3.8, so define an accumulate
333 # function that chains the initial value into the iterator.
334 def accumulate(iterable, initial):
335 return itertools.accumulate(itertools.chain([initial], iterable))
337 # Calculate the offset of each name in the concatenated string. Note that
338 # we need to add 1 to the length to account for the null terminating each
339 # name.
340 offsets = accumulate(map(lambda n: len(n) + 1, names), initial=0)
341 self.namesStringOffsets = list(zip(names, offsets))
343 allTemplatedAttributes = (
344 (m, d)
345 for d in self.descriptors
346 if not d.interface.isExternal()
347 for m in d.interface.members
348 if m.isAttr() and m.getExtendedAttribute("BindingTemplate") is not None
350 # attributesPerTemplate will have the template names as keys, and a
351 # list of tuples as values. Every tuple contains an IDLAttribute and a
352 # descriptor.
353 attributesPerTemplate = dict()
354 for m, d in allTemplatedAttributes:
355 t = m.getExtendedAttribute("BindingTemplate")
356 if isinstance(t[0], list):
357 t = t[0]
358 l = attributesPerTemplate.setdefault(t[0], list())
359 # We want the readonly attributes last, because we use the first
360 # attribute in the list as the canonical attribute for the
361 # template, and if there are any writable attributes the
362 # template should have support for that.
363 if not m.readonly:
364 l.insert(0, (m, d))
365 else:
366 l.append((m, d))
368 for name, attributes in attributesPerTemplate.items():
369 # We use the first attribute to generate a canonical implementation
370 # of getter and setter.
371 firstAttribute, firstDescriptor = attributes[0]
372 template = self.attributeTemplates.get(name)
373 if template is None:
374 raise TypeError(
375 "Unknown BindingTemplate with name %s for %s on %s"
377 name,
378 firstAttribute.identifier.name,
379 firstDescriptor.interface.identifier.name,
383 # This mimics a real IDL attribute for templated bindings.
385 template.attr = Configuration.TemplateIDLAttribute(firstAttribute)
387 def filterExtendedAttributes(extendedAttributes):
388 # These are the extended attributes that we allow to have
389 # different values among all attributes that use the same
390 # template.
391 ignoredAttributes = {
392 "BindingTemplate",
393 "BindingAlias",
394 "ChromeOnly",
395 "Pure",
396 "Pref",
397 "Func",
398 "Throws",
399 "GetterThrows",
400 "SetterThrows",
402 return dict(
403 filter(
404 lambda i: i[0] not in ignoredAttributes,
405 extendedAttributes.items(),
409 firstExtAttrs = filterExtendedAttributes(
410 firstAttribute.getExtendedAttributes()
413 for a, d in attributes:
414 # We want to make sure all getters or setters grouped by a
415 # template have the same WebIDL signatures, so make sure
416 # their types are the same.
417 if template.attr.type != a.type:
418 raise TypeError(
419 "%s on %s and %s on %s have different type, but they're using the same template %s."
421 firstAttribute.identifier.name,
422 firstDescriptor.interface.identifier.name,
423 a.identifier.name,
424 d.interface.identifier.name,
425 name,
429 extAttrs = filterExtendedAttributes(a.getExtendedAttributes())
430 if template.attr.extendedAttributes != extAttrs:
431 for k in extAttrs.keys() - firstExtAttrs.keys():
432 raise TypeError(
433 "%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s."
435 a.identifier.name,
436 d.interface.identifier.name,
438 firstAttribute.identifier.name,
439 firstDescriptor.interface.identifier.name,
440 name,
443 for k in firstExtAttrs.keys() - extAttrs.keys():
444 raise TypeError(
445 "%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s."
447 firstAttribute.identifier.name,
448 firstDescriptor.interface.identifier.name,
450 a.identifier.name,
451 d.interface.identifier.name,
452 name,
455 for k, v in firstExtAttrs.items():
456 if extAttrs[k] != v:
457 raise TypeError(
458 "%s on %s and %s on %s have different values for extended attribute %s, but they're using the same template %s."
460 firstAttribute.identifier.name,
461 firstDescriptor.interface.identifier.name,
462 a.identifier.name,
463 d.interface.identifier.name,
465 name,
469 def sameThrows(getter=False, setter=False):
470 extAttrs1 = firstDescriptor.getExtendedAttributes(
471 firstAttribute, getter=getter, setter=setter
473 extAttrs2 = d.getExtendedAttributes(a, getter=getter, setter=setter)
474 return ("needsErrorResult" in extAttrs1) == (
475 "needsErrorResult" in extAttrs2
478 if not sameThrows(getter=True) or (
479 not a.readonly and not sameThrows(setter=True)
481 raise TypeError(
482 "%s on %s and %s on %s have different annotations about throwing, but they're using the same template %s."
484 firstAttribute.identifier.name,
485 firstDescriptor.interface.identifier.name,
486 a.identifier.name,
487 d.interface.identifier.name,
488 name,
492 for name, template in self.attributeTemplates.items():
493 if template.attr is None:
494 print("Template %s is unused, please remove it." % name)
496 def getInterface(self, ifname):
497 return self.interfaces[ifname]
499 def getDescriptors(self, **filters):
500 """Gets the descriptors that match the given filters."""
501 curr = self.descriptors
502 # Collect up our filters, because we may have a webIDLFile filter that
503 # we always want to apply first.
504 tofilter = [(lambda x: x.interface.isExternal(), False)]
505 for key, val in filters.items():
506 if key == "webIDLFile":
507 # Special-case this part to make it fast, since most of our
508 # getDescriptors calls are conditioned on a webIDLFile. We may
509 # not have this key, in which case we have no descriptors
510 # either.
511 curr = self.descriptorsByFile.get(val, [])
512 continue
513 elif key == "hasInterfaceObject":
515 def getter(x):
516 return x.interface.hasInterfaceObject()
518 elif key == "hasInterfacePrototypeObject":
520 def getter(x):
521 return x.interface.hasInterfacePrototypeObject()
523 elif key == "hasInterfaceOrInterfacePrototypeObject":
525 def getter(x):
526 return x.hasInterfaceOrInterfacePrototypeObject()
528 elif key == "isCallback":
530 def getter(x):
531 return x.interface.isCallback()
533 elif key == "isJSImplemented":
535 def getter(x):
536 return x.interface.isJSImplemented()
538 elif key == "isExposedInAnyWorker":
540 def getter(x):
541 return x.interface.isExposedInAnyWorker()
543 elif key == "isExposedInWorkerDebugger":
545 def getter(x):
546 return x.interface.isExposedInWorkerDebugger()
548 elif key == "isExposedInAnyWorklet":
550 def getter(x):
551 return x.interface.isExposedInAnyWorklet()
553 elif key == "isExposedInWindow":
555 def getter(x):
556 return x.interface.isExposedInWindow()
558 elif key == "isExposedInShadowRealms":
560 def getter(x):
561 return x.interface.isExposedInShadowRealms()
563 elif key == "isSerializable":
565 def getter(x):
566 return x.interface.isSerializable()
568 else:
569 # Have to watch out: just closing over "key" is not enough,
570 # since we're about to mutate its value
571 getter = (lambda attrName: lambda x: getattr(x, attrName))(key)
572 tofilter.append((getter, val))
573 for f in tofilter:
574 curr = [x for x in curr if f[0](x) == f[1]]
575 return curr
577 def getEnums(self, webIDLFile):
578 return [e for e in self.enums if e.filename == webIDLFile]
580 def getDictionaries(self, webIDLFile):
581 return [d for d in self.dictionaries if d.filename == webIDLFile]
583 def getCallbacks(self, webIDLFile):
584 return [c for c in self.callbacks if c.filename == webIDLFile]
586 def getDescriptor(self, interfaceName):
588 Gets the appropriate descriptor for the given interface name.
590 # We may have optimized out this descriptor, but the chances of anyone
591 # asking about it are then slim. Put the check for that _after_ we've
592 # done our normal lookup. But that means we have to do our normal
593 # lookup in a way that will not throw if it fails.
594 d = self.descriptorsByName.get(interfaceName, None)
595 if d:
596 return d
598 raise NoSuchDescriptorError("For " + interfaceName + " found no matches")
600 def getConfig(self):
601 return self
603 def getDictionariesConvertibleToJS(self):
604 return [d for d in self.dictionaries if d.needsConversionToJS]
606 def getDictionariesConvertibleFromJS(self):
607 return [d for d in self.dictionaries if d.needsConversionFromJS]
609 def getDictionaryIfExists(self, dictionaryName):
610 return self.dictionariesByName.get(dictionaryName, None)
613 class NoSuchDescriptorError(TypeError):
614 def __init__(self, str):
615 TypeError.__init__(self, str)
618 def methodReturnsJSObject(method):
619 assert method.isMethod()
621 for signature in method.signatures():
622 returnType = signature[0]
623 if returnType.isObject() or returnType.isSpiderMonkeyInterface():
624 return True
626 return False
629 def MemberIsLegacyUnforgeable(member, descriptor):
630 # Note: "or" and "and" return either their LHS or RHS, not
631 # necessarily booleans. Make sure to return a boolean from this
632 # method, because callers will compare its return value to
633 # booleans.
634 return bool(
635 (member.isAttr() or member.isMethod())
636 and not member.isStatic()
637 and (
638 member.isLegacyUnforgeable()
639 or descriptor.interface.getExtendedAttribute("LegacyUnforgeable")
644 class Descriptor(DescriptorProvider):
646 Represents a single descriptor for an interface. See Bindings.conf.
649 def __init__(self, config, interface, desc, attributeTemplates):
650 DescriptorProvider.__init__(self)
651 self.config = config
652 self.interface = interface
653 self.attributeTemplates = attributeTemplates
654 if self.attributeTemplates is not None:
655 for t in self.attributeTemplates:
656 t.descriptor = self
658 self.wantsXrays = not interface.isExternal() and interface.isExposedInWindow()
660 if self.wantsXrays:
661 # We could try to restrict self.wantsXrayExpandoClass further. For
662 # example, we could set it to false if all of our slots store
663 # Gecko-interface-typed things, because we don't use Xray expando
664 # slots for those. But note that we would need to check the types
665 # of not only the members of "interface" but also of all its
666 # ancestors, because those can have members living in our slots too.
667 # For now, do the simple thing.
668 self.wantsXrayExpandoClass = interface.totalMembersInSlots != 0
670 # Read the desc, and fill in the relevant defaults.
671 ifaceName = self.interface.identifier.name
672 # For generated iterator interfaces for other iterable interfaces, we
673 # just use IterableIterator as the native type, templated on the
674 # nativeType of the iterable interface. That way we can have a
675 # templated implementation for all the duplicated iterator
676 # functionality.
677 if self.interface.isIteratorInterface():
678 itrName = self.interface.iterableInterface.identifier.name
679 itrDesc = self.getDescriptor(itrName)
680 nativeTypeDefault = iteratorNativeType(itrDesc)
681 elif self.interface.isAsyncIteratorInterface():
682 itrName = self.interface.asyncIterableInterface.identifier.name
683 itrDesc = self.getDescriptor(itrName)
684 nativeTypeDefault = iteratorNativeType(itrDesc)
686 elif self.interface.isExternal():
687 nativeTypeDefault = "nsIDOM" + ifaceName
688 else:
689 nativeTypeDefault = "mozilla::dom::" + ifaceName
691 self.nativeType = desc.get("nativeType", nativeTypeDefault)
692 # Now create a version of nativeType that doesn't have extra
693 # mozilla::dom:: at the beginning.
694 prettyNativeType = self.nativeType.split("::")
695 if prettyNativeType[0] == "mozilla":
696 prettyNativeType.pop(0)
697 if prettyNativeType[0] == "dom":
698 prettyNativeType.pop(0)
699 self.prettyNativeType = "::".join(prettyNativeType)
701 self.jsImplParent = desc.get("jsImplParent", self.nativeType)
703 # Do something sane for JSObject
704 if self.nativeType == "JSObject":
705 headerDefault = "js/TypeDecls.h"
706 elif self.interface.isCallback() or self.interface.isJSImplemented():
707 # A copy of CGHeaders.getDeclarationFilename; we can't
708 # import it here, sadly.
709 # Use our local version of the header, not the exported one, so that
710 # test bindings, which don't export, will work correctly.
711 basename = os.path.basename(self.interface.filename)
712 headerDefault = basename.replace(".webidl", "Binding.h")
713 else:
714 if not self.interface.isExternal() and self.interface.getExtendedAttribute(
715 "HeaderFile"
717 headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0]
718 elif (
719 self.interface.isIteratorInterface()
720 or self.interface.isAsyncIteratorInterface()
722 headerDefault = "mozilla/dom/IterableIterator.h"
723 else:
724 headerDefault = self.nativeType
725 headerDefault = headerDefault.replace("::", "/") + ".h"
726 self.headerFile = desc.get("headerFile", headerDefault)
727 self.headerIsDefault = self.headerFile == headerDefault
728 if self.jsImplParent == self.nativeType:
729 self.jsImplParentHeader = self.headerFile
730 else:
731 self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h"
733 self.notflattened = desc.get("notflattened", False)
734 self.register = desc.get("register", True)
736 # If we're concrete, we need to crawl our ancestor interfaces and mark
737 # them as having a concrete descendant.
738 concreteDefault = (
739 not self.interface.isExternal()
740 and not self.interface.isCallback()
741 and not self.interface.isNamespace()
743 # We're going to assume that leaf interfaces are
744 # concrete; otherwise what's the point? Also
745 # interfaces with constructors had better be
746 # concrete; otherwise how can you construct them?
748 not self.interface.hasChildInterfaces()
749 or self.interface.ctor() is not None
753 self.concrete = desc.get("concrete", concreteDefault)
754 self.hasLegacyUnforgeableMembers = self.concrete and any(
755 MemberIsLegacyUnforgeable(m, self) for m in self.interface.members
757 self.operations = {
758 "IndexedGetter": None,
759 "IndexedSetter": None,
760 "IndexedDeleter": None,
761 "NamedGetter": None,
762 "NamedSetter": None,
763 "NamedDeleter": None,
764 "Stringifier": None,
765 "LegacyCaller": None,
768 self.hasDefaultToJSON = False
770 # Stringifiers need to be set up whether an interface is
771 # concrete or not, because they're actually prototype methods and hence
772 # can apply to instances of descendant interfaces. Legacy callers and
773 # named/indexed operations only need to be set up on concrete
774 # interfaces, since they affect the JSClass we end up using, not the
775 # prototype object.
776 def addOperation(operation, m):
777 if not self.operations[operation]:
778 self.operations[operation] = m
780 # Since stringifiers go on the prototype, we only need to worry
781 # about our own stringifier, not those of our ancestor interfaces.
782 if not self.interface.isExternal():
783 for m in self.interface.members:
784 if m.isMethod() and m.isStringifier():
785 addOperation("Stringifier", m)
786 if m.isMethod() and m.isDefaultToJSON():
787 self.hasDefaultToJSON = True
789 # We keep track of instrumente props for all non-external interfaces.
790 self.instrumentedProps = []
791 instrumentedProps = self.interface.getExtendedAttribute("InstrumentedProps")
792 if instrumentedProps:
793 # It's actually a one-element list, with the list
794 # we want as the only element.
795 self.instrumentedProps = instrumentedProps[0]
797 # Check that we don't have duplicated instrumented props.
798 uniqueInstrumentedProps = set(self.instrumentedProps)
799 if len(uniqueInstrumentedProps) != len(self.instrumentedProps):
800 duplicates = [
802 for p in uniqueInstrumentedProps
803 if self.instrumentedProps.count(p) > 1
805 raise TypeError(
806 "Duplicated instrumented properties: %s.\n%s"
807 % (duplicates, self.interface.location)
810 if self.concrete:
811 self.proxy = False
812 iface = self.interface
813 for m in iface.members:
814 # Don't worry about inheriting legacycallers either: in
815 # practice these are on most-derived prototypes.
816 if m.isMethod() and m.isLegacycaller():
817 if not m.isIdentifierLess():
818 raise TypeError(
819 "We don't support legacycaller with "
820 "identifier.\n%s" % m.location
822 if len(m.signatures()) != 1:
823 raise TypeError(
824 "We don't support overloaded "
825 "legacycaller.\n%s" % m.location
827 addOperation("LegacyCaller", m)
829 while iface:
830 for m in iface.members:
831 if not m.isMethod():
832 continue
834 def addIndexedOrNamedOperation(operation, m):
835 if m.isIndexed():
836 operation = "Indexed" + operation
837 else:
838 assert m.isNamed()
839 operation = "Named" + operation
840 addOperation(operation, m)
842 if m.isGetter():
843 addIndexedOrNamedOperation("Getter", m)
844 if m.isSetter():
845 addIndexedOrNamedOperation("Setter", m)
846 if m.isDeleter():
847 addIndexedOrNamedOperation("Deleter", m)
848 if m.isLegacycaller() and iface != self.interface:
849 raise TypeError(
850 "We don't support legacycaller on "
851 "non-leaf interface %s.\n%s" % (iface, iface.location)
854 iface.setUserData("hasConcreteDescendant", True)
855 iface = iface.parent
857 self.proxy = (
858 self.supportsIndexedProperties()
859 or (
860 self.supportsNamedProperties() and not self.hasNamedPropertiesObject
862 or self.isMaybeCrossOriginObject()
865 if self.proxy:
866 if self.isMaybeCrossOriginObject() and (
867 self.supportsIndexedProperties() or self.supportsNamedProperties()
869 raise TypeError(
870 "We don't support named or indexed "
871 "properties on maybe-cross-origin objects. "
872 "This lets us assume that their proxy "
873 "hooks are never called via Xrays. "
874 "Fix %s.\n%s" % (self.interface, self.interface.location)
877 if not self.operations["IndexedGetter"] and (
878 self.operations["IndexedSetter"]
879 or self.operations["IndexedDeleter"]
881 raise SyntaxError(
882 "%s supports indexed properties but does "
883 "not have an indexed getter.\n%s"
884 % (self.interface, self.interface.location)
886 if not self.operations["NamedGetter"] and (
887 self.operations["NamedSetter"] or self.operations["NamedDeleter"]
889 raise SyntaxError(
890 "%s supports named properties but does "
891 "not have a named getter.\n%s"
892 % (self.interface, self.interface.location)
894 iface = self.interface
895 while iface:
896 iface.setUserData("hasProxyDescendant", True)
897 iface = iface.parent
899 if desc.get("wantsQI", None) is not None:
900 self._wantsQI = desc.get("wantsQI", None)
901 self.wrapperCache = (
902 not self.interface.isCallback()
903 and not self.interface.isIteratorInterface()
904 and not self.interface.isAsyncIteratorInterface()
905 and desc.get("wrapperCache", True)
908 self.name = interface.identifier.name
910 # self.implicitJSContext is a list of names of methods and attributes
911 # that need a JSContext.
912 if self.interface.isJSImplemented():
913 self.implicitJSContext = ["constructor"]
914 else:
915 self.implicitJSContext = desc.get("implicitJSContext", [])
916 assert isinstance(self.implicitJSContext, list)
918 self._binaryNames = {}
920 if not self.interface.isExternal():
922 def maybeAddBinaryName(member):
923 binaryName = member.getExtendedAttribute("BinaryName")
924 if binaryName:
925 assert isinstance(binaryName, list)
926 assert len(binaryName) == 1
927 self._binaryNames.setdefault(
928 (member.identifier.name, member.isStatic()), binaryName[0]
931 for member in self.interface.members:
932 if not member.isAttr() and not member.isMethod():
933 continue
934 maybeAddBinaryName(member)
936 ctor = self.interface.ctor()
937 if ctor:
938 maybeAddBinaryName(ctor)
940 # Some default binary names for cases when nothing else got set.
941 self._binaryNames.setdefault(("__legacycaller", False), "LegacyCall")
942 self._binaryNames.setdefault(("__stringifier", False), "Stringify")
944 # Build the prototype chain.
945 self.prototypeChain = []
946 self.needsMissingPropUseCounters = False
947 parent = interface
948 while parent:
949 self.needsMissingPropUseCounters = (
950 self.needsMissingPropUseCounters
951 or parent.getExtendedAttribute("InstrumentedProps")
953 self.prototypeChain.insert(0, parent.identifier.name)
954 parent = parent.parent
955 config.maxProtoChainLength = max(
956 config.maxProtoChainLength, len(self.prototypeChain)
959 self.hasOrdinaryObjectPrototype = desc.get("hasOrdinaryObjectPrototype", False)
961 def binaryNameFor(self, name, isStatic):
962 return self._binaryNames.get((name, isStatic), name)
964 @property
965 def prototypeNameChain(self):
966 return [self.getDescriptor(p).name for p in self.prototypeChain]
968 @property
969 def parentPrototypeName(self):
970 if len(self.prototypeChain) == 1:
971 return None
972 return self.getDescriptor(self.prototypeChain[-2]).name
974 def hasInterfaceOrInterfacePrototypeObject(self):
975 return (
976 self.interface.hasInterfaceObject()
977 or self.interface.hasInterfacePrototypeObject()
980 @property
981 def hasNamedPropertiesObject(self):
982 return self.isGlobal() and self.supportsNamedProperties()
984 def getExtendedAttributes(self, member, getter=False, setter=False):
985 def ensureValidBoolExtendedAttribute(attr, name):
986 if attr is not None and attr is not True:
987 raise TypeError("Unknown value for '%s': %s" % (name, attr[0]))
989 def ensureValidThrowsExtendedAttribute(attr):
990 ensureValidBoolExtendedAttribute(attr, "Throws")
992 def ensureValidCanOOMExtendedAttribute(attr):
993 ensureValidBoolExtendedAttribute(attr, "CanOOM")
995 def maybeAppendNeedsErrorResultToAttrs(attrs, throws):
996 ensureValidThrowsExtendedAttribute(throws)
997 if throws is not None:
998 attrs.append("needsErrorResult")
1000 def maybeAppendCanOOMToAttrs(attrs, canOOM):
1001 ensureValidCanOOMExtendedAttribute(canOOM)
1002 if canOOM is not None:
1003 attrs.append("canOOM")
1005 def maybeAppendNeedsSubjectPrincipalToAttrs(attrs, needsSubjectPrincipal):
1006 if (
1007 needsSubjectPrincipal is not None
1008 and needsSubjectPrincipal is not True
1009 and needsSubjectPrincipal != ["NonSystem"]
1011 raise TypeError(
1012 "Unknown value for 'NeedsSubjectPrincipal': %s"
1013 % needsSubjectPrincipal[0]
1016 if needsSubjectPrincipal is not None:
1017 attrs.append("needsSubjectPrincipal")
1018 if needsSubjectPrincipal == ["NonSystem"]:
1019 attrs.append("needsNonSystemSubjectPrincipal")
1021 name = member.identifier.name
1022 throws = self.interface.isJSImplemented() or member.getExtendedAttribute(
1023 "Throws"
1025 canOOM = member.getExtendedAttribute("CanOOM")
1026 needsSubjectPrincipal = member.getExtendedAttribute("NeedsSubjectPrincipal")
1027 attrs = []
1028 if name in self.implicitJSContext:
1029 attrs.append("implicitJSContext")
1030 if member.isMethod():
1031 if self.interface.isAsyncIteratorInterface() and name == "next":
1032 attrs.append("implicitJSContext")
1033 # JSObject-returning [NewObject] methods must be fallible,
1034 # since they have to (fallibly) allocate the new JSObject.
1035 if member.getExtendedAttribute("NewObject"):
1036 if member.returnsPromise():
1037 throws = True
1038 elif methodReturnsJSObject(member):
1039 canOOM = True
1040 maybeAppendNeedsErrorResultToAttrs(attrs, throws)
1041 maybeAppendCanOOMToAttrs(attrs, canOOM)
1042 maybeAppendNeedsSubjectPrincipalToAttrs(attrs, needsSubjectPrincipal)
1043 return attrs
1045 assert member.isAttr()
1046 assert bool(getter) != bool(setter)
1047 if throws is None:
1048 throwsAttr = "GetterThrows" if getter else "SetterThrows"
1049 throws = member.getExtendedAttribute(throwsAttr)
1050 maybeAppendNeedsErrorResultToAttrs(attrs, throws)
1051 if canOOM is None:
1052 canOOMAttr = "GetterCanOOM" if getter else "SetterCanOOM"
1053 canOOM = member.getExtendedAttribute(canOOMAttr)
1054 maybeAppendCanOOMToAttrs(attrs, canOOM)
1055 if needsSubjectPrincipal is None:
1056 needsSubjectPrincipalAttr = (
1057 "GetterNeedsSubjectPrincipal"
1058 if getter
1059 else "SetterNeedsSubjectPrincipal"
1061 needsSubjectPrincipal = member.getExtendedAttribute(
1062 needsSubjectPrincipalAttr
1064 maybeAppendNeedsSubjectPrincipalToAttrs(attrs, needsSubjectPrincipal)
1065 return attrs
1067 def supportsIndexedProperties(self):
1068 return self.operations["IndexedGetter"] is not None
1070 def lengthNeedsCallerType(self):
1072 Determine whether our length getter needs a caller type; this is needed
1073 in some indexed-getter proxy algorithms. The idea is that if our
1074 indexed getter needs a caller type, our automatically-generated Length()
1075 calls need one too.
1077 assert self.supportsIndexedProperties()
1078 indexedGetter = self.operations["IndexedGetter"]
1079 return indexedGetter.getExtendedAttribute("NeedsCallerType")
1081 def supportsNamedProperties(self):
1082 return self.operations["NamedGetter"] is not None
1084 def supportedNamesNeedCallerType(self):
1086 Determine whether our GetSupportedNames call needs a caller type. The
1087 idea is that if your named getter needs a caller type, then so does
1088 GetSupportedNames.
1090 assert self.supportsNamedProperties()
1091 namedGetter = self.operations["NamedGetter"]
1092 return namedGetter.getExtendedAttribute("NeedsCallerType")
1094 def isMaybeCrossOriginObject(self):
1095 # If we're isGlobal and have cross-origin members, we're a Window, and
1096 # that's not a cross-origin object. The WindowProxy is.
1097 return (
1098 self.concrete
1099 and self.interface.hasCrossOriginMembers
1100 and not self.isGlobal()
1103 def needsHeaderInclude(self):
1105 An interface doesn't need a header file if it is not concrete, not
1106 pref-controlled, has no prototype object, has no static methods or
1107 attributes and has no parent. The parent matters because we assert
1108 things about refcounting that depend on the actual underlying type if we
1109 have a parent.
1112 return (
1113 self.interface.isExternal()
1114 or self.concrete
1115 or self.interface.hasInterfacePrototypeObject()
1116 or any(
1117 (m.isAttr() or m.isMethod()) and m.isStatic()
1118 for m in self.interface.members
1120 or self.interface.parent
1123 def hasThreadChecks(self):
1124 # isExposedConditionally does not necessarily imply thread checks
1125 # (since at least [SecureContext] is independent of them), but we're
1126 # only used to decide whether to include nsThreadUtils.h, so we don't
1127 # worry about that.
1128 return (
1129 self.isExposedConditionally() and not self.interface.isExposedInWindow()
1130 ) or self.interface.isExposedInSomeButNotAllWorkers()
1132 def hasCEReactions(self):
1133 return any(
1134 m.getExtendedAttribute("CEReactions") for m in self.interface.members
1137 def isExposedConditionally(self):
1138 return (
1139 self.interface.isExposedConditionally()
1140 or self.interface.isExposedInSomeButNotAllWorkers()
1143 def needsXrayResolveHooks(self):
1145 Generally, any interface with NeedResolve needs Xray
1146 resolveOwnProperty and enumerateOwnProperties hooks. But for
1147 the special case of plugin-loading elements, we do NOT want
1148 those, because we don't want to instantiate plug-ins simply
1149 due to chrome touching them and that's all those hooks do on
1150 those elements. So we special-case those here.
1152 return self.interface.getExtendedAttribute(
1153 "NeedResolve"
1154 ) and self.interface.identifier.name not in [
1155 "HTMLObjectElement",
1156 "HTMLEmbedElement",
1159 def needsXrayNamedDeleterHook(self):
1160 return self.operations["NamedDeleter"] is not None
1162 def isGlobal(self):
1164 Returns true if this is the primary interface for a global object
1165 of some sort.
1167 return self.interface.getExtendedAttribute("Global")
1169 @property
1170 def namedPropertiesEnumerable(self):
1172 Returns whether this interface should have enumerable named properties
1174 assert self.proxy
1175 assert self.supportsNamedProperties()
1176 iface = self.interface
1177 while iface:
1178 if iface.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
1179 return False
1180 iface = iface.parent
1181 return True
1183 @property
1184 def registersGlobalNamesOnWindow(self):
1185 return (
1186 self.interface.hasInterfaceObject()
1187 and self.interface.isExposedInWindow()
1188 and self.register
1191 def getDescriptor(self, interfaceName):
1193 Gets the appropriate descriptor for the given interface name.
1195 return self.config.getDescriptor(interfaceName)
1197 def getConfig(self):
1198 return self.config
1201 # Some utility methods
1202 def getTypesFromDescriptor(descriptor, includeArgs=True, includeReturns=True):
1204 Get argument and/or return types for all members of the descriptor. By
1205 default returns all argument types (which includes types of writable
1206 attributes) and all return types (which includes types of all attributes).
1208 assert includeArgs or includeReturns # Must want _something_.
1209 members = [m for m in descriptor.interface.members]
1210 if descriptor.interface.ctor():
1211 members.append(descriptor.interface.ctor())
1212 members.extend(descriptor.interface.legacyFactoryFunctions)
1213 signatures = [s for m in members if m.isMethod() for s in m.signatures()]
1214 types = []
1215 for s in signatures:
1216 assert len(s) == 2
1217 (returnType, arguments) = s
1218 if includeReturns:
1219 types.append(returnType)
1220 if includeArgs:
1221 types.extend(a.type for a in arguments)
1223 types.extend(
1224 a.type
1225 for a in members
1226 if (a.isAttr() and (includeReturns or (includeArgs and not a.readonly)))
1229 if descriptor.interface.maplikeOrSetlikeOrIterable:
1230 maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
1231 if maplikeOrSetlikeOrIterable.isMaplike():
1232 # The things we expand into may or may not correctly indicate in
1233 # their formal IDL types what things we have as return values. For
1234 # example, "keys" returns the moral equivalent of sequence<keyType>
1235 # but just claims to return "object". Similarly, "values" returns
1236 # the moral equivalent of sequence<valueType> but claims to return
1237 # "object". And due to bug 1155340, "get" claims to return "any"
1238 # instead of the right type. So let's just manually work around
1239 # that lack of specificity. For our arguments, we already enforce
1240 # the right types at the IDL level, so those will get picked up
1241 # correctly.
1242 assert maplikeOrSetlikeOrIterable.hasKeyType()
1243 assert maplikeOrSetlikeOrIterable.hasValueType()
1244 if includeReturns:
1245 types.append(maplikeOrSetlikeOrIterable.keyType)
1246 types.append(maplikeOrSetlikeOrIterable.valueType)
1247 elif maplikeOrSetlikeOrIterable.isSetlike():
1248 assert maplikeOrSetlikeOrIterable.hasKeyType()
1249 assert maplikeOrSetlikeOrIterable.hasValueType()
1250 assert (
1251 maplikeOrSetlikeOrIterable.keyType
1252 == maplikeOrSetlikeOrIterable.valueType
1254 # As in the maplike case, we don't always declare our return values
1255 # quite correctly.
1256 if includeReturns:
1257 types.append(maplikeOrSetlikeOrIterable.keyType)
1258 else:
1259 assert (
1260 maplikeOrSetlikeOrIterable.isIterable()
1261 or maplikeOrSetlikeOrIterable.isAsyncIterable()
1263 # As in the maplike/setlike cases we don't do a good job of
1264 # declaring our actual return types, while our argument types, if
1265 # any, are declared fine.
1266 if includeReturns:
1267 if maplikeOrSetlikeOrIterable.hasKeyType():
1268 types.append(maplikeOrSetlikeOrIterable.keyType)
1269 if maplikeOrSetlikeOrIterable.hasValueType():
1270 types.append(maplikeOrSetlikeOrIterable.valueType)
1272 return types
1275 def getTypesFromDictionary(dictionary):
1277 Get all member types for this dictionary
1279 return [m.type for m in dictionary.members]
1282 def getTypesFromCallback(callback):
1284 Get the types this callback depends on: its return type and the
1285 types of its arguments.
1287 sig = callback.signatures()[0]
1288 types = [sig[0]] # Return type
1289 types.extend(arg.type for arg in sig[1]) # Arguments
1290 return types
1293 def getAllTypes(descriptors, dictionaries, callbacks):
1295 Generate all the types we're dealing with. For each type, a tuple
1296 containing type, dictionary is yielded. The dictionary can be None if the
1297 type does not come from a dictionary.
1299 for d in descriptors:
1300 if d.interface.isExternal():
1301 continue
1302 for t in getTypesFromDescriptor(d):
1303 yield (t, None)
1304 for dictionary in dictionaries:
1305 for t in getTypesFromDictionary(dictionary):
1306 yield (t, dictionary)
1307 for callback in callbacks:
1308 for t in getTypesFromCallback(callback):
1309 yield (t, callback)
1312 # For sync value iterators, we use default array implementation, for async
1313 # iterators and sync pair iterators, we use AsyncIterableIterator or
1314 # IterableIterator instead.
1315 def iteratorNativeType(descriptor):
1316 assert descriptor.interface.isIterable() or descriptor.interface.isAsyncIterable()
1317 iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
1318 assert iterableDecl.isPairIterator() or descriptor.interface.isAsyncIterable()
1319 if descriptor.interface.isIterable():
1320 return "mozilla::dom::IterableIterator<%s>" % descriptor.nativeType
1321 needReturnMethod = toStringBool(
1322 descriptor.interface.maplikeOrSetlikeOrIterable.getExtendedAttribute(
1323 "GenerateReturnMethod"
1325 is not None
1327 return "mozilla::dom::binding_detail::AsyncIterableIteratorNative<%s, %s>" % (
1328 descriptor.nativeType,
1329 needReturnMethod,
1333 def findInnermostType(t):
1335 Find the innermost type of the given type, unwrapping Promise and Record
1336 types, as well as everything that unroll() unwraps.
1338 while True:
1339 if t.isRecord():
1340 t = t.inner
1341 elif t.unroll() != t:
1342 t = t.unroll()
1343 elif t.isPromise():
1344 t = t.promiseInnerType()
1345 else:
1346 return t
1349 def getDependentDictionariesFromDictionary(d):
1351 Find all the dictionaries contained in the given dictionary, as ancestors or
1352 members. This returns a generator.
1354 while d:
1355 yield d
1356 for member in d.members:
1357 for next in getDictionariesFromType(member.type):
1358 yield next
1359 d = d.parent
1362 def getDictionariesFromType(type):
1364 Find all the dictionaries contained in type. This can be used to find
1365 dictionaries that need conversion to JS (by looking at types that get
1366 converted to JS) or dictionaries that need conversion from JS (by looking at
1367 types that get converted from JS).
1369 This returns a generator.
1371 type = findInnermostType(type)
1372 if type.isUnion():
1373 # Look for dictionaries in all the member types
1374 for t in type.flatMemberTypes:
1375 for next in getDictionariesFromType(t):
1376 yield next
1377 elif type.isDictionary():
1378 # Find the dictionaries that are itself, any of its ancestors, or
1379 # contained in any of its member types.
1380 for d in getDependentDictionariesFromDictionary(type.inner):
1381 yield d
1384 def getDictionariesConvertedToJS(descriptors, dictionaries, callbacks):
1385 for desc in descriptors:
1386 if desc.interface.isExternal():
1387 continue
1389 if desc.interface.isJSImplemented():
1390 # For a JS-implemented interface, we need to-JS
1391 # conversions for all the types involved.
1392 for t in getTypesFromDescriptor(desc):
1393 for d in getDictionariesFromType(t):
1394 yield d
1395 elif desc.interface.isCallback():
1396 # For callbacks we only want to include the arguments, since that's
1397 # where the to-JS conversion happens.
1398 for t in getTypesFromDescriptor(desc, includeReturns=False):
1399 for d in getDictionariesFromType(t):
1400 yield d
1401 else:
1402 # For normal interfaces, we only want to include return values,
1403 # since that's where to-JS conversion happens.
1404 for t in getTypesFromDescriptor(desc, includeArgs=False):
1405 for d in getDictionariesFromType(t):
1406 yield d
1408 for callback in callbacks:
1409 # We only want to look at the arguments
1410 sig = callback.signatures()[0]
1411 for arg in sig[1]:
1412 for d in getDictionariesFromType(arg.type):
1413 yield d
1415 for dictionary in dictionaries:
1416 if dictionary.needsConversionToJS:
1417 # It's explicitly flagged as needing to-JS conversion, and all its
1418 # dependent dictionaries will need to-JS conversion too.
1419 for d in getDependentDictionariesFromDictionary(dictionary):
1420 yield d
1423 def getDictionariesConvertedFromJS(descriptors, dictionaries, callbacks):
1424 for desc in descriptors:
1425 if desc.interface.isExternal():
1426 continue
1428 if desc.interface.isJSImplemented():
1429 # For a JS-implemented interface, we need from-JS conversions for
1430 # all the types involved.
1431 for t in getTypesFromDescriptor(desc):
1432 for d in getDictionariesFromType(t):
1433 yield d
1434 elif desc.interface.isCallback():
1435 # For callbacks we only want to include the return value, since
1436 # that's where teh from-JS conversion happens.
1437 for t in getTypesFromDescriptor(desc, includeArgs=False):
1438 for d in getDictionariesFromType(t):
1439 yield d
1440 else:
1441 # For normal interfaces, we only want to include arguments values,
1442 # since that's where from-JS conversion happens.
1443 for t in getTypesFromDescriptor(desc, includeReturns=False):
1444 for d in getDictionariesFromType(t):
1445 yield d
1447 for callback in callbacks:
1448 # We only want to look at the return value
1449 sig = callback.signatures()[0]
1450 for d in getDictionariesFromType(sig[0]):
1451 yield d
1453 for dictionary in dictionaries:
1454 if dictionary.needsConversionFromJS:
1455 # It's explicitly flagged as needing from-JS conversion, and all its
1456 # dependent dictionaries will need from-JS conversion too.
1457 for d in getDependentDictionariesFromDictionary(dictionary):
1458 yield d