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/.
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
):
17 Converts IDL/Python Boolean (True/False) to C++ Boolean (true/false)
19 return str(not not arg
).lower()
22 class DescriptorProvider
:
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
35 def isChildPath(path
, basePath
):
36 path
= os
.path
.normpath(path
)
37 return os
.path
.commonprefix((path
, basePath
)) == basePath
40 class Configuration(DescriptorProvider
):
42 Represents global configuration state based on IDL parse data and
43 the configuration file.
46 class IDLAttrGetterOrSetterTemplate
:
47 class TemplateAdditionalArg
:
48 def __init__(self
, type, name
, value
=None):
53 def __init__(self
, template
, getter
, setter
, argument
, attrName
):
54 self
.descriptor
= None
55 self
.usedInOtherInterfaces
= False
59 Configuration
.IDLAttrGetterOrSetterTemplate
.TemplateAdditionalArg(
63 self
.attrNameString
= attrName
66 class TemplateIDLAttribute
:
67 def __init__(self
, attr
):
69 assert not attr
.isMaplikeOrSetlikeAttr()
70 assert not attr
.slotIndices
72 self
.identifier
= attr
.identifier
74 self
.extendedAttributes
= attr
.getExtendedAttributes()
75 self
.slotIndices
= None
77 def getExtendedAttribute(self
, name
):
78 return self
.extendedAttributes
.get(name
)
83 def isMaplikeOrSetlikeAttr(self
):
92 def __init__(self
, filename
, webRoots
, parseData
, generatedEvents
=[]):
93 DescriptorProvider
.__init
__(self
)
95 # Read the configuration file.
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
117 self
.descriptors
= []
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
130 if thing
.interface
.filename
!= thing
.filename
:
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 "
137 "%s" % (thing
.location
, thing
.interface
.location
)
140 assert not thing
.isType()
143 not thing
.isInterface()
144 and not thing
.isNamespace()
145 and not thing
.isInterfaceMixin()
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"
154 if not thing
.isExternal():
155 for partial
in thing
.getPartials():
156 if partial
.filename
!= thing
.filename
:
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"
164 "%s" % (partial
.location
, thing
.location
)
167 # The rest of the logic doesn't apply to mixins.
168 if thing
.isInterfaceMixin():
172 if not iface
.isExternal():
174 iface
.getExtendedAttribute("ChromeOnly")
175 or iface
.getExtendedAttribute("Func")
176 == ["nsContentUtils::IsCallerChromeOrFuzzingEnabled"]
177 or not iface
.hasInterfaceObject()
178 or isInWebIDLRoot(iface
.filename
)
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)
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
208 raise NoSuchDescriptorError(
209 "Bindings.conf contains entries for "
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
}
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)
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,
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
258 # We already found a file that contains a union with
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
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
]
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
)
281 t
= findInnermostType(t
)
284 for m
in t
.flatMemberTypes
:
287 for t
, _
in getAllTypes(self
.descriptors
, self
.dictionaries
, self
.callbacks
):
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).
319 d
.interface
.getClassName()
320 for d
in self
.getDescriptors(hasInterfaceOrInterfacePrototypeObject
=True)
323 d
.interface
.getClassName()
324 for d
in self
.getDescriptors(hasOrdinaryObjectPrototype
=True)
327 # Now also add the names from windowGlobalNames, we need them for the
328 # perfect hash that we build for these.
329 names
.update(n
[0] for n
in self
.windowGlobalNames
)
331 # Sorting is not strictly necessary, but makes the generated code a bit
333 names
= sorted(names
)
335 # We can't rely on being able to pass initial=0 to itertools.accumulate
336 # because it was only added in version 3.8, so define an accumulate
337 # function that chains the initial value into the iterator.
338 def accumulate(iterable
, initial
):
339 return itertools
.accumulate(itertools
.chain([initial
], iterable
))
341 # Calculate the offset of each name in the concatenated string. Note that
342 # we need to add 1 to the length to account for the null terminating each
344 offsets
= accumulate(map(lambda n
: len(n
) + 1, names
), initial
=0)
345 self
.namesStringOffsets
= list(zip(names
, offsets
))
347 allTemplatedAttributes
= (
349 for d
in self
.descriptors
350 if not d
.interface
.isExternal()
351 for m
in d
.interface
.members
352 if m
.isAttr() and m
.getExtendedAttribute("BindingTemplate") is not None
354 # attributesPerTemplate will have the template names as keys, and a
355 # list of tuples as values. Every tuple contains an IDLAttribute and a
357 attributesPerTemplate
= dict()
358 for m
, d
in allTemplatedAttributes
:
359 t
= m
.getExtendedAttribute("BindingTemplate")
360 if isinstance(t
[0], list):
362 l
= attributesPerTemplate
.setdefault(t
[0], list())
363 # We want the readonly attributes last, because we use the first
364 # attribute in the list as the canonical attribute for the
365 # template, and if there are any writable attributes the
366 # template should have support for that.
372 for name
, attributes
in attributesPerTemplate
.items():
373 # We use the first attribute to generate a canonical implementation
374 # of getter and setter.
375 firstAttribute
, firstDescriptor
= attributes
[0]
376 template
= self
.attributeTemplates
.get(name
)
379 "Unknown BindingTemplate with name %s for %s on %s"
382 firstAttribute
.identifier
.name
,
383 firstDescriptor
.interface
.identifier
.name
,
387 # This mimics a real IDL attribute for templated bindings.
389 template
.attr
= Configuration
.TemplateIDLAttribute(firstAttribute
)
391 def filterExtendedAttributes(extendedAttributes
):
392 # These are the extended attributes that we allow to have
393 # different values among all attributes that use the same
395 ignoredAttributes
= {
408 lambda i
: i
[0] not in ignoredAttributes
,
409 extendedAttributes
.items(),
413 firstExtAttrs
= filterExtendedAttributes(
414 firstAttribute
.getExtendedAttributes()
417 for a
, d
in attributes
:
418 # We want to make sure all getters or setters grouped by a
419 # template have the same WebIDL signatures, so make sure
420 # their types are the same.
421 if template
.attr
.type != a
.type:
423 "%s on %s and %s on %s have different type, but they're using the same template %s."
425 firstAttribute
.identifier
.name
,
426 firstDescriptor
.interface
.identifier
.name
,
428 d
.interface
.identifier
.name
,
433 extAttrs
= filterExtendedAttributes(a
.getExtendedAttributes())
434 if template
.attr
.extendedAttributes
!= extAttrs
:
435 for k
in extAttrs
.keys() - firstExtAttrs
.keys():
437 "%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s."
440 d
.interface
.identifier
.name
,
442 firstAttribute
.identifier
.name
,
443 firstDescriptor
.interface
.identifier
.name
,
447 for k
in firstExtAttrs
.keys() - extAttrs
.keys():
449 "%s on %s has extended attribute %s and %s on %s does not, but they're using the same template %s."
451 firstAttribute
.identifier
.name
,
452 firstDescriptor
.interface
.identifier
.name
,
455 d
.interface
.identifier
.name
,
459 for k
, v
in firstExtAttrs
.items():
462 "%s on %s and %s on %s have different values for extended attribute %s, but they're using the same template %s."
464 firstAttribute
.identifier
.name
,
465 firstDescriptor
.interface
.identifier
.name
,
467 d
.interface
.identifier
.name
,
473 def sameThrows(getter
=False, setter
=False):
474 extAttrs1
= firstDescriptor
.getExtendedAttributes(
475 firstAttribute
, getter
=getter
, setter
=setter
477 extAttrs2
= d
.getExtendedAttributes(a
, getter
=getter
, setter
=setter
)
478 return ("needsErrorResult" in extAttrs1
) == (
479 "needsErrorResult" in extAttrs2
482 if not sameThrows(getter
=True) or (
483 not a
.readonly
and not sameThrows(setter
=True)
486 "%s on %s and %s on %s have different annotations about throwing, but they're using the same template %s."
488 firstAttribute
.identifier
.name
,
489 firstDescriptor
.interface
.identifier
.name
,
491 d
.interface
.identifier
.name
,
496 for name
, template
in self
.attributeTemplates
.items():
497 if template
.attr
is None:
498 print("Template %s is unused, please remove it." % name
)
500 def getInterface(self
, ifname
):
501 return self
.interfaces
[ifname
]
503 def getDescriptors(self
, **filters
):
504 """Gets the descriptors that match the given filters."""
505 curr
= self
.descriptors
506 # Collect up our filters, because we may have a webIDLFile filter that
507 # we always want to apply first.
508 tofilter
= [(lambda x
: x
.interface
.isExternal(), False)]
509 for key
, val
in filters
.items():
510 if key
== "webIDLFile":
511 # Special-case this part to make it fast, since most of our
512 # getDescriptors calls are conditioned on a webIDLFile. We may
513 # not have this key, in which case we have no descriptors
515 curr
= self
.descriptorsByFile
.get(val
, [])
517 elif key
== "hasInterfaceObject":
520 return x
.interface
.hasInterfaceObject()
522 elif key
== "hasInterfacePrototypeObject":
525 return x
.interface
.hasInterfacePrototypeObject()
527 elif key
== "hasInterfaceOrInterfacePrototypeObject":
530 return x
.hasInterfaceOrInterfacePrototypeObject()
532 elif key
== "hasOrdinaryObjectPrototype":
535 return x
.hasOrdinaryObjectPrototype()
537 elif key
== "isCallback":
540 return x
.interface
.isCallback()
542 elif key
== "isJSImplemented":
545 return x
.interface
.isJSImplemented()
547 elif key
== "isExposedInAnyWorker":
550 return x
.interface
.isExposedInAnyWorker()
552 elif key
== "isExposedInWorkerDebugger":
555 return x
.interface
.isExposedInWorkerDebugger()
557 elif key
== "isExposedInAnyWorklet":
560 return x
.interface
.isExposedInAnyWorklet()
562 elif key
== "isExposedInWindow":
565 return x
.interface
.isExposedInWindow()
567 elif key
== "isExposedInShadowRealms":
570 return x
.interface
.isExposedInShadowRealms()
572 elif key
== "isSerializable":
575 return x
.interface
.isSerializable()
578 # Have to watch out: just closing over "key" is not enough,
579 # since we're about to mutate its value
580 getter
= (lambda attrName
: lambda x
: getattr(x
, attrName
))(key
)
581 tofilter
.append((getter
, val
))
583 curr
= [x
for x
in curr
if f
[0](x
) == f
[1]]
586 def getEnums(self
, webIDLFile
):
587 return [e
for e
in self
.enums
if e
.filename
== webIDLFile
]
589 def getDictionaries(self
, webIDLFile
):
590 return [d
for d
in self
.dictionaries
if d
.filename
== webIDLFile
]
592 def getCallbacks(self
, webIDLFile
):
593 return [c
for c
in self
.callbacks
if c
.filename
== webIDLFile
]
595 def getDescriptor(self
, interfaceName
):
597 Gets the appropriate descriptor for the given interface name.
599 # We may have optimized out this descriptor, but the chances of anyone
600 # asking about it are then slim. Put the check for that _after_ we've
601 # done our normal lookup. But that means we have to do our normal
602 # lookup in a way that will not throw if it fails.
603 d
= self
.descriptorsByName
.get(interfaceName
, None)
607 raise NoSuchDescriptorError("For " + interfaceName
+ " found no matches")
612 def getDictionariesConvertibleToJS(self
):
613 return [d
for d
in self
.dictionaries
if d
.needsConversionToJS
]
615 def getDictionariesConvertibleFromJS(self
):
616 return [d
for d
in self
.dictionaries
if d
.needsConversionFromJS
]
618 def getDictionaryIfExists(self
, dictionaryName
):
619 return self
.dictionariesByName
.get(dictionaryName
, None)
622 class NoSuchDescriptorError(TypeError):
623 def __init__(self
, str):
624 TypeError.__init
__(self
, str)
627 def methodReturnsJSObject(method
):
628 assert method
.isMethod()
630 for signature
in method
.signatures():
631 returnType
= signature
[0]
632 if returnType
.isObject() or returnType
.isSpiderMonkeyInterface():
638 def MemberIsLegacyUnforgeable(member
, descriptor
):
639 # Note: "or" and "and" return either their LHS or RHS, not
640 # necessarily booleans. Make sure to return a boolean from this
641 # method, because callers will compare its return value to
644 (member
.isAttr() or member
.isMethod())
645 and not member
.isStatic()
647 member
.isLegacyUnforgeable()
648 or descriptor
.interface
.getExtendedAttribute("LegacyUnforgeable")
653 class Descriptor(DescriptorProvider
):
655 Represents a single descriptor for an interface. See Bindings.conf.
658 def __init__(self
, config
, interface
, desc
, attributeTemplates
):
659 DescriptorProvider
.__init
__(self
)
661 self
.interface
= interface
662 self
.attributeTemplates
= attributeTemplates
663 if self
.attributeTemplates
is not None:
664 for t
in self
.attributeTemplates
:
667 self
.wantsXrays
= not interface
.isExternal() and interface
.isExposedInWindow()
670 # We could try to restrict self.wantsXrayExpandoClass further. For
671 # example, we could set it to false if all of our slots store
672 # Gecko-interface-typed things, because we don't use Xray expando
673 # slots for those. But note that we would need to check the types
674 # of not only the members of "interface" but also of all its
675 # ancestors, because those can have members living in our slots too.
676 # For now, do the simple thing.
677 self
.wantsXrayExpandoClass
= interface
.totalMembersInSlots
!= 0
679 # Read the desc, and fill in the relevant defaults.
680 ifaceName
= self
.interface
.identifier
.name
681 # For generated iterator interfaces for other iterable interfaces, we
682 # just use IterableIterator as the native type, templated on the
683 # nativeType of the iterable interface. That way we can have a
684 # templated implementation for all the duplicated iterator
686 if self
.interface
.isIteratorInterface():
687 itrName
= self
.interface
.iterableInterface
.identifier
.name
688 itrDesc
= self
.getDescriptor(itrName
)
689 nativeTypeDefault
= iteratorNativeType(itrDesc
)
690 elif self
.interface
.isAsyncIteratorInterface():
691 itrName
= self
.interface
.asyncIterableInterface
.identifier
.name
692 itrDesc
= self
.getDescriptor(itrName
)
693 nativeTypeDefault
= iteratorNativeType(itrDesc
)
695 elif self
.interface
.isExternal():
696 nativeTypeDefault
= "nsIDOM" + ifaceName
698 nativeTypeDefault
= "mozilla::dom::" + ifaceName
700 self
.nativeType
= desc
.get("nativeType", nativeTypeDefault
)
701 # Now create a version of nativeType that doesn't have extra
702 # mozilla::dom:: at the beginning.
703 prettyNativeType
= self
.nativeType
.split("::")
704 if prettyNativeType
[0] == "mozilla":
705 prettyNativeType
.pop(0)
706 if prettyNativeType
[0] == "dom":
707 prettyNativeType
.pop(0)
708 self
.prettyNativeType
= "::".join(prettyNativeType
)
710 self
.jsImplParent
= desc
.get("jsImplParent", self
.nativeType
)
712 # Do something sane for JSObject
713 if self
.nativeType
== "JSObject":
714 headerDefault
= "js/TypeDecls.h"
715 elif self
.interface
.isCallback() or self
.interface
.isJSImplemented():
716 # A copy of CGHeaders.getDeclarationFilename; we can't
717 # import it here, sadly.
718 # Use our local version of the header, not the exported one, so that
719 # test bindings, which don't export, will work correctly.
720 basename
= os
.path
.basename(self
.interface
.filename
)
721 headerDefault
= basename
.replace(".webidl", "Binding.h")
723 if not self
.interface
.isExternal() and self
.interface
.getExtendedAttribute(
726 headerDefault
= self
.interface
.getExtendedAttribute("HeaderFile")[0]
728 self
.interface
.isIteratorInterface()
729 or self
.interface
.isAsyncIteratorInterface()
731 headerDefault
= "mozilla/dom/IterableIterator.h"
733 headerDefault
= self
.nativeType
734 headerDefault
= headerDefault
.replace("::", "/") + ".h"
735 self
.headerFile
= desc
.get("headerFile", headerDefault
)
736 self
.headerIsDefault
= self
.headerFile
== headerDefault
737 if self
.jsImplParent
== self
.nativeType
:
738 self
.jsImplParentHeader
= self
.headerFile
740 self
.jsImplParentHeader
= self
.jsImplParent
.replace("::", "/") + ".h"
742 self
.notflattened
= desc
.get("notflattened", False)
743 self
.register
= desc
.get("register", True)
745 # If we're concrete, we need to crawl our ancestor interfaces and mark
746 # them as having a concrete descendant.
748 not self
.interface
.isExternal()
749 and not self
.interface
.isCallback()
750 and not self
.interface
.isNamespace()
752 # We're going to assume that leaf interfaces are
753 # concrete; otherwise what's the point? Also
754 # interfaces with constructors had better be
755 # concrete; otherwise how can you construct them?
757 not self
.interface
.hasChildInterfaces()
758 or self
.interface
.ctor() is not None
762 self
.concrete
= desc
.get("concrete", concreteDefault
)
763 self
.hasLegacyUnforgeableMembers
= self
.concrete
and any(
764 MemberIsLegacyUnforgeable(m
, self
) for m
in self
.interface
.members
767 "IndexedGetter": None,
768 "IndexedSetter": None,
769 "IndexedDeleter": None,
772 "NamedDeleter": None,
774 "LegacyCaller": None,
777 self
.hasDefaultToJSON
= False
779 # Stringifiers need to be set up whether an interface is
780 # concrete or not, because they're actually prototype methods and hence
781 # can apply to instances of descendant interfaces. Legacy callers and
782 # named/indexed operations only need to be set up on concrete
783 # interfaces, since they affect the JSClass we end up using, not the
785 def addOperation(operation
, m
):
786 if not self
.operations
[operation
]:
787 self
.operations
[operation
] = m
789 # Since stringifiers go on the prototype, we only need to worry
790 # about our own stringifier, not those of our ancestor interfaces.
791 if not self
.interface
.isExternal():
792 for m
in self
.interface
.members
:
793 if m
.isMethod() and m
.isStringifier():
794 addOperation("Stringifier", m
)
795 if m
.isMethod() and m
.isDefaultToJSON():
796 self
.hasDefaultToJSON
= True
798 # We keep track of instrumente props for all non-external interfaces.
799 self
.instrumentedProps
= []
800 instrumentedProps
= self
.interface
.getExtendedAttribute("InstrumentedProps")
801 if instrumentedProps
:
802 # It's actually a one-element list, with the list
803 # we want as the only element.
804 self
.instrumentedProps
= instrumentedProps
[0]
806 # Check that we don't have duplicated instrumented props.
807 uniqueInstrumentedProps
= set(self
.instrumentedProps
)
808 if len(uniqueInstrumentedProps
) != len(self
.instrumentedProps
):
811 for p
in uniqueInstrumentedProps
812 if self
.instrumentedProps
.count(p
) > 1
815 "Duplicated instrumented properties: %s.\n%s"
816 % (duplicates
, self
.interface
.location
)
821 iface
= self
.interface
822 for m
in iface
.members
:
823 # Don't worry about inheriting legacycallers either: in
824 # practice these are on most-derived prototypes.
825 if m
.isMethod() and m
.isLegacycaller():
826 if not m
.isIdentifierLess():
828 "We don't support legacycaller with "
829 "identifier.\n%s" % m
.location
831 if len(m
.signatures()) != 1:
833 "We don't support overloaded "
834 "legacycaller.\n%s" % m
.location
836 addOperation("LegacyCaller", m
)
838 if desc
.get("hasOrdinaryObjectPrototype", False):
839 iface
.setUserData("hasOrdinaryObjectPrototype", True)
842 for m
in iface
.members
:
846 def addIndexedOrNamedOperation(operation
, m
):
848 operation
= "Indexed" + operation
851 operation
= "Named" + operation
852 addOperation(operation
, m
)
855 addIndexedOrNamedOperation("Getter", m
)
857 addIndexedOrNamedOperation("Setter", m
)
859 addIndexedOrNamedOperation("Deleter", m
)
860 if m
.isLegacycaller() and iface
!= self
.interface
:
862 "We don't support legacycaller on "
863 "non-leaf interface %s.\n%s" % (iface
, iface
.location
)
866 iface
.setUserData("hasConcreteDescendant", True)
870 self
.supportsIndexedProperties()
872 self
.supportsNamedProperties() and not self
.hasNamedPropertiesObject
874 or self
.isMaybeCrossOriginObject()
878 if self
.isMaybeCrossOriginObject() and (
879 self
.supportsIndexedProperties() or self
.supportsNamedProperties()
882 "We don't support named or indexed "
883 "properties on maybe-cross-origin objects. "
884 "This lets us assume that their proxy "
885 "hooks are never called via Xrays. "
886 "Fix %s.\n%s" % (self
.interface
, self
.interface
.location
)
889 if not self
.operations
["IndexedGetter"] and (
890 self
.operations
["IndexedSetter"]
891 or self
.operations
["IndexedDeleter"]
894 "%s supports indexed properties but does "
895 "not have an indexed getter.\n%s"
896 % (self
.interface
, self
.interface
.location
)
898 if not self
.operations
["NamedGetter"] and (
899 self
.operations
["NamedSetter"] or self
.operations
["NamedDeleter"]
902 "%s supports named properties but does "
903 "not have a named getter.\n%s"
904 % (self
.interface
, self
.interface
.location
)
906 iface
= self
.interface
908 iface
.setUserData("hasProxyDescendant", True)
911 if desc
.get("wantsQI", None) is not None:
912 self
._wantsQI
= desc
.get("wantsQI", None)
913 self
.wrapperCache
= (
914 not self
.interface
.isCallback()
915 and not self
.interface
.isIteratorInterface()
916 and not self
.interface
.isAsyncIteratorInterface()
917 and desc
.get("wrapperCache", True)
920 self
.name
= interface
.identifier
.name
922 # self.implicitJSContext is a list of names of methods and attributes
923 # that need a JSContext.
924 if self
.interface
.isJSImplemented():
925 self
.implicitJSContext
= ["constructor"]
927 self
.implicitJSContext
= desc
.get("implicitJSContext", [])
928 assert isinstance(self
.implicitJSContext
, list)
930 self
._binaryNames
= {}
932 if not self
.interface
.isExternal():
934 def maybeAddBinaryName(member
):
935 binaryName
= member
.getExtendedAttribute("BinaryName")
937 assert isinstance(binaryName
, list)
938 assert len(binaryName
) == 1
939 self
._binaryNames
.setdefault(
940 (member
.identifier
.name
, member
.isStatic()), binaryName
[0]
943 for member
in self
.interface
.members
:
944 if not member
.isAttr() and not member
.isMethod():
946 maybeAddBinaryName(member
)
948 ctor
= self
.interface
.ctor()
950 maybeAddBinaryName(ctor
)
952 # Some default binary names for cases when nothing else got set.
953 self
._binaryNames
.setdefault(("__legacycaller", False), "LegacyCall")
954 self
._binaryNames
.setdefault(("__stringifier", False), "Stringify")
956 # Build the prototype chain.
957 self
.prototypeChain
= []
958 self
.needsMissingPropUseCounters
= False
961 self
.needsMissingPropUseCounters
= (
962 self
.needsMissingPropUseCounters
963 or parent
.getExtendedAttribute("InstrumentedProps")
965 self
.prototypeChain
.insert(0, parent
.identifier
.name
)
966 parent
= parent
.parent
967 config
.maxProtoChainLength
= max(
968 config
.maxProtoChainLength
, len(self
.prototypeChain
)
971 def binaryNameFor(self
, name
, isStatic
):
972 return self
._binaryNames
.get((name
, isStatic
), name
)
975 def prototypeNameChain(self
):
976 return [self
.getDescriptor(p
).name
for p
in self
.prototypeChain
]
979 def parentPrototypeName(self
):
980 if len(self
.prototypeChain
) == 1:
982 return self
.getDescriptor(self
.prototypeChain
[-2]).name
984 def hasInterfaceOrInterfacePrototypeObject(self
):
986 self
.interface
.hasInterfaceObject()
987 or self
.interface
.hasInterfacePrototypeObject()
990 def hasOrdinaryObjectPrototype(self
):
991 return self
.interface
.getUserData("hasOrdinaryObjectPrototype", False)
994 def hasNamedPropertiesObject(self
):
995 return self
.isGlobal() and self
.supportsNamedProperties()
997 def getExtendedAttributes(self
, member
, getter
=False, setter
=False):
998 def ensureValidBoolExtendedAttribute(attr
, name
):
999 if attr
is not None and attr
is not True:
1000 raise TypeError("Unknown value for '%s': %s" % (name
, attr
[0]))
1002 def ensureValidThrowsExtendedAttribute(attr
):
1003 ensureValidBoolExtendedAttribute(attr
, "Throws")
1005 def ensureValidCanOOMExtendedAttribute(attr
):
1006 ensureValidBoolExtendedAttribute(attr
, "CanOOM")
1008 def maybeAppendNeedsErrorResultToAttrs(attrs
, throws
):
1009 ensureValidThrowsExtendedAttribute(throws
)
1010 if throws
is not None:
1011 attrs
.append("needsErrorResult")
1013 def maybeAppendCanOOMToAttrs(attrs
, canOOM
):
1014 ensureValidCanOOMExtendedAttribute(canOOM
)
1015 if canOOM
is not None:
1016 attrs
.append("canOOM")
1018 def maybeAppendNeedsSubjectPrincipalToAttrs(attrs
, needsSubjectPrincipal
):
1020 needsSubjectPrincipal
is not None
1021 and needsSubjectPrincipal
is not True
1022 and needsSubjectPrincipal
!= ["NonSystem"]
1025 "Unknown value for 'NeedsSubjectPrincipal': %s"
1026 % needsSubjectPrincipal
[0]
1029 if needsSubjectPrincipal
is not None:
1030 attrs
.append("needsSubjectPrincipal")
1031 if needsSubjectPrincipal
== ["NonSystem"]:
1032 attrs
.append("needsNonSystemSubjectPrincipal")
1034 name
= member
.identifier
.name
1035 throws
= self
.interface
.isJSImplemented() or member
.getExtendedAttribute(
1038 canOOM
= member
.getExtendedAttribute("CanOOM")
1039 needsSubjectPrincipal
= member
.getExtendedAttribute("NeedsSubjectPrincipal")
1041 if name
in self
.implicitJSContext
:
1042 attrs
.append("implicitJSContext")
1043 if member
.isMethod():
1044 if self
.interface
.isAsyncIteratorInterface() and name
== "next":
1045 attrs
.append("implicitJSContext")
1046 # JSObject-returning [NewObject] methods must be fallible,
1047 # since they have to (fallibly) allocate the new JSObject.
1048 if member
.getExtendedAttribute("NewObject"):
1049 if member
.returnsPromise():
1051 elif methodReturnsJSObject(member
):
1053 maybeAppendNeedsErrorResultToAttrs(attrs
, throws
)
1054 maybeAppendCanOOMToAttrs(attrs
, canOOM
)
1055 maybeAppendNeedsSubjectPrincipalToAttrs(attrs
, needsSubjectPrincipal
)
1058 assert member
.isAttr()
1059 assert bool(getter
) != bool(setter
)
1061 throwsAttr
= "GetterThrows" if getter
else "SetterThrows"
1062 throws
= member
.getExtendedAttribute(throwsAttr
)
1063 maybeAppendNeedsErrorResultToAttrs(attrs
, throws
)
1065 canOOMAttr
= "GetterCanOOM" if getter
else "SetterCanOOM"
1066 canOOM
= member
.getExtendedAttribute(canOOMAttr
)
1067 maybeAppendCanOOMToAttrs(attrs
, canOOM
)
1068 if needsSubjectPrincipal
is None:
1069 needsSubjectPrincipalAttr
= (
1070 "GetterNeedsSubjectPrincipal"
1072 else "SetterNeedsSubjectPrincipal"
1074 needsSubjectPrincipal
= member
.getExtendedAttribute(
1075 needsSubjectPrincipalAttr
1077 maybeAppendNeedsSubjectPrincipalToAttrs(attrs
, needsSubjectPrincipal
)
1080 def supportsIndexedProperties(self
):
1081 return self
.operations
["IndexedGetter"] is not None
1083 def lengthNeedsCallerType(self
):
1085 Determine whether our length getter needs a caller type; this is needed
1086 in some indexed-getter proxy algorithms. The idea is that if our
1087 indexed getter needs a caller type, our automatically-generated Length()
1090 assert self
.supportsIndexedProperties()
1091 indexedGetter
= self
.operations
["IndexedGetter"]
1092 return indexedGetter
.getExtendedAttribute("NeedsCallerType")
1094 def supportsNamedProperties(self
):
1095 return self
.operations
["NamedGetter"] is not None
1097 def supportedNamesNeedCallerType(self
):
1099 Determine whether our GetSupportedNames call needs a caller type. The
1100 idea is that if your named getter needs a caller type, then so does
1103 assert self
.supportsNamedProperties()
1104 namedGetter
= self
.operations
["NamedGetter"]
1105 return namedGetter
.getExtendedAttribute("NeedsCallerType")
1107 def isMaybeCrossOriginObject(self
):
1108 # If we're isGlobal and have cross-origin members, we're a Window, and
1109 # that's not a cross-origin object. The WindowProxy is.
1112 and self
.interface
.hasCrossOriginMembers
1113 and not self
.isGlobal()
1116 def needsHeaderInclude(self
):
1118 An interface doesn't need a header file if it is not concrete, not
1119 pref-controlled, has no prototype object, has no static methods or
1120 attributes and has no parent. The parent matters because we assert
1121 things about refcounting that depend on the actual underlying type if we
1126 self
.interface
.isExternal()
1128 or self
.interface
.hasInterfacePrototypeObject()
1130 (m
.isAttr() or m
.isMethod()) and m
.isStatic()
1131 for m
in self
.interface
.members
1133 or self
.interface
.parent
1136 def hasThreadChecks(self
):
1137 # isExposedConditionally does not necessarily imply thread checks
1138 # (since at least [SecureContext] is independent of them), but we're
1139 # only used to decide whether to include nsThreadUtils.h, so we don't
1142 self
.isExposedConditionally() and not self
.interface
.isExposedInWindow()
1143 ) or self
.interface
.isExposedInSomeButNotAllWorkers()
1145 def hasCEReactions(self
):
1147 m
.getExtendedAttribute("CEReactions") for m
in self
.interface
.members
1150 def isExposedConditionally(self
):
1152 self
.interface
.isExposedConditionally()
1153 or self
.interface
.isExposedInSomeButNotAllWorkers()
1156 def needsXrayResolveHooks(self
):
1158 Generally, any interface with NeedResolve needs Xray
1159 resolveOwnProperty and enumerateOwnProperties hooks. But for
1160 the special case of plugin-loading elements, we do NOT want
1161 those, because we don't want to instantiate plug-ins simply
1162 due to chrome touching them and that's all those hooks do on
1163 those elements. So we special-case those here.
1165 return self
.interface
.getExtendedAttribute(
1167 ) and self
.interface
.identifier
.name
not in [
1168 "HTMLObjectElement",
1172 def needsXrayNamedDeleterHook(self
):
1173 return self
.operations
["NamedDeleter"] is not None
1177 Returns true if this is the primary interface for a global object
1180 return self
.interface
.getExtendedAttribute("Global")
1183 def namedPropertiesEnumerable(self
):
1185 Returns whether this interface should have enumerable named properties
1188 assert self
.supportsNamedProperties()
1189 iface
= self
.interface
1191 if iface
.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
1193 iface
= iface
.parent
1197 def registersGlobalNamesOnWindow(self
):
1199 self
.interface
.hasInterfaceObject()
1200 and self
.interface
.isExposedInWindow()
1204 def getDescriptor(self
, interfaceName
):
1206 Gets the appropriate descriptor for the given interface name.
1208 return self
.config
.getDescriptor(interfaceName
)
1210 def getConfig(self
):
1214 # Some utility methods
1215 def getTypesFromDescriptor(descriptor
, includeArgs
=True, includeReturns
=True):
1217 Get argument and/or return types for all members of the descriptor. By
1218 default returns all argument types (which includes types of writable
1219 attributes) and all return types (which includes types of all attributes).
1221 assert includeArgs
or includeReturns
# Must want _something_.
1222 members
= [m
for m
in descriptor
.interface
.members
]
1223 if descriptor
.interface
.ctor():
1224 members
.append(descriptor
.interface
.ctor())
1225 members
.extend(descriptor
.interface
.legacyFactoryFunctions
)
1226 signatures
= [s
for m
in members
if m
.isMethod() for s
in m
.signatures()]
1228 for s
in signatures
:
1230 (returnType
, arguments
) = s
1232 types
.append(returnType
)
1234 types
.extend(a
.type for a
in arguments
)
1239 if (a
.isAttr() and (includeReturns
or (includeArgs
and not a
.readonly
)))
1242 if descriptor
.interface
.maplikeOrSetlikeOrIterable
:
1243 maplikeOrSetlikeOrIterable
= descriptor
.interface
.maplikeOrSetlikeOrIterable
1244 if maplikeOrSetlikeOrIterable
.isMaplike():
1245 # The things we expand into may or may not correctly indicate in
1246 # their formal IDL types what things we have as return values. For
1247 # example, "keys" returns the moral equivalent of sequence<keyType>
1248 # but just claims to return "object". Similarly, "values" returns
1249 # the moral equivalent of sequence<valueType> but claims to return
1250 # "object". And due to bug 1155340, "get" claims to return "any"
1251 # instead of the right type. So let's just manually work around
1252 # that lack of specificity. For our arguments, we already enforce
1253 # the right types at the IDL level, so those will get picked up
1255 assert maplikeOrSetlikeOrIterable
.hasKeyType()
1256 assert maplikeOrSetlikeOrIterable
.hasValueType()
1258 types
.append(maplikeOrSetlikeOrIterable
.keyType
)
1259 types
.append(maplikeOrSetlikeOrIterable
.valueType
)
1260 elif maplikeOrSetlikeOrIterable
.isSetlike():
1261 assert maplikeOrSetlikeOrIterable
.hasKeyType()
1262 assert maplikeOrSetlikeOrIterable
.hasValueType()
1264 maplikeOrSetlikeOrIterable
.keyType
1265 == maplikeOrSetlikeOrIterable
.valueType
1267 # As in the maplike case, we don't always declare our return values
1270 types
.append(maplikeOrSetlikeOrIterable
.keyType
)
1273 maplikeOrSetlikeOrIterable
.isIterable()
1274 or maplikeOrSetlikeOrIterable
.isAsyncIterable()
1276 # As in the maplike/setlike cases we don't do a good job of
1277 # declaring our actual return types, while our argument types, if
1278 # any, are declared fine.
1280 if maplikeOrSetlikeOrIterable
.hasKeyType():
1281 types
.append(maplikeOrSetlikeOrIterable
.keyType
)
1282 if maplikeOrSetlikeOrIterable
.hasValueType():
1283 types
.append(maplikeOrSetlikeOrIterable
.valueType
)
1288 def getTypesFromDictionary(dictionary
):
1290 Get all member types for this dictionary
1292 return [m
.type for m
in dictionary
.members
]
1295 def getTypesFromCallback(callback
):
1297 Get the types this callback depends on: its return type and the
1298 types of its arguments.
1300 sig
= callback
.signatures()[0]
1301 types
= [sig
[0]] # Return type
1302 types
.extend(arg
.type for arg
in sig
[1]) # Arguments
1306 def getAllTypes(descriptors
, dictionaries
, callbacks
):
1308 Generate all the types we're dealing with. For each type, a tuple
1309 containing type, dictionary is yielded. The dictionary can be None if the
1310 type does not come from a dictionary.
1312 for d
in descriptors
:
1313 if d
.interface
.isExternal():
1315 for t
in getTypesFromDescriptor(d
):
1317 for dictionary
in dictionaries
:
1318 for t
in getTypesFromDictionary(dictionary
):
1319 yield (t
, dictionary
)
1320 for callback
in callbacks
:
1321 for t
in getTypesFromCallback(callback
):
1325 # For sync value iterators, we use default array implementation, for async
1326 # iterators and sync pair iterators, we use AsyncIterableIterator or
1327 # IterableIterator instead.
1328 def iteratorNativeType(descriptor
):
1329 assert descriptor
.interface
.isIterable() or descriptor
.interface
.isAsyncIterable()
1330 iterableDecl
= descriptor
.interface
.maplikeOrSetlikeOrIterable
1331 assert iterableDecl
.isPairIterator() or descriptor
.interface
.isAsyncIterable()
1332 if descriptor
.interface
.isIterable():
1333 return "mozilla::dom::IterableIterator<%s>" % descriptor
.nativeType
1334 needReturnMethod
= toStringBool(
1335 descriptor
.interface
.maplikeOrSetlikeOrIterable
.getExtendedAttribute(
1336 "GenerateReturnMethod"
1340 return "mozilla::dom::binding_detail::AsyncIterableIteratorNative<%s, %s>" % (
1341 descriptor
.nativeType
,
1346 def findInnermostType(t
):
1348 Find the innermost type of the given type, unwrapping Promise and Record
1349 types, as well as everything that unroll() unwraps.
1354 elif t
.unroll() != t
:
1357 t
= t
.promiseInnerType()
1362 def getDependentDictionariesFromDictionary(d
):
1364 Find all the dictionaries contained in the given dictionary, as ancestors or
1365 members. This returns a generator.
1369 for member
in d
.members
:
1370 for next
in getDictionariesFromType(member
.type):
1375 def getDictionariesFromType(type):
1377 Find all the dictionaries contained in type. This can be used to find
1378 dictionaries that need conversion to JS (by looking at types that get
1379 converted to JS) or dictionaries that need conversion from JS (by looking at
1380 types that get converted from JS).
1382 This returns a generator.
1384 type = findInnermostType(type)
1386 # Look for dictionaries in all the member types
1387 for t
in type.flatMemberTypes
:
1388 for next
in getDictionariesFromType(t
):
1390 elif type.isDictionary():
1391 # Find the dictionaries that are itself, any of its ancestors, or
1392 # contained in any of its member types.
1393 for d
in getDependentDictionariesFromDictionary(type.inner
):
1397 def getDictionariesConvertedToJS(descriptors
, dictionaries
, callbacks
):
1398 for desc
in descriptors
:
1399 if desc
.interface
.isExternal():
1402 if desc
.interface
.isJSImplemented():
1403 # For a JS-implemented interface, we need to-JS
1404 # conversions for all the types involved.
1405 for t
in getTypesFromDescriptor(desc
):
1406 for d
in getDictionariesFromType(t
):
1408 elif desc
.interface
.isCallback():
1409 # For callbacks we only want to include the arguments, since that's
1410 # where the to-JS conversion happens.
1411 for t
in getTypesFromDescriptor(desc
, includeReturns
=False):
1412 for d
in getDictionariesFromType(t
):
1415 # For normal interfaces, we only want to include return values,
1416 # since that's where to-JS conversion happens.
1417 for t
in getTypesFromDescriptor(desc
, includeArgs
=False):
1418 for d
in getDictionariesFromType(t
):
1421 for callback
in callbacks
:
1422 # We only want to look at the arguments
1423 sig
= callback
.signatures()[0]
1425 for d
in getDictionariesFromType(arg
.type):
1428 for dictionary
in dictionaries
:
1429 if dictionary
.needsConversionToJS
:
1430 # It's explicitly flagged as needing to-JS conversion, and all its
1431 # dependent dictionaries will need to-JS conversion too.
1432 for d
in getDependentDictionariesFromDictionary(dictionary
):
1436 def getDictionariesConvertedFromJS(descriptors
, dictionaries
, callbacks
):
1437 for desc
in descriptors
:
1438 if desc
.interface
.isExternal():
1441 if desc
.interface
.isJSImplemented():
1442 # For a JS-implemented interface, we need from-JS conversions for
1443 # all the types involved.
1444 for t
in getTypesFromDescriptor(desc
):
1445 for d
in getDictionariesFromType(t
):
1447 elif desc
.interface
.isCallback():
1448 # For callbacks we only want to include the return value, since
1449 # that's where teh from-JS conversion happens.
1450 for t
in getTypesFromDescriptor(desc
, includeArgs
=False):
1451 for d
in getDictionariesFromType(t
):
1454 # For normal interfaces, we only want to include arguments values,
1455 # since that's where from-JS conversion happens.
1456 for t
in getTypesFromDescriptor(desc
, includeReturns
=False):
1457 for d
in getDictionariesFromType(t
):
1460 for callback
in callbacks
:
1461 # We only want to look at the return value
1462 sig
= callback
.signatures()[0]
1463 for d
in getDictionariesFromType(sig
[0]):
1466 for dictionary
in dictionaries
:
1467 if dictionary
.needsConversionFromJS
:
1468 # It's explicitly flagged as needing from-JS conversion, and all its
1469 # dependent dictionaries will need from-JS conversion too.
1470 for d
in getDependentDictionariesFromDictionary(dictionary
):