Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / bindings / Codegen.py
blob266ae88785d329fc9be08d62acb62a2e6f364416
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 # Common codegen classes.
7 import functools
8 import math
9 from operator import attrgetter
10 import os
11 import re
12 import string
13 import textwrap
15 from Configuration import (
16 Descriptor,
17 MemberIsLegacyUnforgeable,
18 NoSuchDescriptorError,
19 getAllTypes,
20 getTypesFromCallback,
21 getTypesFromDescriptor,
22 getTypesFromDictionary,
24 from perfecthash import PerfectHash
25 from WebIDL import (
26 BuiltinTypes,
27 IDLAttribute,
28 IDLBuiltinType,
29 IDLDefaultDictionaryValue,
30 IDLDictionary,
31 IDLEmptySequenceValue,
32 IDLInterfaceMember,
33 IDLNullValue,
34 IDLSequenceType,
35 IDLType,
36 IDLUndefinedValue,
39 AUTOGENERATED_WARNING_COMMENT = (
40 "/* THIS FILE IS AUTOGENERATED BY Codegen.py - DO NOT EDIT */\n\n"
42 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT = (
43 "/* THIS FILE IS AUTOGENERATED FROM %s BY Codegen.py - DO NOT EDIT */\n\n"
45 ADDPROPERTY_HOOK_NAME = "_addProperty"
46 GETWRAPPERCACHE_HOOK_NAME = "_getWrapperCache"
47 FINALIZE_HOOK_NAME = "_finalize"
48 OBJECT_MOVED_HOOK_NAME = "_objectMoved"
49 CONSTRUCT_HOOK_NAME = "_constructor"
50 LEGACYCALLER_HOOK_NAME = "_legacycaller"
51 RESOLVE_HOOK_NAME = "_resolve"
52 MAY_RESOLVE_HOOK_NAME = "_mayResolve"
53 NEW_ENUMERATE_HOOK_NAME = "_newEnumerate"
54 INSTANCE_RESERVED_SLOTS = 1
56 # This size is arbitrary. It is a power of 2 to make using it as a modulo
57 # operand cheap, and is usually around 1/3-1/5th of the set size (sometimes
58 # smaller for very large sets).
59 GLOBAL_NAMES_PHF_SIZE = 256
62 def reservedSlot(slotIndex, forXray):
63 base = "DOM_EXPANDO_RESERVED_SLOTS" if forXray else "DOM_INSTANCE_RESERVED_SLOTS"
64 return "(%s + %d)" % (base, slotIndex)
67 def getSlotIndex(member, descriptor):
68 slotIndex = member.slotIndices[descriptor.interface.identifier.name]
69 return slotIndex[0] if isinstance(slotIndex, tuple) else slotIndex
72 def memberReservedSlot(member, descriptor):
73 return reservedSlot(getSlotIndex(member, descriptor), False)
76 def memberXrayExpandoReservedSlot(member, descriptor):
77 return reservedSlot(getSlotIndex(member, descriptor), True)
80 def mayUseXrayExpandoSlots(descriptor, attr):
81 assert not attr.getExtendedAttribute("NewObject")
82 # For attributes whose type is a Gecko interface we always use
83 # slots on the reflector for caching. Also, for interfaces that
84 # don't want Xrays we obviously never use the Xray expando slot.
85 return descriptor.wantsXrays and not attr.type.isGeckoInterface()
88 def reflectedHTMLAttributesArrayIndex(descriptor, attr):
89 slots = attr.slotIndices[descriptor.interface.identifier.name]
90 return slots[1]
93 def getReflectedHTMLAttributesIface(descriptor):
94 iface = descriptor.interface
95 while iface:
96 if iface.reflectedHTMLAttributesReturningFrozenArray:
97 return iface
98 iface = iface.parent
99 return None
102 def toStringBool(arg):
104 Converts IDL/Python Boolean (True/False) to C++ Boolean (true/false)
106 return str(not not arg).lower()
109 def toBindingNamespace(arg):
110 return arg + "_Binding"
113 def isTypeCopyConstructible(type):
114 # Nullable and sequence stuff doesn't affect copy-constructibility
115 type = type.unroll()
116 return (
117 type.isUndefined()
118 or type.isPrimitive()
119 or type.isString()
120 or type.isEnum()
121 or (type.isUnion() and CGUnionStruct.isUnionCopyConstructible(type))
122 or (
123 type.isDictionary()
124 and CGDictionary.isDictionaryCopyConstructible(type.inner)
127 # Interface types are only copy-constructible if they're Gecko
128 # interfaces. SpiderMonkey interfaces are not copy-constructible
129 # because of rooting issues.
130 (type.isInterface() and type.isGeckoInterface())
134 class CycleCollectionUnsupported(TypeError):
135 def __init__(self, message):
136 TypeError.__init__(self, message)
139 def idlTypeNeedsCycleCollection(type):
140 type = type.unroll() # Takes care of sequences and nullables
141 if (
142 (type.isPrimitive() and type.tag() in builtinNames)
143 or type.isUndefined()
144 or type.isEnum()
145 or type.isString()
146 or type.isAny()
147 or type.isObject()
148 or type.isSpiderMonkeyInterface()
150 return False
151 elif type.isCallback() or type.isPromise() or type.isGeckoInterface():
152 return True
153 elif type.isUnion():
154 return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes)
155 elif type.isRecord():
156 if idlTypeNeedsCycleCollection(type.inner):
157 raise CycleCollectionUnsupported(
158 "Cycle collection for type %s is not supported" % type
160 return False
161 elif type.isDictionary():
162 return CGDictionary.dictionaryNeedsCycleCollection(type.inner)
163 else:
164 raise CycleCollectionUnsupported(
165 "Don't know whether to cycle-collect type %s" % type
169 def idlTypeNeedsCallContext(type, descriptor=None, allowTreatNonCallableAsNull=False):
171 Returns whether the given type needs error reporting via a
172 BindingCallContext for JS-to-C++ conversions. This will happen when the
173 conversion can throw an exception due to logic in the IDL spec or
174 Gecko-specific security checks. In particular, a type needs a
175 BindingCallContext if and only if the JS-to-C++ conversion for that type can
176 end up calling ThrowErrorMessage.
178 For some types this depends on the descriptor (e.g. because we do certain
179 checks only for some kinds of interfaces).
181 The allowTreatNonCallableAsNull optimization is there so we can avoid
182 generating an unnecessary BindingCallContext for all the event handler
183 attribute setters.
186 while True:
187 if type.isSequence():
188 # Sequences can always throw "not an object"
189 return True
190 if type.nullable():
191 # treatNonObjectAsNull() and treatNonCallableAsNull() are
192 # only sane things to test on nullable types, so do that now.
193 if (
194 allowTreatNonCallableAsNull
195 and type.isCallback()
196 and (type.treatNonObjectAsNull() or type.treatNonCallableAsNull())
198 # This can't throw. so never needs a method description.
199 return False
200 type = type.inner
201 else:
202 break
204 if type.isUndefined():
205 # Clearly doesn't need a method description; we can only get here from
206 # CGHeaders trying to decide whether to include the method description
207 # header.
208 return False
209 # The float check needs to come before the isPrimitive() check,
210 # because floats are primitives too.
211 if type.isFloat():
212 # Floats can throw if restricted.
213 return not type.isUnrestricted()
214 if type.isPrimitive() and type.tag() in builtinNames:
215 # Numbers can throw if enforcing range.
216 return type.hasEnforceRange()
217 if type.isEnum():
218 # Can throw on invalid value.
219 return True
220 if type.isString():
221 # Can throw if it's a ByteString
222 return type.isByteString()
223 if type.isAny():
224 # JS-implemented interfaces do extra security checks so need a
225 # method description here. If we have no descriptor, this
226 # might be JS-implemented thing, so it will do the security
227 # check and we need the method description.
228 return not descriptor or descriptor.interface.isJSImplemented()
229 if type.isPromise():
230 # JS-to-Promise conversion won't cause us to throw any
231 # specific exceptions, so does not need a method description.
232 return False
233 if (
234 type.isObject()
235 or type.isInterface()
236 or type.isCallback()
237 or type.isDictionary()
238 or type.isRecord()
239 or type.isObservableArray()
241 # These can all throw if a primitive is passed in, at the very least.
242 # There are some rare cases when we know we have an object, but those
243 # are not worth the complexity of optimizing for.
245 # Note that we checked the [LegacyTreatNonObjectAsNull] case already when
246 # unwrapping nullables.
247 return True
248 if type.isUnion():
249 # Can throw if a type not in the union is passed in.
250 return True
251 raise TypeError("Don't know whether type '%s' needs a method description" % type)
254 # TryPreserveWrapper uses the addProperty hook to preserve the wrapper of
255 # non-nsISupports cycle collected objects, so if wantsAddProperty is changed
256 # to not cover that case then TryPreserveWrapper will need to be changed.
257 def wantsAddProperty(desc):
258 return desc.concrete and desc.wrapperCache and not desc.isGlobal()
261 def wantsGetWrapperCache(desc):
262 return (
263 desc.concrete and desc.wrapperCache and not desc.isGlobal() and not desc.proxy
267 def indent(s, indentLevel=2):
269 Indent C++ code.
271 Weird secret feature: this doesn't indent lines that start with # (such as
272 #include lines or #ifdef/#endif).
275 # We'll want to insert the indent at the beginnings of lines, but we
276 # don't want to indent empty lines.
277 padding = indentLevel * " "
278 return "\n".join(
280 (padding + line) if line and line[0] != "#" else line
281 for line in s.split("\n")
286 # dedent() and fill() are often called on the same string multiple
287 # times. We want to memoize their return values so we don't keep
288 # recomputing them all the time.
289 def memoize(fn):
291 Decorator to memoize a function of one argument. The cache just
292 grows without bound.
294 cache = {}
296 @functools.wraps(fn)
297 def wrapper(arg):
298 retval = cache.get(arg)
299 if retval is None:
300 retval = cache[arg] = fn(arg)
301 return retval
303 return wrapper
306 @memoize
307 def dedent(s):
309 Remove all leading whitespace from s, and remove a blank line
310 at the beginning.
312 if s.startswith("\n"):
313 s = s[1:]
314 return textwrap.dedent(s)
317 # This works by transforming the fill()-template to an equivalent
318 # string.Template.
319 fill_multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?")
322 find_substitutions = re.compile(r"\${")
325 @memoize
326 def compile_fill_template(template):
328 Helper function for fill(). Given the template string passed to fill(),
329 do the reusable part of template processing and return a pair (t,
330 argModList) that can be used every time fill() is called with that
331 template argument.
333 argsModList is list of tuples that represent modifications to be
334 made to args. Each modification has, in order: i) the arg name,
335 ii) the modified name, iii) the indent depth.
337 t = dedent(template)
338 assert t.endswith("\n") or "\n" not in t
339 argModList = []
341 def replace(match):
343 Replaces a line like ' $*{xyz}\n' with '${xyz_n}',
344 where n is the indent depth, and add a corresponding entry to
345 argModList.
347 Note that this needs to close over argModList, so it has to be
348 defined inside compile_fill_template().
350 indentation, name, nl = match.groups()
351 depth = len(indentation)
353 # Check that $*{xyz} appears by itself on a line.
354 prev = match.string[: match.start()]
355 if (prev and not prev.endswith("\n")) or nl is None:
356 raise ValueError(
357 "Invalid fill() template: $*{%s} must appear by itself on a line" % name
360 # Now replace this whole line of template with the indented equivalent.
361 modified_name = name + "_" + str(depth)
362 argModList.append((name, modified_name, depth))
363 return "${" + modified_name + "}"
365 t = re.sub(fill_multiline_substitution_re, replace, t)
366 if not re.search(find_substitutions, t):
367 raise TypeError("Using fill() when dedent() would do.")
368 return (string.Template(t), argModList)
371 def fill(template, **args):
373 Convenience function for filling in a multiline template.
375 `fill(template, name1=v1, name2=v2)` is a lot like
376 `string.Template(template).substitute({"name1": v1, "name2": v2})`.
378 However, it's shorter, and has a few nice features:
380 * If `template` is indented, fill() automatically dedents it!
381 This makes code using fill() with Python's multiline strings
382 much nicer to look at.
384 * If `template` starts with a blank line, fill() strips it off.
385 (Again, convenient with multiline strings.)
387 * fill() recognizes a special kind of substitution
388 of the form `$*{name}`.
390 Use this to paste in, and automatically indent, multiple lines.
391 (Mnemonic: The `*` is for "multiple lines").
393 A `$*` substitution must appear by itself on a line, with optional
394 preceding indentation (spaces only). The whole line is replaced by the
395 corresponding keyword argument, indented appropriately. If the
396 argument is an empty string, no output is generated, not even a blank
397 line.
400 t, argModList = compile_fill_template(template)
401 # Now apply argModList to args
402 for name, modified_name, depth in argModList:
403 if not (args[name] == "" or args[name].endswith("\n")):
404 raise ValueError(
405 "Argument %s with value %r is missing a newline" % (name, args[name])
407 args[modified_name] = indent(args[name], depth)
409 return t.substitute(args)
412 class CGThing:
414 Abstract base class for things that spit out code.
417 def __init__(self):
418 pass # Nothing for now
420 def declare(self):
421 """Produce code for a header file."""
422 assert False # Override me!
424 def define(self):
425 """Produce code for a cpp file."""
426 assert False # Override me!
428 def deps(self):
429 """Produce the deps for a pp file"""
430 assert False # Override me!
433 class CGStringTable(CGThing):
435 Generate a function accessor for a WebIDL string table, using the existing
436 concatenated names string and mapping indexes to offsets in that string:
438 const char *accessorName(unsigned int index) {
439 static const uint16_t offsets = { ... };
440 return BindingName(offsets[index]);
443 This is more efficient than the more natural:
445 const char *table[] = {
449 The uint16_t offsets are smaller than the pointer equivalents, and the
450 concatenated string requires no runtime relocations.
453 def __init__(self, accessorName, strings, static=False):
454 CGThing.__init__(self)
455 self.accessorName = accessorName
456 self.strings = strings
457 self.static = static
459 def declare(self):
460 if self.static:
461 return ""
462 return "const char *%s(unsigned int aIndex);\n" % self.accessorName
464 def define(self):
465 offsets = []
466 for s in self.strings:
467 offsets.append(BindingNamesOffsetEnum(s))
468 return fill(
470 ${static}const char *${name}(unsigned int aIndex)
472 static const BindingNamesOffset offsets[] = {
473 $*{offsets}
475 return BindingName(offsets[aIndex]);
477 """,
478 static="static " if self.static else "",
479 name=self.accessorName,
480 offsets="".join("BindingNamesOffset::%s,\n" % o for o in offsets),
484 class CGNativePropertyHooks(CGThing):
486 Generate a NativePropertyHooks for a given descriptor
489 def __init__(self, descriptor, properties):
490 CGThing.__init__(self)
491 assert descriptor.wantsXrays
492 self.descriptor = descriptor
493 self.properties = properties
495 def declare(self):
496 return ""
498 def define(self):
499 if (
500 self.descriptor.concrete
501 and self.descriptor.proxy
502 and not self.descriptor.isMaybeCrossOriginObject()
504 if self.descriptor.needsXrayNamedDeleterHook():
505 deleteNamedProperty = "DeleteNamedProperty"
506 else:
507 deleteNamedProperty = "nullptr"
508 namedOrIndexed = fill(
510 const NativeNamedOrIndexedPropertyHooks sNativeNamedOrIndexedPropertyHooks = {
511 binding_detail::ResolveOwnProperty,
512 binding_detail::EnumerateOwnProperties,
513 ${deleteNamedProperty}
515 """,
516 deleteNamedProperty=deleteNamedProperty,
518 namedOrIndexedPointer = "&sNativeNamedOrIndexedPropertyHooks"
519 elif self.descriptor.needsXrayResolveHooks():
520 namedOrIndexed = dedent(
522 const NativeNamedOrIndexedPropertyHooks sNativeNamedOrIndexedPropertyHooks = {
523 ResolveOwnPropertyViaResolve,
524 EnumerateOwnPropertiesViaGetOwnPropertyNames,
525 nullptr
529 namedOrIndexedPointer = "&sNativeNamedOrIndexedPropertyHooks"
530 else:
531 namedOrIndexed = ""
532 namedOrIndexedPointer = "nullptr"
533 if self.properties.hasNonChromeOnly():
534 regular = "sNativeProperties.Upcast()"
535 else:
536 regular = "nullptr"
537 if self.properties.hasChromeOnly():
538 chrome = "sChromeOnlyNativeProperties.Upcast()"
539 else:
540 chrome = "nullptr"
541 constructorID = "constructors::id::"
542 if self.descriptor.interface.hasInterfaceObject():
543 constructorID += self.descriptor.name
544 else:
545 constructorID += "_ID_Count"
546 prototypeID = "prototypes::id::"
547 if self.descriptor.interface.hasInterfacePrototypeObject():
548 prototypeID += self.descriptor.name
549 else:
550 prototypeID += "_ID_Count"
552 if self.descriptor.wantsXrayExpandoClass:
553 expandoClass = "&sXrayExpandoObjectClass"
554 else:
555 expandoClass = "&DefaultXrayExpandoObjectClass"
557 return namedOrIndexed + fill(
559 bool sNativePropertiesInited = false;
560 const NativePropertyHooks sNativePropertyHooks = {
561 ${namedOrIndexedPointer},
562 { ${regular}, ${chrome}, &sNativePropertiesInited },
563 ${prototypeID},
564 ${constructorID},
565 ${expandoClass}
567 """,
568 namedOrIndexedPointer=namedOrIndexedPointer,
569 regular=regular,
570 chrome=chrome,
571 prototypeID=prototypeID,
572 constructorID=constructorID,
573 expandoClass=expandoClass,
577 def NativePropertyHooks(descriptor):
578 return (
579 "&sEmptyNativePropertyHooks"
580 if not descriptor.wantsXrays
581 else "&sNativePropertyHooks"
585 def DOMClass(descriptor):
586 protoList = ["prototypes::id::" + proto for proto in descriptor.prototypeNameChain]
587 # Pad out the list to the right length with _ID_Count so we
588 # guarantee that all the lists are the same length. _ID_Count
589 # is never the ID of any prototype, so it's safe to use as
590 # padding.
591 protoList.extend(
592 ["prototypes::id::_ID_Count"]
593 * (descriptor.config.maxProtoChainLength - len(protoList))
596 if descriptor.interface.isSerializable():
597 serializer = "Serialize"
598 else:
599 serializer = "nullptr"
601 if wantsGetWrapperCache(descriptor):
602 wrapperCacheGetter = GETWRAPPERCACHE_HOOK_NAME
603 else:
604 wrapperCacheGetter = "nullptr"
606 if descriptor.hasOrdinaryObjectPrototype():
607 getProto = "JS::GetRealmObjectPrototypeHandle"
608 else:
609 getProto = "GetProtoObjectHandle"
611 return fill(
613 { ${protoChain} },
614 std::is_base_of_v<nsISupports, ${nativeType}>,
615 ${hooks},
616 FindAssociatedGlobalForNative<${nativeType}>::Get,
617 ${getProto},
618 GetCCParticipant<${nativeType}>::Get(),
619 ${serializer},
620 ${wrapperCacheGetter}
621 """,
622 protoChain=", ".join(protoList),
623 nativeType=descriptor.nativeType,
624 hooks=NativePropertyHooks(descriptor),
625 serializer=serializer,
626 wrapperCacheGetter=wrapperCacheGetter,
627 getProto=getProto,
631 def InstanceReservedSlots(descriptor):
632 slots = INSTANCE_RESERVED_SLOTS + descriptor.interface.totalMembersInSlots
633 if descriptor.isMaybeCrossOriginObject():
634 # We need a slot for the cross-origin holder too.
635 if descriptor.interface.hasChildInterfaces():
636 raise TypeError(
637 "We don't support non-leaf cross-origin interfaces "
638 "like %s" % descriptor.interface.identifier.name
640 slots += 1
641 return slots
644 class CGDOMJSClass(CGThing):
646 Generate a DOMJSClass for a given descriptor
649 def __init__(self, descriptor):
650 CGThing.__init__(self)
651 self.descriptor = descriptor
653 def declare(self):
654 return ""
656 def define(self):
657 callHook = (
658 LEGACYCALLER_HOOK_NAME
659 if self.descriptor.operations["LegacyCaller"]
660 else "nullptr"
662 objectMovedHook = (
663 OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else "nullptr"
665 slotCount = InstanceReservedSlots(self.descriptor)
666 classFlags = "JSCLASS_IS_DOMJSCLASS | JSCLASS_FOREGROUND_FINALIZE | "
667 if self.descriptor.isGlobal():
668 classFlags += (
669 "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS)"
671 traceHook = "JS_GlobalObjectTraceHook"
672 reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
673 else:
674 classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
675 iface = getReflectedHTMLAttributesIface(self.descriptor)
676 if iface:
677 traceHook = (
678 "%s::ReflectedHTMLAttributeSlots::Trace"
679 % toBindingNamespace(iface.identifier.name)
681 else:
682 traceHook = "nullptr"
683 reservedSlots = slotCount
684 if self.descriptor.interface.hasProbablyShortLivingWrapper():
685 if not self.descriptor.wrapperCache:
686 raise TypeError(
687 "Need a wrapper cache to support nursery "
688 "allocation of DOM objects"
690 classFlags += " | JSCLASS_SKIP_NURSERY_FINALIZE"
692 if self.descriptor.interface.getExtendedAttribute("NeedResolve"):
693 resolveHook = RESOLVE_HOOK_NAME
694 mayResolveHook = MAY_RESOLVE_HOOK_NAME
695 newEnumerateHook = NEW_ENUMERATE_HOOK_NAME
696 elif self.descriptor.isGlobal():
697 resolveHook = "mozilla::dom::ResolveGlobal"
698 mayResolveHook = "mozilla::dom::MayResolveGlobal"
699 newEnumerateHook = "mozilla::dom::EnumerateGlobal"
700 else:
701 resolveHook = "nullptr"
702 mayResolveHook = "nullptr"
703 newEnumerateHook = "nullptr"
705 return fill(
707 static const JSClassOps sClassOps = {
708 ${addProperty}, /* addProperty */
709 nullptr, /* delProperty */
710 nullptr, /* enumerate */
711 ${newEnumerate}, /* newEnumerate */
712 ${resolve}, /* resolve */
713 ${mayResolve}, /* mayResolve */
714 ${finalize}, /* finalize */
715 ${call}, /* call */
716 nullptr, /* construct */
717 ${trace}, /* trace */
720 static const js::ClassExtension sClassExtension = {
721 ${objectMoved} /* objectMovedOp */
724 static const DOMJSClass sClass = {
725 { "${name}",
726 ${flags},
727 &sClassOps,
728 JS_NULL_CLASS_SPEC,
729 &sClassExtension,
730 JS_NULL_OBJECT_OPS
732 $*{descriptor}
734 static_assert(${instanceReservedSlots} == DOM_INSTANCE_RESERVED_SLOTS,
735 "Must have the right minimal number of reserved slots.");
736 static_assert(${reservedSlots} >= ${slotCount},
737 "Must have enough reserved slots.");
738 """,
739 name=self.descriptor.interface.getClassName(),
740 flags=classFlags,
741 addProperty=(
742 ADDPROPERTY_HOOK_NAME
743 if wantsAddProperty(self.descriptor)
744 else "nullptr"
746 newEnumerate=newEnumerateHook,
747 resolve=resolveHook,
748 mayResolve=mayResolveHook,
749 finalize=FINALIZE_HOOK_NAME,
750 call=callHook,
751 trace=traceHook,
752 objectMoved=objectMovedHook,
753 descriptor=DOMClass(self.descriptor),
754 instanceReservedSlots=INSTANCE_RESERVED_SLOTS,
755 reservedSlots=reservedSlots,
756 slotCount=slotCount,
760 class CGDOMProxyJSClass(CGThing):
762 Generate a DOMJSClass for a given proxy descriptor
765 def __init__(self, descriptor):
766 CGThing.__init__(self)
767 self.descriptor = descriptor
769 def declare(self):
770 return ""
772 def define(self):
773 slotCount = InstanceReservedSlots(self.descriptor)
774 # We need one reserved slot (DOM_OBJECT_SLOT).
775 flags = ["JSCLASS_IS_DOMJSCLASS", "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount]
776 # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because
777 # we don't want people ever adding that to any interface other than
778 # HTMLAllCollection. So just hardcode it here.
779 if self.descriptor.interface.identifier.name == "HTMLAllCollection":
780 flags.append("JSCLASS_EMULATES_UNDEFINED")
781 return fill(
783 static const DOMJSClass sClass = {
784 PROXY_CLASS_DEF("${name}",
785 ${flags}),
786 $*{descriptor}
788 """,
789 name=self.descriptor.interface.identifier.name,
790 flags=" | ".join(flags),
791 descriptor=DOMClass(self.descriptor),
795 class CGXrayExpandoJSClass(CGThing):
797 Generate a JSClass for an Xray expando object. This is only
798 needed if we have members in slots (for [Cached] or [StoreInSlot]
799 stuff).
802 def __init__(self, descriptor):
803 assert descriptor.interface.totalMembersInSlots != 0
804 assert descriptor.wantsXrays
805 assert descriptor.wantsXrayExpandoClass
806 CGThing.__init__(self)
807 self.descriptor = descriptor
809 def declare(self):
810 return ""
812 def define(self):
813 iface = getReflectedHTMLAttributesIface(self.descriptor)
814 if iface:
815 ops = (
816 "&%s::ReflectedHTMLAttributeSlots::sXrayExpandoObjectClassOps"
817 % toBindingNamespace(iface.identifier.name)
819 else:
820 ops = "&xpc::XrayExpandoObjectClassOps"
821 return fill(
823 // This may allocate too many slots, because we only really need
824 // slots for our non-interface-typed members that we cache. But
825 // allocating slots only for those would make the slot index
826 // computations much more complicated, so let's do this the simple
827 // way for now.
828 DEFINE_XRAY_EXPANDO_CLASS_WITH_OPS(static, sXrayExpandoObjectClass, ${memberSlots},
829 ${ops});
830 """,
831 memberSlots=self.descriptor.interface.totalMembersInSlots,
832 ops=ops,
836 def PrototypeIDAndDepth(descriptor):
837 prototypeID = "prototypes::id::"
838 if descriptor.interface.hasInterfacePrototypeObject():
839 prototypeID += descriptor.interface.identifier.name
840 depth = "PrototypeTraits<%s>::Depth" % prototypeID
841 else:
842 prototypeID += "_ID_Count"
843 depth = "0"
844 return (prototypeID, depth)
847 def InterfacePrototypeObjectProtoGetter(descriptor):
849 Returns a tuple with two elements:
851 1) The name of the function to call to get the prototype to use for the
852 interface prototype object as a JSObject*.
854 2) The name of the function to call to get the prototype to use for the
855 interface prototype object as a JS::Handle<JSObject*> or None if no
856 such function exists.
858 parentProtoName = descriptor.parentPrototypeName
859 if descriptor.hasNamedPropertiesObject:
860 protoGetter = "GetNamedPropertiesObject"
861 protoHandleGetter = None
862 elif parentProtoName is None:
863 protoHandleGetter = None
864 if descriptor.interface.getExtendedAttribute("ExceptionClass"):
865 protoGetter = "JS::GetRealmErrorPrototype"
866 elif descriptor.interface.isIteratorInterface():
867 protoGetter = "JS::GetRealmIteratorPrototype"
868 elif descriptor.interface.isAsyncIteratorInterface():
869 protoGetter = "JS::GetRealmAsyncIteratorPrototype"
870 else:
871 protoGetter = "JS::GetRealmObjectPrototype"
872 protoHandleGetter = "JS::GetRealmObjectPrototypeHandle"
873 else:
874 prefix = toBindingNamespace(parentProtoName)
875 protoGetter = prefix + "::GetProtoObject"
876 protoHandleGetter = prefix + "::GetProtoObjectHandle"
878 return (protoGetter, protoHandleGetter)
881 class CGPrototypeJSClass(CGThing):
882 def __init__(self, descriptor, properties):
883 CGThing.__init__(self)
884 self.descriptor = descriptor
885 self.properties = properties
887 def declare(self):
888 # We're purely for internal consumption
889 return ""
891 def define(self):
892 prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
893 slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE"
894 # Globals handle unforgeables directly in Wrap() instead of
895 # via a holder.
896 if (
897 self.descriptor.hasLegacyUnforgeableMembers
898 and not self.descriptor.isGlobal()
900 slotCount += (
901 " + 1 /* slot for the JSObject holding the unforgeable properties */"
903 (protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor)
904 type = (
905 "eGlobalInterfacePrototype"
906 if self.descriptor.isGlobal()
907 else "eInterfacePrototype"
909 return fill(
911 static const DOMIfaceAndProtoJSClass sPrototypeClass = {
913 "${name}Prototype",
914 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
915 JS_NULL_CLASS_OPS,
916 JS_NULL_CLASS_SPEC,
917 JS_NULL_CLASS_EXT,
918 JS_NULL_OBJECT_OPS
920 ${type},
921 ${prototypeID},
922 ${depth},
923 ${hooks},
924 ${protoGetter}
926 """,
927 name=self.descriptor.interface.getClassName(),
928 slotCount=slotCount,
929 type=type,
930 hooks=NativePropertyHooks(self.descriptor),
931 prototypeID=prototypeID,
932 depth=depth,
933 protoGetter=protoGetter,
937 def InterfaceObjectProtoGetter(descriptor):
939 Returns the name of the function to call to get the prototype to use for the
940 interface object's prototype as a JS::Handle<JSObject*>.
942 assert not descriptor.interface.isNamespace()
943 parentInterface = descriptor.interface.parent
944 if parentInterface:
945 parentIfaceName = parentInterface.identifier.name
946 parentDesc = descriptor.getDescriptor(parentIfaceName)
947 prefix = toBindingNamespace(parentDesc.name)
948 protoHandleGetter = prefix + "::GetConstructorObjectHandle"
949 else:
950 protoHandleGetter = "JS::GetRealmFunctionPrototypeHandle"
951 return protoHandleGetter
954 class CGNamespaceObjectJSClass(CGThing):
955 def __init__(self, descriptor):
956 CGThing.__init__(self)
957 self.descriptor = descriptor
959 def declare(self):
960 # We're purely for internal consumption
961 return ""
963 def define(self):
964 classString = self.descriptor.interface.getExtendedAttribute("ClassString")
965 if classString is None:
966 classString = self.descriptor.interface.identifier.name
967 else:
968 classString = classString[0]
969 return fill(
971 static const DOMIfaceAndProtoJSClass sNamespaceObjectClass = {
973 "${classString}",
974 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS,
975 JS_NULL_CLASS_OPS,
976 JS_NULL_CLASS_SPEC,
977 JS_NULL_CLASS_EXT,
978 JS_NULL_OBJECT_OPS
980 eNamespace,
981 prototypes::id::_ID_Count,
983 ${hooks},
984 // This isn't strictly following the spec (see
985 // https://console.spec.whatwg.org/#ref-for-dfn-namespace-object),
986 // but should be ok for Xrays.
987 JS::GetRealmObjectPrototype
989 """,
990 classString=classString,
991 hooks=NativePropertyHooks(self.descriptor),
995 class CGInterfaceObjectInfo(CGThing):
996 def __init__(self, descriptor):
997 CGThing.__init__(self)
998 self.descriptor = descriptor
1000 def declare(self):
1001 # We're purely for internal consumption
1002 return ""
1004 def define(self):
1005 if self.descriptor.interface.ctor():
1006 ctorname = CONSTRUCT_HOOK_NAME
1007 constructorArgs = methodLength(self.descriptor.interface.ctor())
1008 else:
1009 ctorname = "ThrowingConstructor"
1010 constructorArgs = 0
1011 constructorName = self.descriptor.interface.getClassName()
1012 wantsIsInstance = self.descriptor.interface.hasInterfacePrototypeObject()
1014 prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
1015 protoHandleGetter = InterfaceObjectProtoGetter(self.descriptor)
1017 return fill(
1019 static const DOMInterfaceInfo sInterfaceObjectInfo = {
1020 { ${ctorname}, ${hooks} },
1021 ${protoHandleGetter},
1022 ${depth},
1023 ${prototypeID},
1024 ${wantsIsInstance},
1025 ${constructorArgs},
1026 "${constructorName}",
1028 """,
1029 ctorname=ctorname,
1030 hooks=NativePropertyHooks(self.descriptor),
1031 protoHandleGetter=protoHandleGetter,
1032 depth=depth,
1033 prototypeID=prototypeID,
1034 wantsIsInstance=toStringBool(wantsIsInstance),
1035 constructorArgs=constructorArgs,
1036 constructorName=constructorName,
1040 class CGList(CGThing):
1042 Generate code for a list of GCThings. Just concatenates them together, with
1043 an optional joiner string. "\n" is a common joiner.
1046 def __init__(self, children, joiner=""):
1047 CGThing.__init__(self)
1048 # Make a copy of the kids into a list, because if someone passes in a
1049 # generator we won't be able to both declare and define ourselves, or
1050 # define ourselves more than once!
1051 self.children = list(children)
1052 self.joiner = joiner
1054 def append(self, child):
1055 self.children.append(child)
1057 def prepend(self, child):
1058 self.children.insert(0, child)
1060 def extend(self, kids):
1061 self.children.extend(kids)
1063 def join(self, iterable):
1064 return self.joiner.join(s for s in iterable if len(s) > 0)
1066 def declare(self):
1067 return self.join(
1068 child.declare() for child in self.children if child is not None
1071 def define(self):
1072 return self.join(child.define() for child in self.children if child is not None)
1074 def deps(self):
1075 deps = set()
1076 for child in self.children:
1077 if child is None:
1078 continue
1079 deps = deps.union(child.deps())
1080 return deps
1082 def __len__(self):
1083 return len(self.children)
1086 class CGGeneric(CGThing):
1088 A class that spits out a fixed string into the codegen. Can spit out a
1089 separate string for the declaration too.
1092 def __init__(self, define="", declare=""):
1093 self.declareText = declare
1094 self.defineText = define
1096 def declare(self):
1097 return self.declareText
1099 def define(self):
1100 return self.defineText
1102 def deps(self):
1103 return set()
1106 class CGIndenter(CGThing):
1108 A class that takes another CGThing and generates code that indents that
1109 CGThing by some number of spaces. The default indent is two spaces.
1112 def __init__(self, child, indentLevel=2, declareOnly=False):
1113 assert isinstance(child, CGThing)
1114 CGThing.__init__(self)
1115 self.child = child
1116 self.indentLevel = indentLevel
1117 self.declareOnly = declareOnly
1119 def declare(self):
1120 return indent(self.child.declare(), self.indentLevel)
1122 def define(self):
1123 defn = self.child.define()
1124 if self.declareOnly:
1125 return defn
1126 else:
1127 return indent(defn, self.indentLevel)
1130 class CGWrapper(CGThing):
1132 Generic CGThing that wraps other CGThings with pre and post text.
1135 def __init__(
1136 self,
1137 child,
1138 pre="",
1139 post="",
1140 declarePre=None,
1141 declarePost=None,
1142 definePre=None,
1143 definePost=None,
1144 declareOnly=False,
1145 defineOnly=False,
1146 reindent=False,
1148 CGThing.__init__(self)
1149 self.child = child
1150 self.declarePre = declarePre or pre
1151 self.declarePost = declarePost or post
1152 self.definePre = definePre or pre
1153 self.definePost = definePost or post
1154 self.declareOnly = declareOnly
1155 self.defineOnly = defineOnly
1156 self.reindent = reindent
1158 def declare(self):
1159 if self.defineOnly:
1160 return ""
1161 decl = self.child.declare()
1162 if self.reindent:
1163 decl = self.reindentString(decl, self.declarePre)
1164 return self.declarePre + decl + self.declarePost
1166 def define(self):
1167 if self.declareOnly:
1168 return ""
1169 defn = self.child.define()
1170 if self.reindent:
1171 defn = self.reindentString(defn, self.definePre)
1172 return self.definePre + defn + self.definePost
1174 @staticmethod
1175 def reindentString(stringToIndent, widthString):
1176 # We don't use lineStartDetector because we don't want to
1177 # insert whitespace at the beginning of our _first_ line.
1178 # Use the length of the last line of width string, in case
1179 # it is a multiline string.
1180 lastLineWidth = len(widthString.splitlines()[-1])
1181 return stripTrailingWhitespace(
1182 stringToIndent.replace("\n", "\n" + (" " * lastLineWidth))
1185 def deps(self):
1186 return self.child.deps()
1189 class CGIfWrapper(CGList):
1190 def __init__(self, child, condition):
1191 CGList.__init__(
1192 self,
1194 CGWrapper(
1195 CGGeneric(condition), pre="if (", post=") {\n", reindent=True
1197 CGIndenter(child),
1198 CGGeneric("}\n"),
1203 class CGIfElseWrapper(CGList):
1204 def __init__(self, condition, ifTrue, ifFalse):
1205 CGList.__init__(
1206 self,
1208 CGWrapper(
1209 CGGeneric(condition), pre="if (", post=") {\n", reindent=True
1211 CGIndenter(ifTrue),
1212 CGGeneric("} else {\n"),
1213 CGIndenter(ifFalse),
1214 CGGeneric("}\n"),
1219 class CGElseChain(CGThing):
1221 Concatenate if statements in an if-else-if-else chain.
1224 def __init__(self, children):
1225 self.children = [c for c in children if c is not None]
1227 def declare(self):
1228 assert False
1230 def define(self):
1231 if not self.children:
1232 return ""
1233 s = self.children[0].define()
1234 assert s.endswith("\n")
1235 for child in self.children[1:]:
1236 code = child.define()
1237 assert code.startswith("if") or code.startswith("{")
1238 assert code.endswith("\n")
1239 s = s.rstrip() + " else " + code
1240 return s
1243 class CGTemplatedType(CGWrapper):
1244 def __init__(self, templateName, child, isConst=False, isReference=False):
1245 if isinstance(child, list):
1246 child = CGList(child, ", ")
1247 const = "const " if isConst else ""
1248 pre = "%s%s<" % (const, templateName)
1249 ref = "&" if isReference else ""
1250 post = ">%s" % ref
1251 CGWrapper.__init__(self, child, pre=pre, post=post)
1254 class CGNamespace(CGThing):
1256 Generates namespace block that wraps other CGThings.
1259 def __init__(self, namespace, child):
1260 CGThing.__init__(self)
1261 self.child = child
1262 self.pre = "namespace %s {\n" % namespace
1263 self.post = "} // namespace %s\n" % namespace
1265 def declare(self):
1266 decl = self.child.declare()
1267 if len(decl.strip()) == 0:
1268 return ""
1269 return self.pre + decl + self.post
1271 def define(self):
1272 defn = self.child.define()
1273 if len(defn.strip()) == 0:
1274 return ""
1275 return self.pre + defn + self.post
1277 def deps(self):
1278 return self.child.deps()
1280 @staticmethod
1281 def build(namespaces, child):
1283 Static helper method to build multiple wrapped namespaces.
1285 if not namespaces:
1286 return CGWrapper(child)
1287 return CGNamespace("::".join(namespaces), child)
1290 class CGIncludeGuard(CGWrapper):
1292 Generates include guards for a header.
1295 def __init__(self, prefix, child):
1296 """|prefix| is the filename without the extension."""
1297 define = "DOM_%s_H_" % prefix.upper()
1298 CGWrapper.__init__(
1299 self,
1300 child,
1301 declarePre="#ifndef %s\n#define %s\n\n" % (define, define),
1302 declarePost="\n#endif // %s\n" % define,
1306 class CGHeaders(CGWrapper):
1308 Generates the appropriate include statements.
1311 def __init__(
1312 self,
1313 descriptors,
1314 dictionaries,
1315 callbacks,
1316 callbackDescriptors,
1317 declareIncludes,
1318 defineIncludes,
1319 prefix,
1320 child,
1321 config=None,
1322 jsImplementedDescriptors=[],
1325 Builds a set of includes to cover |descriptors|.
1327 Also includes the files in |declareIncludes| in the header
1328 file and the files in |defineIncludes| in the .cpp.
1330 |prefix| contains the basename of the file that we generate include
1331 statements for.
1334 # Determine the filenames for which we need headers.
1335 interfaceDeps = [d.interface for d in descriptors]
1336 ancestors = []
1337 for iface in interfaceDeps:
1338 if iface.parent:
1339 # We're going to need our parent's prototype, to use as the
1340 # prototype of our prototype object.
1341 ancestors.append(iface.parent)
1342 # And if we have an interface object, we'll need the nearest
1343 # ancestor with an interface object too, so we can use its
1344 # interface object as the proto of our interface object.
1345 if iface.hasInterfaceObject():
1346 parent = iface.parent
1347 while parent and not parent.hasInterfaceObject():
1348 parent = parent.parent
1349 if parent:
1350 ancestors.append(parent)
1351 interfaceDeps.extend(ancestors)
1353 # Include parent interface headers needed for default toJSON code.
1354 jsonInterfaceParents = []
1355 for desc in descriptors:
1356 if not desc.hasDefaultToJSON:
1357 continue
1358 parent = desc.interface.parent
1359 while parent:
1360 parentDesc = desc.getDescriptor(parent.identifier.name)
1361 if parentDesc.hasDefaultToJSON:
1362 jsonInterfaceParents.append(parentDesc.interface)
1363 parent = parent.parent
1364 interfaceDeps.extend(jsonInterfaceParents)
1366 bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps)
1368 # Grab all the implementation declaration files we need.
1369 implementationIncludes = set(
1370 d.headerFile for d in descriptors if d.needsHeaderInclude()
1373 # Now find all the things we'll need as arguments because we
1374 # need to wrap or unwrap them.
1375 bindingHeaders = set()
1376 declareIncludes = set(declareIncludes)
1378 def addHeadersForType(typeAndPossibleOriginType):
1380 Add the relevant headers for this type. We use its origin type, if
1381 passed, to decide what to do with interface types.
1383 t, originType = typeAndPossibleOriginType
1384 isFromDictionary = originType and originType.isDictionary()
1385 isFromCallback = originType and originType.isCallback()
1386 # Dictionaries have members that need to be actually
1387 # declared, not just forward-declared.
1388 # Callbacks have nullable union arguments that need to be actually
1389 # declared, not just forward-declared.
1390 if isFromDictionary:
1391 headerSet = declareIncludes
1392 elif isFromCallback and t.nullable() and t.isUnion():
1393 headerSet = declareIncludes
1394 else:
1395 headerSet = bindingHeaders
1396 # Strip off outer layers and add headers they might require. (This
1397 # is conservative: only nullable non-pointer types need Nullable.h;
1398 # only sequences or observable arrays outside unions need
1399 # ForOfIterator.h; only functions that return, and attributes that
1400 # are, sequences or observable arrays in interfaces need Array.h, &c.)
1401 unrolled = t
1402 while True:
1403 if idlTypeNeedsCallContext(unrolled):
1404 bindingHeaders.add("mozilla/dom/BindingCallContext.h")
1405 if unrolled.nullable():
1406 headerSet.add("mozilla/dom/Nullable.h")
1407 elif unrolled.isSequence() or unrolled.isObservableArray():
1408 bindingHeaders.add("js/Array.h")
1409 bindingHeaders.add("js/ForOfIterator.h")
1410 if unrolled.isObservableArray():
1411 bindingHeaders.add("mozilla/dom/ObservableArrayProxyHandler.h")
1412 else:
1413 break
1414 unrolled = unrolled.inner
1415 if unrolled.isUnion():
1416 headerSet.add(self.getUnionDeclarationFilename(config, unrolled))
1417 for t in unrolled.flatMemberTypes:
1418 addHeadersForType((t, None))
1419 elif unrolled.isPromise():
1420 # See comment in the isInterface() case for why we add
1421 # Promise.h to headerSet, not bindingHeaders.
1422 headerSet.add("mozilla/dom/Promise.h")
1423 # We need ToJSValue to do the Promise to JS conversion.
1424 bindingHeaders.add("mozilla/dom/ToJSValue.h")
1425 elif unrolled.isInterface():
1426 if unrolled.isSpiderMonkeyInterface():
1427 bindingHeaders.add("jsfriendapi.h")
1428 if jsImplementedDescriptors:
1429 # Since we can't forward-declare typed array types
1430 # (because they're typedefs), we have to go ahead and
1431 # just include their header if we need to have functions
1432 # taking references to them declared in that header.
1433 headerSet = declareIncludes
1434 headerSet.add("mozilla/dom/TypedArray.h")
1435 else:
1436 try:
1437 typeDesc = config.getDescriptor(unrolled.inner.identifier.name)
1438 except NoSuchDescriptorError:
1439 return
1440 # Dictionaries with interface members rely on the
1441 # actual class definition of that interface member
1442 # being visible in the binding header, because they
1443 # store them in RefPtr and have inline
1444 # constructors/destructors.
1446 # XXXbz maybe dictionaries with interface members
1447 # should just have out-of-line constructors and
1448 # destructors?
1449 headerSet.add(typeDesc.headerFile)
1450 elif unrolled.isDictionary():
1451 headerSet.add(self.getDeclarationFilename(unrolled.inner))
1452 # And if it needs rooting, we need RootedDictionary too
1453 if typeNeedsRooting(unrolled):
1454 headerSet.add("mozilla/dom/RootedDictionary.h")
1455 elif unrolled.isCallback():
1456 headerSet.add(self.getDeclarationFilename(unrolled.callback))
1457 elif unrolled.isFloat() and not unrolled.isUnrestricted():
1458 # Restricted floats are tested for finiteness
1459 bindingHeaders.add("mozilla/FloatingPoint.h")
1460 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
1461 elif unrolled.isEnum():
1462 filename = self.getDeclarationFilename(unrolled.inner)
1463 declareIncludes.add(filename)
1464 elif unrolled.isPrimitive():
1465 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
1466 elif unrolled.isRecord():
1467 if isFromDictionary or jsImplementedDescriptors:
1468 declareIncludes.add("mozilla/dom/Record.h")
1469 else:
1470 bindingHeaders.add("mozilla/dom/Record.h")
1471 # Also add headers for the type the record is
1472 # parametrized over, if needed.
1473 addHeadersForType((t.inner, originType if isFromDictionary else None))
1475 for t in getAllTypes(
1476 descriptors + callbackDescriptors, dictionaries, callbacks
1478 addHeadersForType(t)
1480 def addHeaderForFunc(func, desc):
1481 if func is None:
1482 return
1483 # Include the right class header, which we can only do
1484 # if this is a class member function.
1485 if desc is not None and not desc.headerIsDefault:
1486 # An explicit header file was provided, assume that we know
1487 # what we're doing.
1488 return
1490 if "::" in func:
1491 # Strip out the function name and convert "::" to "/"
1492 bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")
1494 # Now for non-callback descriptors make sure we include any
1495 # headers needed by Func declarations and other things like that.
1496 for desc in descriptors:
1497 # If this is an iterator or an async iterator interface generated
1498 # for a separate iterable interface, skip generating type includes,
1499 # as we have what we need in IterableIterator.h
1500 if (
1501 desc.interface.isIteratorInterface()
1502 or desc.interface.isAsyncIteratorInterface()
1504 continue
1506 for m in desc.interface.members:
1507 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), desc)
1508 staticTypeOverride = PropertyDefiner.getStringAttr(
1509 m, "StaticClassOverride"
1511 if staticTypeOverride:
1512 bindingHeaders.add("/".join(staticTypeOverride.split("::")) + ".h")
1513 # getExtendedAttribute() returns a list, extract the entry.
1514 funcList = desc.interface.getExtendedAttribute("Func")
1515 if funcList is not None:
1516 addHeaderForFunc(funcList[0], desc)
1518 if desc.interface.maplikeOrSetlikeOrIterable:
1519 # We need ToJSValue.h for maplike/setlike type conversions
1520 bindingHeaders.add("mozilla/dom/ToJSValue.h")
1521 # Add headers for the key and value types of the
1522 # maplike/setlike/iterable, since they'll be needed for
1523 # convenience functions
1524 if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType():
1525 addHeadersForType(
1526 (desc.interface.maplikeOrSetlikeOrIterable.keyType, None)
1528 if desc.interface.maplikeOrSetlikeOrIterable.hasValueType():
1529 addHeadersForType(
1530 (desc.interface.maplikeOrSetlikeOrIterable.valueType, None)
1533 for d in dictionaries:
1534 if d.parent:
1535 declareIncludes.add(self.getDeclarationFilename(d.parent))
1536 bindingHeaders.add(self.getDeclarationFilename(d))
1537 for m in d.members:
1538 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), None)
1539 # No need to worry about Func on members of ancestors, because that
1540 # will happen automatically in whatever files those ancestors live
1541 # in.
1543 for c in callbacks:
1544 bindingHeaders.add(self.getDeclarationFilename(c))
1546 for c in callbackDescriptors:
1547 bindingHeaders.add(self.getDeclarationFilename(c.interface))
1549 if len(callbacks) != 0:
1550 # We need CallbackFunction to serve as our parent class
1551 declareIncludes.add("mozilla/dom/CallbackFunction.h")
1552 # And we need ToJSValue.h so we can wrap "this" objects
1553 declareIncludes.add("mozilla/dom/ToJSValue.h")
1555 if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0:
1556 # We need CallbackInterface to serve as our parent class
1557 declareIncludes.add("mozilla/dom/CallbackInterface.h")
1558 # And we need ToJSValue.h so we can wrap "this" objects
1559 declareIncludes.add("mozilla/dom/ToJSValue.h")
1561 # Also need to include the headers for ancestors of
1562 # JS-implemented interfaces.
1563 for jsImplemented in jsImplementedDescriptors:
1564 jsParent = jsImplemented.interface.parent
1565 if jsParent:
1566 parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name)
1567 declareIncludes.add(parentDesc.jsImplParentHeader)
1569 # Now make sure we're not trying to include the header from inside itself
1570 declareIncludes.discard(prefix + ".h")
1572 # Let the machinery do its thing.
1573 def _includeString(includes):
1574 def headerName(include):
1575 # System headers are specified inside angle brackets.
1576 if include.startswith("<"):
1577 return include
1578 # Non-system headers need to be placed in quotes.
1579 return '"%s"' % include
1581 return "".join(["#include %s\n" % headerName(i) for i in includes]) + "\n"
1583 CGWrapper.__init__(
1584 self,
1585 child,
1586 declarePre=_includeString(sorted(declareIncludes)),
1587 definePre=_includeString(
1588 sorted(
1589 set(defineIncludes)
1590 | bindingIncludes
1591 | bindingHeaders
1592 | implementationIncludes
1597 @staticmethod
1598 def getDeclarationFilename(decl):
1599 # Use our local version of the header, not the exported one, so that
1600 # test bindings, which don't export, will work correctly.
1601 basename = os.path.basename(decl.filename)
1602 return basename.replace(".webidl", "Binding.h")
1604 @staticmethod
1605 def getUnionDeclarationFilename(config, unionType):
1606 assert unionType.isUnion()
1607 assert unionType.unroll() == unionType
1608 # If a union is "defined" in multiple files, it goes in UnionTypes.h.
1609 if len(config.filenamesPerUnion[unionType.name]) > 1:
1610 return "mozilla/dom/UnionTypes.h"
1611 # If a union is defined by a built-in typedef, it also goes in
1612 # UnionTypes.h.
1613 assert len(config.filenamesPerUnion[unionType.name]) == 1
1614 if "<unknown>" in config.filenamesPerUnion[unionType.name]:
1615 return "mozilla/dom/UnionTypes.h"
1616 return CGHeaders.getDeclarationFilename(unionType)
1619 def SortedDictValues(d):
1621 Returns a list of values from the dict sorted by key.
1623 return [v for k, v in sorted(d.items())]
1626 def UnionsForFile(config, webIDLFile):
1628 Returns a list of union types for all union types that are only used in
1629 webIDLFile. If webIDLFile is None this will return the list of tuples for
1630 union types that are used in more than one WebIDL file.
1632 return config.unionsPerFilename.get(webIDLFile, [])
1635 def UnionTypes(unionTypes, config):
1637 The unionTypes argument should be a list of union types. This is typically
1638 the list generated by UnionsForFile.
1640 Returns a tuple containing a set of header filenames to include in
1641 the header for the types in unionTypes, a set of header filenames to
1642 include in the implementation file for the types in unionTypes, a set
1643 of tuples containing a type declaration and a boolean if the type is a
1644 struct for member types of the union, a list of traverse methods,
1645 unlink methods and a list of union types. These last three lists only
1646 contain unique union types.
1649 headers = set()
1650 implheaders = set()
1651 declarations = set()
1652 unionStructs = dict()
1653 traverseMethods = dict()
1654 unlinkMethods = dict()
1656 for t in unionTypes:
1657 name = str(t)
1658 if name not in unionStructs:
1659 unionStructs[name] = t
1661 def addHeadersForType(f):
1662 if f.nullable():
1663 headers.add("mozilla/dom/Nullable.h")
1664 isSequence = f.isSequence()
1665 if isSequence:
1666 # Dealing with sequences requires for-of-compatible
1667 # iteration.
1668 implheaders.add("js/ForOfIterator.h")
1669 # Sequences can always throw "not an object" exceptions.
1670 implheaders.add("mozilla/dom/BindingCallContext.h")
1671 if typeNeedsRooting(f):
1672 headers.add("mozilla/dom/RootedSequence.h")
1673 f = f.unroll()
1674 if idlTypeNeedsCallContext(f):
1675 implheaders.add("mozilla/dom/BindingCallContext.h")
1676 if f.isPromise():
1677 headers.add("mozilla/dom/Promise.h")
1678 # We need ToJSValue to do the Promise to JS conversion.
1679 headers.add("mozilla/dom/ToJSValue.h")
1680 elif f.isInterface():
1681 if f.isSpiderMonkeyInterface():
1682 headers.add("js/RootingAPI.h")
1683 headers.add("js/Value.h")
1684 headers.add("mozilla/dom/TypedArray.h")
1685 else:
1686 try:
1687 typeDesc = config.getDescriptor(f.inner.identifier.name)
1688 except NoSuchDescriptorError:
1689 return
1690 if typeDesc.interface.isCallback() or isSequence:
1691 # Callback interfaces always use strong refs, so
1692 # we need to include the right header to be able
1693 # to Release() in our inlined code.
1695 # Similarly, sequences always contain strong
1696 # refs, so we'll need the header to handler
1697 # those.
1698 headers.add(typeDesc.headerFile)
1699 elif typeDesc.interface.identifier.name == "WindowProxy":
1700 # In UnionTypes.h we need to see the declaration of the
1701 # WindowProxyHolder that we use to store the WindowProxy, so
1702 # we have its sizeof and know how big to make our union.
1703 headers.add(typeDesc.headerFile)
1704 else:
1705 declarations.add((typeDesc.nativeType, False))
1706 implheaders.add(typeDesc.headerFile)
1707 elif f.isDictionary():
1708 # For a dictionary, we need to see its declaration in
1709 # UnionTypes.h so we have its sizeof and know how big to
1710 # make our union.
1711 headers.add(CGHeaders.getDeclarationFilename(f.inner))
1712 # And if it needs rooting, we need RootedDictionary too
1713 if typeNeedsRooting(f):
1714 headers.add("mozilla/dom/RootedDictionary.h")
1715 elif f.isFloat() and not f.isUnrestricted():
1716 # Restricted floats are tested for finiteness
1717 implheaders.add("mozilla/FloatingPoint.h")
1718 implheaders.add("mozilla/dom/PrimitiveConversions.h")
1719 elif f.isEnum():
1720 # Need to see the actual definition of the enum,
1721 # unfortunately.
1722 headers.add(CGHeaders.getDeclarationFilename(f.inner))
1723 elif f.isPrimitive():
1724 implheaders.add("mozilla/dom/PrimitiveConversions.h")
1725 elif f.isCallback():
1726 # Callbacks always use strong refs, so we need to include
1727 # the right header to be able to Release() in our inlined
1728 # code.
1729 headers.add(CGHeaders.getDeclarationFilename(f.callback))
1730 elif f.isRecord():
1731 headers.add("mozilla/dom/Record.h")
1732 # And add headers for the type we're parametrized over
1733 addHeadersForType(f.inner)
1734 # And if it needs rooting, we need RootedRecord too
1735 if typeNeedsRooting(f):
1736 headers.add("mozilla/dom/RootedRecord.h")
1738 implheaders.add(CGHeaders.getUnionDeclarationFilename(config, t))
1739 for f in t.flatMemberTypes:
1740 assert not f.nullable()
1741 addHeadersForType(f)
1743 if idlTypeNeedsCycleCollection(t):
1744 declarations.add(
1745 ("mozilla::dom::%s" % CGUnionStruct.unionTypeName(t, True), False)
1747 traverseMethods[name] = CGCycleCollectionTraverseForOwningUnionMethod(t)
1748 unlinkMethods[name] = CGCycleCollectionUnlinkForOwningUnionMethod(t)
1750 # The order of items in CGList is important.
1751 # Since the union structs friend the unlinkMethods, the forward-declaration
1752 # for these methods should come before the class declaration. Otherwise
1753 # some compilers treat the friend declaration as a forward-declaration in
1754 # the class scope.
1755 return (
1756 headers,
1757 implheaders,
1758 declarations,
1759 SortedDictValues(traverseMethods),
1760 SortedDictValues(unlinkMethods),
1761 SortedDictValues(unionStructs),
1765 class Argument:
1767 A class for outputting the type and name of an argument
1770 def __init__(self, argType, name, default=None):
1771 self.argType = argType
1772 self.name = name
1773 self.default = default
1775 def declare(self):
1776 string = self.argType + " " + self.name
1777 if self.default is not None:
1778 string += " = " + self.default
1779 return string
1781 def define(self):
1782 return self.argType + " " + self.name
1785 class CGAbstractMethod(CGThing):
1787 An abstract class for generating code for a method. Subclasses
1788 should override definition_body to create the actual code.
1790 descriptor is the descriptor for the interface the method is associated with
1792 name is the name of the method as a string
1794 returnType is the IDLType of the return value
1796 args is a list of Argument objects
1798 inline should be True to generate an inline method, whose body is
1799 part of the declaration.
1801 alwaysInline should be True to generate an inline method annotated with
1802 MOZ_ALWAYS_INLINE.
1804 static should be True to generate a static method, which only has
1805 a definition.
1807 If templateArgs is not None it should be a list of strings containing
1808 template arguments, and the function will be templatized using those
1809 arguments.
1811 canRunScript should be True to generate a MOZ_CAN_RUN_SCRIPT annotation.
1813 signatureOnly should be True to only declare the signature (either in
1814 the header, or if static is True in the cpp file).
1817 def __init__(
1818 self,
1819 descriptor,
1820 name,
1821 returnType,
1822 args,
1823 inline=False,
1824 alwaysInline=False,
1825 static=False,
1826 templateArgs=None,
1827 canRunScript=False,
1828 signatureOnly=False,
1830 CGThing.__init__(self)
1831 self.descriptor = descriptor
1832 self.name = name
1833 self.returnType = returnType
1834 self.args = args
1835 self.inline = inline
1836 self.alwaysInline = alwaysInline
1837 self.static = static
1838 self.templateArgs = templateArgs
1839 self.canRunScript = canRunScript
1840 self.signatureOnly = signatureOnly
1842 def _argstring(self, declare):
1843 return ", ".join([a.declare() if declare else a.define() for a in self.args])
1845 def _template(self):
1846 if self.templateArgs is None:
1847 return ""
1848 return "template <%s>\n" % ", ".join(self.templateArgs)
1850 def _decorators(self):
1851 decorators = []
1852 if self.canRunScript:
1853 decorators.append("MOZ_CAN_RUN_SCRIPT")
1854 if self.alwaysInline:
1855 decorators.append("MOZ_ALWAYS_INLINE")
1856 elif self.inline:
1857 decorators.append("inline")
1858 if self.static:
1859 decorators.append("static")
1860 decorators.append(self.returnType)
1861 maybeNewline = " " if self.inline else "\n"
1862 return " ".join(decorators) + maybeNewline
1864 def signature(self):
1865 return "%s%s%s(%s);\n" % (
1866 self._template(),
1867 self._decorators(),
1868 self.name,
1869 self._argstring(True),
1872 def declare(self):
1873 if self.static:
1874 return ""
1875 if self.inline:
1876 return self._define(True)
1877 return self.signature()
1879 def indent_body(self, body):
1881 Indent the code returned by self.definition_body(). Most classes
1882 simply indent everything two spaces. This is here for
1883 CGRegisterProtos, which needs custom indentation.
1885 return indent(body)
1887 def _define(self, fromDeclare=False):
1888 return (
1889 self.definition_prologue(fromDeclare)
1890 + self.indent_body(self.definition_body())
1891 + self.definition_epilogue()
1894 def define(self):
1895 if self.signatureOnly:
1896 if self.static:
1897 # self.static makes us not output anything in the header, so output the signature here.
1898 return self.signature()
1899 return ""
1900 return "" if (self.inline and not self.static) else self._define()
1902 def definition_prologue(self, fromDeclare):
1903 error_reporting_label = self.error_reporting_label()
1904 if error_reporting_label:
1905 # We're going to want a BindingCallContext. Rename our JSContext*
1906 # arg accordingly.
1907 i = 0
1908 while i < len(self.args):
1909 arg = self.args[i]
1910 if arg.argType == "JSContext*":
1911 cxname = arg.name
1912 self.args[i] = Argument(arg.argType, "cx_", arg.default)
1913 break
1914 i += 1
1915 if i == len(self.args):
1916 raise TypeError("Must have a JSContext* to create a BindingCallContext")
1918 prologue = "%s%s%s(%s)\n{\n" % (
1919 self._template(),
1920 self._decorators(),
1921 self.name,
1922 self._argstring(fromDeclare),
1924 if error_reporting_label:
1925 prologue += indent(
1926 fill(
1928 BindingCallContext ${cxname}(cx_, ${label});
1929 """,
1930 cxname=cxname,
1931 label=error_reporting_label,
1935 profiler_label = self.auto_profiler_label()
1936 if profiler_label:
1937 prologue += indent(profiler_label) + "\n"
1939 return prologue
1941 def definition_epilogue(self):
1942 return "}\n"
1944 def definition_body(self):
1945 assert False # Override me!
1948 Override this method to return a pair of (descriptive string, name of a
1949 JSContext* variable) in order to generate a profiler label for this method.
1952 def auto_profiler_label(self):
1953 return None # Override me!
1956 Override this method to return a string to be used as the label for a
1957 BindingCallContext. If this does not return None, one of the arguments of
1958 this method must be of type 'JSContext*'. Its name will be replaced with
1959 'cx_' and a BindingCallContext named 'cx' will be instantiated with the
1960 given label.
1963 def error_reporting_label(self):
1964 return None # Override me!
1967 class CGAbstractStaticMethod(CGAbstractMethod):
1969 Abstract base class for codegen of implementation-only (no
1970 declaration) static methods.
1973 def __init__(self, descriptor, name, returnType, args, canRunScript=False):
1974 CGAbstractMethod.__init__(
1975 self,
1976 descriptor,
1977 name,
1978 returnType,
1979 args,
1980 inline=False,
1981 static=True,
1982 canRunScript=canRunScript,
1986 class CGAbstractClassHook(CGAbstractStaticMethod):
1988 Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
1989 'this' unwrapping as it assumes that the unwrapped type is always known.
1992 def __init__(self, descriptor, name, returnType, args):
1993 CGAbstractStaticMethod.__init__(self, descriptor, name, returnType, args)
1995 def definition_body_prologue(self):
1996 return "%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" % (
1997 self.descriptor.nativeType,
1998 self.descriptor.nativeType,
2001 def definition_body(self):
2002 return self.definition_body_prologue() + self.generate_code()
2004 def generate_code(self):
2005 assert False # Override me!
2008 class CGAddPropertyHook(CGAbstractClassHook):
2010 A hook for addProperty, used to preserve our wrapper from GC.
2013 def __init__(self, descriptor):
2014 args = [
2015 Argument("JSContext*", "cx"),
2016 Argument("JS::Handle<JSObject*>", "obj"),
2017 Argument("JS::Handle<jsid>", "id"),
2018 Argument("JS::Handle<JS::Value>", "val"),
2020 CGAbstractClassHook.__init__(
2021 self, descriptor, ADDPROPERTY_HOOK_NAME, "bool", args
2024 def generate_code(self):
2025 assert self.descriptor.wrapperCache
2026 # This hook is also called by TryPreserveWrapper on non-nsISupports
2027 # cycle collected objects, so if addProperty is ever changed to do
2028 # anything more or less than preserve the wrapper, TryPreserveWrapper
2029 # will need to be changed.
2030 return dedent(
2032 // We don't want to preserve if we don't have a wrapper, and we
2033 // obviously can't preserve if we're not initialized.
2034 if (self && self->GetWrapperPreserveColor()) {
2035 PreserveWrapper(self);
2037 return true;
2042 class CGGetWrapperCacheHook(CGAbstractClassHook):
2044 A hook for GetWrapperCache, used by HasReleasedWrapper to get the
2045 nsWrapperCache pointer for a non-nsISupports object.
2048 def __init__(self, descriptor):
2049 args = [Argument("JS::Handle<JSObject*>", "obj")]
2050 CGAbstractClassHook.__init__(
2051 self, descriptor, GETWRAPPERCACHE_HOOK_NAME, "nsWrapperCache*", args
2054 def generate_code(self):
2055 assert self.descriptor.wrapperCache
2056 return dedent(
2058 return self;
2063 class CGDefineHTMLAttributeSlots(CGThing):
2065 Function to get the slots object for reflected HTML attributes that return
2066 a FrozenArray<Element> value.
2069 def __init__(self, descriptor):
2070 self.descriptor = descriptor
2071 CGThing.__init__(self)
2073 def declare(self):
2074 atts = self.descriptor.interface.reflectedHTMLAttributesReturningFrozenArray
2075 return fill(
2077 using ReflectedHTMLAttributeSlots = binding_detail::ReflectedHTMLAttributeSlots<${slotIndex}, ${xraySlotIndex}, ${arrayLength}>;
2078 """,
2079 slotIndex=reservedSlot(atts.slotIndex, False),
2080 xraySlotIndex=reservedSlot(atts.slotIndex, True),
2081 arrayLength=atts.totalMembersInSlots,
2084 def define(self):
2085 return ""
2088 def finalizeHook(descriptor, hookName, gcx, obj):
2089 finalize = "JS::SetReservedSlot(%s, DOM_OBJECT_SLOT, JS::UndefinedValue());\n" % obj
2090 if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
2091 finalize += fill(
2093 // Either our proxy created an expando object or not. If it did,
2094 // then we would have preserved ourselves, and hence if we're going
2095 // away so is our C++ object and we should reset its expando value.
2096 // It's possible that in this situation the C++ object's reflector
2097 // pointer has been nulled out, but if not it's pointing to us. If
2098 // our proxy did _not_ create an expando object then it's possible
2099 // that we're no longer the reflector for our C++ object (and
2100 // incremental finalization is finally getting to us), and that in
2101 // the meantime the new reflector has created an expando object.
2102 // In that case we do NOT want to clear the expando pointer in the
2103 // C++ object.
2105 // It's important to do this before we ClearWrapper, of course.
2106 JSObject* reflector = self->GetWrapperMaybeDead();
2107 if (!reflector || reflector == ${obj}) {
2108 self->mExpandoAndGeneration.expando = JS::UndefinedValue();
2110 """,
2111 obj=obj,
2113 for m in descriptor.interface.members:
2114 if m.isAttr() and m.type.isObservableArray():
2115 finalize += fill(
2118 JS::Value val = JS::GetReservedSlot(obj, ${slot});
2119 if (!val.isUndefined()) {
2120 JSObject* obj = &val.toObject();
2121 js::SetProxyReservedSlot(obj, OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT, JS::UndefinedValue());
2124 """,
2125 slot=memberReservedSlot(m, descriptor),
2127 iface = getReflectedHTMLAttributesIface(descriptor)
2128 if iface:
2129 finalize += "%s::ReflectedHTMLAttributeSlots::Finalize(%s);\n" % (
2130 toBindingNamespace(iface.identifier.name),
2131 obj,
2133 if descriptor.wrapperCache:
2134 finalize += "ClearWrapper(self, self, %s);\n" % obj
2135 if descriptor.isGlobal():
2136 finalize += "mozilla::dom::FinalizeGlobal(%s, %s);\n" % (gcx, obj)
2137 finalize += fill(
2139 if (size_t mallocBytes = BindingJSObjectMallocBytes(self)) {
2140 JS::RemoveAssociatedMemory(${obj}, mallocBytes,
2141 JS::MemoryUse::DOMBinding);
2143 """,
2144 obj=obj,
2146 finalize += "AddForDeferredFinalization<%s>(self);\n" % descriptor.nativeType
2147 return CGIfWrapper(CGGeneric(finalize), "self")
2150 class CGClassFinalizeHook(CGAbstractClassHook):
2152 A hook for finalize, used to release our native object.
2155 def __init__(self, descriptor):
2156 args = [Argument("JS::GCContext*", "gcx"), Argument("JSObject*", "obj")]
2157 CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, "void", args)
2159 def generate_code(self):
2160 return finalizeHook(
2161 self.descriptor, self.name, self.args[0].name, self.args[1].name
2162 ).define()
2165 def objectMovedHook(descriptor, hookName, obj, old):
2166 assert descriptor.wrapperCache
2167 return fill(
2169 if (self) {
2170 UpdateWrapper(self, self, ${obj}, ${old});
2173 return 0;
2174 """,
2175 obj=obj,
2176 old=old,
2180 class CGClassObjectMovedHook(CGAbstractClassHook):
2182 A hook for objectMovedOp, used to update the wrapper cache when an object it
2183 is holding moves.
2186 def __init__(self, descriptor):
2187 args = [Argument("JSObject*", "obj"), Argument("JSObject*", "old")]
2188 CGAbstractClassHook.__init__(
2189 self, descriptor, OBJECT_MOVED_HOOK_NAME, "size_t", args
2192 def generate_code(self):
2193 return objectMovedHook(
2194 self.descriptor, self.name, self.args[0].name, self.args[1].name
2198 def JSNativeArguments():
2199 return [
2200 Argument("JSContext*", "cx"),
2201 Argument("unsigned", "argc"),
2202 Argument("JS::Value*", "vp"),
2206 class CGClassConstructor(CGAbstractStaticMethod):
2208 JS-visible constructor for our objects
2211 def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
2212 CGAbstractStaticMethod.__init__(
2213 self, descriptor, name, "bool", JSNativeArguments()
2215 self._ctor = ctor
2217 def define(self):
2218 if not self._ctor:
2219 return ""
2220 return CGAbstractStaticMethod.define(self)
2222 def definition_body(self):
2223 return self.generate_code()
2225 def generate_code(self):
2226 if self._ctor.isHTMLConstructor():
2227 # We better have a prototype object. Otherwise our proto
2228 # id won't make sense.
2229 assert self.descriptor.interface.hasInterfacePrototypeObject()
2230 # We also better have a constructor object, if this is
2231 # getting called!
2232 assert self.descriptor.interface.hasInterfaceObject()
2233 # We can't just pass null for the CreateInterfaceObjects callback,
2234 # because our newTarget might be in a different compartment, in
2235 # which case we'll need to look up constructor objects in that
2236 # compartment.
2237 return fill(
2239 return HTMLConstructor(cx, argc, vp,
2240 constructors::id::${name},
2241 prototypes::id::${name},
2242 CreateInterfaceObjects);
2243 """,
2244 name=self.descriptor.name,
2247 # If the interface is already SecureContext, notify getConditionList to skip that check,
2248 # because the constructor won't be exposed in non-secure contexts to start with.
2249 alreadySecureContext = self.descriptor.interface.getExtendedAttribute(
2250 "SecureContext"
2253 # We want to throw if any of the conditions returned by getConditionList are false.
2254 conditionsCheck = ""
2255 rawConditions = getRawConditionList(
2256 self._ctor, "cx", "obj", alreadySecureContext
2258 if len(rawConditions) > 0:
2259 notConditions = " ||\n".join("!" + cond for cond in rawConditions)
2260 failedCheckAction = CGGeneric("return ThrowingConstructor(cx, argc, vp);\n")
2261 conditionsCheck = (
2262 CGIfWrapper(failedCheckAction, notConditions).define() + "\n"
2265 # Additionally, we want to throw if a caller does a bareword invocation
2266 # of a constructor without |new|.
2267 ctorName = GetConstructorNameForReporting(self.descriptor, self._ctor)
2269 preamble = fill(
2271 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2272 JS::Rooted<JSObject*> obj(cx, &args.callee());
2273 $*{conditionsCheck}
2274 if (!args.isConstructing()) {
2275 return ThrowConstructorWithoutNew(cx, "${ctorName}");
2278 JS::Rooted<JSObject*> desiredProto(cx);
2279 if (!GetDesiredProto(cx, args,
2280 prototypes::id::${name},
2281 CreateInterfaceObjects,
2282 &desiredProto)) {
2283 return false;
2285 """,
2286 conditionsCheck=conditionsCheck,
2287 ctorName=ctorName,
2288 name=self.descriptor.name,
2291 name = self._ctor.identifier.name
2292 nativeName = MakeNativeName(self.descriptor.binaryNameFor(name, True))
2293 callGenerator = CGMethodCall(
2294 nativeName, True, self.descriptor, self._ctor, isConstructor=True
2296 return preamble + "\n" + callGenerator.define()
2298 def auto_profiler_label(self):
2299 return fill(
2301 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
2302 "${ctorName}", "constructor", DOM, cx,
2303 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
2304 """,
2305 ctorName=GetConstructorNameForReporting(self.descriptor, self._ctor),
2308 def error_reporting_label(self):
2309 return CGSpecializedMethod.error_reporting_label_helper(
2310 self.descriptor, self._ctor, isConstructor=True
2314 def LegacyFactoryFunctionName(m):
2315 return "_" + m.identifier.name
2318 class CGLegacyFactoryFunctions(CGThing):
2319 def __init__(self, descriptor):
2320 self.descriptor = descriptor
2321 CGThing.__init__(self)
2323 def declare(self):
2324 return ""
2326 def define(self):
2327 if len(self.descriptor.interface.legacyFactoryFunctions) == 0:
2328 return ""
2330 constructorID = "constructors::id::"
2331 if self.descriptor.interface.hasInterfaceObject():
2332 constructorID += self.descriptor.name
2333 else:
2334 constructorID += "_ID_Count"
2336 legacyFactoryFunctions = ""
2337 for n in self.descriptor.interface.legacyFactoryFunctions:
2338 legacyFactoryFunctions += (
2339 '{ "%s", { %s, &sLegacyFactoryFunctionNativePropertyHooks }, %i },\n'
2340 % (n.identifier.name, LegacyFactoryFunctionName(n), methodLength(n))
2343 return fill(
2345 bool sLegacyFactoryFunctionNativePropertiesInited = true;
2346 const NativePropertyHooks sLegacyFactoryFunctionNativePropertyHooks = {
2347 nullptr,
2348 { nullptr, nullptr, &sLegacyFactoryFunctionNativePropertiesInited },
2349 prototypes::id::${name},
2350 ${constructorID},
2351 nullptr
2354 static const LegacyFactoryFunction legacyFactoryFunctions[] = {
2355 $*{legacyFactoryFunctions}
2357 """,
2358 name=self.descriptor.name,
2359 constructorID=constructorID,
2360 legacyFactoryFunctions=legacyFactoryFunctions,
2364 def isChromeOnly(m):
2365 return m.getExtendedAttribute("ChromeOnly")
2368 def prefIdentifier(pref):
2369 return pref.replace(".", "_").replace("-", "_")
2372 def prefHeader(pref):
2373 return "mozilla/StaticPrefs_%s.h" % pref.partition(".")[0]
2376 def computeGlobalNamesFromExposureSet(exposureSet):
2377 assert exposureSet is None or isinstance(exposureSet, set)
2379 if exposureSet:
2380 # Nonempty set
2381 return " | ".join(map(lambda g: "GlobalNames::%s" % g, sorted(exposureSet)))
2383 return "0"
2386 class MemberCondition:
2388 An object representing the condition for a member to actually be
2389 exposed. Any of the arguments can be None. If not
2390 None, they should have the following types:
2392 pref: The name of the preference.
2393 func: The name of the function.
2394 secureContext: A bool indicating whether a secure context is required.
2395 nonExposedGlobals: A set of names of globals. Can be empty, in which case
2396 it's treated the same way as None.
2397 trial: The name of the origin trial.
2400 def __init__(
2401 self,
2402 pref=None,
2403 func=None,
2404 secureContext=False,
2405 nonExposedGlobals=None,
2406 trial=None,
2408 assert pref is None or isinstance(pref, str)
2409 assert func is None or isinstance(func, str)
2410 assert trial is None or isinstance(trial, str)
2411 assert isinstance(secureContext, bool)
2412 self.pref = pref
2413 if self.pref:
2414 identifier = prefIdentifier(self.pref)
2415 self.prefFuncIndex = "WebIDLPrefIndex::" + identifier
2416 else:
2417 self.prefFuncIndex = "WebIDLPrefIndex::NoPref"
2419 self.secureContext = secureContext
2421 def toFuncPtr(val):
2422 if val is None:
2423 return "nullptr"
2424 return "&" + val
2426 self.func = toFuncPtr(func)
2428 self.nonExposedGlobals = computeGlobalNamesFromExposureSet(nonExposedGlobals)
2430 if trial:
2431 self.trial = "OriginTrial::" + trial
2432 else:
2433 self.trial = "OriginTrial(0)"
2435 def __eq__(self, other):
2436 return (
2437 self.pref == other.pref
2438 and self.func == other.func
2439 and self.secureContext == other.secureContext
2440 and self.nonExposedGlobals == other.nonExposedGlobals
2441 and self.trial == other.trial
2444 def __ne__(self, other):
2445 return not self.__eq__(other)
2447 def hasDisablers(self):
2448 return (
2449 self.pref is not None
2450 or self.secureContext
2451 or self.func != "nullptr"
2452 or self.nonExposedGlobals != "0"
2453 or self.trial != "OriginTrial(0)"
2457 class PropertyDefiner:
2459 A common superclass for defining things on prototype objects.
2461 Subclasses should implement generateArray to generate the actual arrays of
2462 things we're defining. They should also set self.chrome to the list of
2463 things only exposed to chrome and self.regular to the list of things exposed
2464 to both chrome and web pages.
2467 def __init__(self, descriptor, name):
2468 self.descriptor = descriptor
2469 self.name = name
2471 def hasChromeOnly(self):
2472 return len(self.chrome) > 0
2474 def hasNonChromeOnly(self):
2475 return len(self.regular) > 0
2477 def variableName(self, chrome):
2478 if chrome:
2479 if self.hasChromeOnly():
2480 return "sChrome" + self.name
2481 else:
2482 if self.hasNonChromeOnly():
2483 return "s" + self.name
2484 return "nullptr"
2486 def usedForXrays(self):
2487 return self.descriptor.wantsXrays
2489 def length(self, chrome):
2490 return len(self.chrome) if chrome else len(self.regular)
2492 def __str__(self):
2493 # We only need to generate id arrays for things that will end
2494 # up used via ResolveProperty or EnumerateProperties.
2495 str = self.generateArray(self.regular, self.variableName(False))
2496 if self.hasChromeOnly():
2497 str += self.generateArray(self.chrome, self.variableName(True))
2498 return str
2500 @staticmethod
2501 def getStringAttr(member, name):
2502 attr = member.getExtendedAttribute(name)
2503 if attr is None:
2504 return None
2505 # It's a list of strings
2506 assert len(attr) == 1
2507 assert attr[0] is not None
2508 return attr[0]
2510 @staticmethod
2511 def getControllingCondition(interfaceMember, descriptor):
2512 interface = descriptor.interface
2513 nonExposureSet = interface.exposureSet - interfaceMember.exposureSet
2515 trial = PropertyDefiner.getStringAttr(interfaceMember, "Trial")
2516 if trial and interface.identifier.name in ["Window", "Document"]:
2517 raise TypeError(
2518 "[Trial] not yet supported for %s.%s, see bug 1757935"
2519 % (interface.identifier.name, interfaceMember.identifier.name)
2522 return MemberCondition(
2523 PropertyDefiner.getStringAttr(interfaceMember, "Pref"),
2524 PropertyDefiner.getStringAttr(interfaceMember, "Func"),
2525 interfaceMember.getExtendedAttribute("SecureContext") is not None,
2526 nonExposureSet,
2527 trial,
2530 @staticmethod
2531 def generatePrefableArrayValues(
2532 array,
2533 descriptor,
2534 specFormatter,
2535 specTerminator,
2536 getCondition,
2537 getDataTuple,
2538 switchToCondition=None,
2541 This method generates an array of spec entries for interface members. It returns
2542 a tuple containing the array of spec entries and the maximum of the number of
2543 spec entries per condition.
2545 array is an array of interface members.
2547 descriptor is the descriptor for the interface that array contains members of.
2549 specFormatter is a function that takes a single argument, a tuple,
2550 and returns a string, a spec array entry.
2552 specTerminator is a terminator for the spec array (inserted every time
2553 our controlling pref changes and at the end of the array).
2555 getCondition is a callback function that takes an array entry and
2556 returns the corresponding MemberCondition.
2558 getDataTuple is a callback function that takes an array entry and
2559 returns a tuple suitable to be passed to specFormatter.
2561 switchToCondition is a function that takes a MemberCondition and an array of
2562 previously generated spec entries. If None is passed for this function then all
2563 the interface members should return the same value from getCondition.
2566 def unsupportedSwitchToCondition(condition, specs):
2567 # If no specs have been added yet then this is just the first call to
2568 # switchToCondition that we call to avoid putting a specTerminator at the
2569 # front of the list.
2570 if len(specs) == 0:
2571 return
2572 raise "Not supported"
2574 if switchToCondition is None:
2575 switchToCondition = unsupportedSwitchToCondition
2577 specs = []
2578 numSpecsInCurPrefable = 0
2579 maxNumSpecsInPrefable = 0
2581 # So we won't put a specTerminator at the very front of the list:
2582 lastCondition = getCondition(array[0], descriptor)
2584 switchToCondition(lastCondition, specs)
2586 for member in array:
2587 curCondition = getCondition(member, descriptor)
2588 if lastCondition != curCondition:
2589 # Terminate previous list
2590 specs.append(specTerminator)
2591 if numSpecsInCurPrefable > maxNumSpecsInPrefable:
2592 maxNumSpecsInPrefable = numSpecsInCurPrefable
2593 numSpecsInCurPrefable = 0
2594 # And switch to our new condition
2595 switchToCondition(curCondition, specs)
2596 lastCondition = curCondition
2597 # And the actual spec
2598 specs.append(specFormatter(getDataTuple(member, descriptor)))
2599 numSpecsInCurPrefable += 1
2600 if numSpecsInCurPrefable > maxNumSpecsInPrefable:
2601 maxNumSpecsInPrefable = numSpecsInCurPrefable
2602 specs.append(specTerminator)
2604 return (specs, maxNumSpecsInPrefable)
2606 def generatePrefableArray(
2607 self,
2608 array,
2609 name,
2610 specFormatter,
2611 specTerminator,
2612 specType,
2613 getCondition,
2614 getDataTuple,
2617 This method generates our various arrays.
2619 array is an array of interface members as passed to generateArray
2621 name is the name as passed to generateArray
2623 specFormatter is a function that takes a single argument, a tuple,
2624 and returns a string, a spec array entry
2626 specTerminator is a terminator for the spec array (inserted every time
2627 our controlling pref changes and at the end of the array)
2629 specType is the actual typename of our spec
2631 getCondition is a callback function that takes an array entry and
2632 returns the corresponding MemberCondition.
2634 getDataTuple is a callback function that takes an array entry and
2635 returns a tuple suitable to be passed to specFormatter.
2638 # We want to generate a single list of specs, but with specTerminator
2639 # inserted at every point where the pref name controlling the member
2640 # changes. That will make sure the order of the properties as exposed
2641 # on the interface and interface prototype objects does not change when
2642 # pref control is added to members while still allowing us to define all
2643 # the members in the smallest number of JSAPI calls.
2644 assert len(array) != 0
2646 disablers = []
2647 prefableSpecs = []
2649 disablersTemplate = dedent(
2651 static const PrefableDisablers %s_disablers%d = {
2652 %s, %s, %s, %s, %s
2656 prefableWithDisablersTemplate = " { &%s_disablers%d, &%s_specs[%d] }"
2657 prefableWithoutDisablersTemplate = " { nullptr, &%s_specs[%d] }"
2659 def switchToCondition(condition, specs):
2660 # Set up pointers to the new sets of specs inside prefableSpecs
2661 if condition.hasDisablers():
2662 prefableSpecs.append(
2663 prefableWithDisablersTemplate % (name, len(specs), name, len(specs))
2665 disablers.append(
2666 disablersTemplate
2668 name,
2669 len(specs),
2670 condition.prefFuncIndex,
2671 condition.nonExposedGlobals,
2672 toStringBool(condition.secureContext),
2673 condition.trial,
2674 condition.func,
2677 else:
2678 prefableSpecs.append(
2679 prefableWithoutDisablersTemplate % (name, len(specs))
2682 specs, maxNumSpecsInPrefable = self.generatePrefableArrayValues(
2683 array,
2684 self.descriptor,
2685 specFormatter,
2686 specTerminator,
2687 getCondition,
2688 getDataTuple,
2689 switchToCondition,
2691 prefableSpecs.append(" { nullptr, nullptr }")
2693 specType = "const " + specType
2694 arrays = fill(
2696 MOZ_GLOBINIT static ${specType} ${name}_specs[] = {
2697 ${specs}
2700 ${disablers}
2701 static const Prefable<${specType}> ${name}[] = {
2702 ${prefableSpecs}
2705 """,
2706 specType=specType,
2707 name=name,
2708 disablers="\n".join(disablers),
2709 specs=",\n".join(specs),
2710 prefableSpecs=",\n".join(prefableSpecs),
2713 if self.usedForXrays():
2714 arrays = fill(
2716 $*{arrays}
2717 static_assert(${numPrefableSpecs} <= 1ull << NUM_BITS_PROPERTY_INFO_PREF_INDEX,
2718 "We have a prefable index that is >= (1 << NUM_BITS_PROPERTY_INFO_PREF_INDEX)");
2719 static_assert(${maxNumSpecsInPrefable} <= 1ull << NUM_BITS_PROPERTY_INFO_SPEC_INDEX,
2720 "We have a spec index that is >= (1 << NUM_BITS_PROPERTY_INFO_SPEC_INDEX)");
2722 """,
2723 arrays=arrays,
2724 # Minus 1 because there's a list terminator in prefableSpecs.
2725 numPrefableSpecs=len(prefableSpecs) - 1,
2726 maxNumSpecsInPrefable=maxNumSpecsInPrefable,
2729 return arrays
2732 # The length of a method is the minimum of the lengths of the
2733 # argument lists of all its overloads.
2734 def overloadLength(arguments):
2735 i = len(arguments)
2736 while i > 0 and arguments[i - 1].optional:
2737 i -= 1
2738 return i
2741 def methodLength(method):
2742 signatures = method.signatures()
2743 return min(overloadLength(arguments) for retType, arguments in signatures)
2746 def clearableCachedAttrs(descriptor):
2747 return (
2749 for m in descriptor.interface.members
2750 if m.isAttr() and
2751 # Constants should never need clearing!
2752 m.dependsOn != "Nothing" and m.slotIndices is not None
2756 def MakeClearCachedValueNativeName(member):
2757 return "ClearCached%sValue" % MakeNativeName(member.identifier.name)
2760 def IDLToCIdentifier(name):
2761 return name.replace("-", "_")
2764 def EnumerabilityFlags(member):
2765 if member.getExtendedAttribute("NonEnumerable"):
2766 return "0"
2767 return "JSPROP_ENUMERATE"
2770 class MethodDefiner(PropertyDefiner):
2772 A class for defining methods on a prototype object.
2775 def __init__(self, descriptor, name, crossOriginOnly, static, unforgeable=False):
2776 assert not (static and unforgeable)
2777 PropertyDefiner.__init__(self, descriptor, name)
2779 # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
2780 # We should be able to check for special operations without an
2781 # identifier. For now we check if the name starts with __
2783 # Ignore non-static methods for interfaces without a proto object
2784 if descriptor.interface.hasInterfacePrototypeObject() or static:
2785 methods = [
2787 for m in descriptor.interface.members
2788 if m.isMethod()
2789 and m.isStatic() == static
2790 and MemberIsLegacyUnforgeable(m, descriptor) == unforgeable
2791 and (
2792 not crossOriginOnly or m.getExtendedAttribute("CrossOriginCallable")
2794 and not m.isIdentifierLess()
2795 and not m.getExtendedAttribute("Unexposed")
2797 else:
2798 methods = []
2799 self.chrome = []
2800 self.regular = []
2801 for m in methods:
2802 method = self.methodData(m, descriptor)
2804 if m.isStatic():
2805 method["nativeName"] = CppKeywords.checkMethodName(
2806 IDLToCIdentifier(m.identifier.name)
2809 if isChromeOnly(m):
2810 self.chrome.append(method)
2811 else:
2812 self.regular.append(method)
2814 # TODO: Once iterable is implemented, use tiebreak rules instead of
2815 # failing. Also, may be more tiebreak rules to implement once spec bug
2816 # is resolved.
2817 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=28592
2818 def hasIterator(methods, regular):
2819 return any("@@iterator" in m.aliases for m in methods) or any(
2820 "@@iterator" == r["name"] for r in regular
2823 # Check whether we need to output an @@iterator due to having an indexed
2824 # getter. We only do this while outputting non-static and
2825 # non-unforgeable methods, since the @@iterator function will be
2826 # neither.
2827 if not static and not unforgeable and descriptor.supportsIndexedProperties():
2828 if hasIterator(methods, self.regular):
2829 raise TypeError(
2830 "Cannot have indexed getter/attr on "
2831 "interface %s with other members "
2832 "that generate @@iterator, such as "
2833 "maplike/setlike or aliased functions."
2834 % self.descriptor.interface.identifier.name
2836 self.regular.append(
2838 "name": "@@iterator",
2839 "methodInfo": False,
2840 "selfHostedName": "$ArrayValues",
2841 "length": 0,
2842 "flags": "0", # Not enumerable, per spec.
2843 "condition": MemberCondition(),
2847 # Generate the keys/values/entries aliases for value iterables.
2848 maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
2849 if (
2850 not static
2851 and not unforgeable
2852 and maplikeOrSetlikeOrIterable
2853 and maplikeOrSetlikeOrIterable.isIterable()
2854 and maplikeOrSetlikeOrIterable.isValueIterator()
2856 # Add our keys/values/entries/forEach
2857 self.regular.append(
2859 "name": "keys",
2860 "methodInfo": False,
2861 "selfHostedName": "ArrayKeys",
2862 "length": 0,
2863 "flags": "JSPROP_ENUMERATE",
2864 "condition": PropertyDefiner.getControllingCondition(
2865 maplikeOrSetlikeOrIterable, descriptor
2869 self.regular.append(
2871 "name": "values",
2872 "methodInfo": False,
2873 "selfHostedName": "$ArrayValues",
2874 "length": 0,
2875 "flags": "JSPROP_ENUMERATE",
2876 "condition": PropertyDefiner.getControllingCondition(
2877 maplikeOrSetlikeOrIterable, descriptor
2881 self.regular.append(
2883 "name": "entries",
2884 "methodInfo": False,
2885 "selfHostedName": "ArrayEntries",
2886 "length": 0,
2887 "flags": "JSPROP_ENUMERATE",
2888 "condition": PropertyDefiner.getControllingCondition(
2889 maplikeOrSetlikeOrIterable, descriptor
2893 self.regular.append(
2895 "name": "forEach",
2896 "methodInfo": False,
2897 "selfHostedName": "ArrayForEach",
2898 "length": 1,
2899 "flags": "JSPROP_ENUMERATE",
2900 "condition": PropertyDefiner.getControllingCondition(
2901 maplikeOrSetlikeOrIterable, descriptor
2906 if not static:
2907 stringifier = descriptor.operations["Stringifier"]
2908 if stringifier and unforgeable == MemberIsLegacyUnforgeable(
2909 stringifier, descriptor
2911 toStringDesc = {
2912 "name": GetWebExposedName(stringifier, descriptor),
2913 "nativeName": stringifier.identifier.name,
2914 "length": 0,
2915 "flags": "JSPROP_ENUMERATE",
2916 "condition": PropertyDefiner.getControllingCondition(
2917 stringifier, descriptor
2920 if isChromeOnly(stringifier):
2921 self.chrome.append(toStringDesc)
2922 else:
2923 self.regular.append(toStringDesc)
2924 if unforgeable and descriptor.interface.getExtendedAttribute(
2925 "LegacyUnforgeable"
2927 # Synthesize our valueOf method
2928 self.regular.append(
2930 "name": "valueOf",
2931 "selfHostedName": "Object_valueOf",
2932 "methodInfo": False,
2933 "length": 0,
2934 "flags": "0", # readonly/permanent added automatically.
2935 "condition": MemberCondition(),
2939 if descriptor.interface.isJSImplemented():
2940 if static:
2941 if descriptor.interface.hasInterfaceObject():
2942 self.chrome.append(
2944 "name": "_create",
2945 "nativeName": ("%s::_Create" % descriptor.name),
2946 "methodInfo": False,
2947 "length": 2,
2948 "flags": "0",
2949 "condition": MemberCondition(),
2953 self.unforgeable = unforgeable
2955 if static:
2956 if not descriptor.interface.hasInterfaceObject():
2957 # static methods go on the interface object
2958 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
2959 else:
2960 if not descriptor.interface.hasInterfacePrototypeObject():
2961 # non-static methods go on the interface prototype object
2962 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
2964 @staticmethod
2965 def methodData(m, descriptor, overrideFlags=None):
2966 return {
2967 "name": m.identifier.name,
2968 "methodInfo": not m.isStatic(),
2969 "length": methodLength(m),
2970 "flags": (
2971 EnumerabilityFlags(m) if (overrideFlags is None) else overrideFlags
2973 "condition": PropertyDefiner.getControllingCondition(m, descriptor),
2974 "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
2975 "returnsPromise": m.returnsPromise(),
2976 "hasIteratorAlias": "@@iterator" in m.aliases,
2979 @staticmethod
2980 def formatSpec(fields):
2981 if fields[0].startswith("@@"):
2982 fields = (fields[0][2:],) + fields[1:]
2983 return " JS_SYM_FNSPEC(%s, %s, %s, %s, %s, %s)" % fields
2984 return ' JS_FNSPEC("%s", %s, %s, %s, %s, %s)' % fields
2986 @staticmethod
2987 def specData(m, descriptor, unforgeable=False):
2988 def flags(m, unforgeable):
2989 unforgeable = " | JSPROP_PERMANENT | JSPROP_READONLY" if unforgeable else ""
2990 return m["flags"] + unforgeable
2992 if "selfHostedName" in m:
2993 selfHostedName = '"%s"' % m["selfHostedName"]
2994 assert not m.get("methodInfo", True)
2995 accessor = "nullptr"
2996 jitinfo = "nullptr"
2997 else:
2998 selfHostedName = "nullptr"
2999 # When defining symbols, function name may not match symbol name
3000 methodName = m.get("methodName", m["name"])
3001 accessor = m.get("nativeName", IDLToCIdentifier(methodName))
3002 if m.get("methodInfo", True):
3003 if m.get("returnsPromise", False):
3004 exceptionPolicy = "ConvertExceptionsToPromises"
3005 else:
3006 exceptionPolicy = "ThrowExceptions"
3008 # Cast this in case the methodInfo is a
3009 # JSTypedMethodJitInfo.
3010 jitinfo = (
3011 "reinterpret_cast<const JSJitInfo*>(&%s_methodinfo)" % accessor
3013 if m.get("allowCrossOriginThis", False):
3014 accessor = (
3015 "(GenericMethod<CrossOriginThisPolicy, %s>)" % exceptionPolicy
3017 elif descriptor.interface.hasDescendantWithCrossOriginMembers:
3018 accessor = (
3019 "(GenericMethod<MaybeCrossOriginObjectThisPolicy, %s>)"
3020 % exceptionPolicy
3022 elif descriptor.interface.isOnGlobalProtoChain():
3023 accessor = (
3024 "(GenericMethod<MaybeGlobalThisPolicy, %s>)" % exceptionPolicy
3026 else:
3027 accessor = "(GenericMethod<NormalThisPolicy, %s>)" % exceptionPolicy
3028 else:
3029 if m.get("returnsPromise", False):
3030 jitinfo = "&%s_methodinfo" % accessor
3031 accessor = "StaticMethodPromiseWrapper"
3032 else:
3033 jitinfo = "nullptr"
3035 return (
3036 m["name"],
3037 accessor,
3038 jitinfo,
3039 m["length"],
3040 flags(m, unforgeable),
3041 selfHostedName,
3044 @staticmethod
3045 def condition(m, d):
3046 return m["condition"]
3048 def generateArray(self, array, name):
3049 if len(array) == 0:
3050 return ""
3052 return self.generatePrefableArray(
3053 array,
3054 name,
3055 self.formatSpec,
3056 " JS_FS_END",
3057 "JSFunctionSpec",
3058 self.condition,
3059 functools.partial(self.specData, unforgeable=self.unforgeable),
3063 class AttrDefiner(PropertyDefiner):
3064 def __init__(self, descriptor, name, crossOriginOnly, static, unforgeable=False):
3065 assert not (static and unforgeable)
3066 PropertyDefiner.__init__(self, descriptor, name)
3067 self.name = name
3068 # Ignore non-static attributes for interfaces without a proto object
3069 if descriptor.interface.hasInterfacePrototypeObject() or static:
3070 idlAttrs = [
3072 for m in descriptor.interface.members
3073 if m.isAttr()
3074 and m.isStatic() == static
3075 and MemberIsLegacyUnforgeable(m, descriptor) == unforgeable
3076 and (
3077 not crossOriginOnly
3078 or m.getExtendedAttribute("CrossOriginReadable")
3079 or m.getExtendedAttribute("CrossOriginWritable")
3082 else:
3083 idlAttrs = []
3085 attributes = []
3086 for attr in idlAttrs:
3087 attributes.extend(self.attrData(attr, unforgeable))
3088 self.chrome = [m for m in attributes if isChromeOnly(m["attr"])]
3089 self.regular = [m for m in attributes if not isChromeOnly(m["attr"])]
3090 self.static = static
3092 if static:
3093 if not descriptor.interface.hasInterfaceObject():
3094 # static attributes go on the interface object
3095 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
3096 else:
3097 if not descriptor.interface.hasInterfacePrototypeObject():
3098 # non-static attributes go on the interface prototype object
3099 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
3101 @staticmethod
3102 def attrData(attr, unforgeable=False, overrideFlags=None):
3103 if overrideFlags is None:
3104 permanent = " | JSPROP_PERMANENT" if unforgeable else ""
3105 flags = EnumerabilityFlags(attr) + permanent
3106 else:
3107 flags = overrideFlags
3108 return (
3109 {"name": name, "attr": attr, "flags": flags}
3110 for name in [attr.identifier.name] + attr.bindingAliases
3113 @staticmethod
3114 def condition(m, d):
3115 return PropertyDefiner.getControllingCondition(m["attr"], d)
3117 @staticmethod
3118 def specData(entry, descriptor, static=False, crossOriginOnly=False):
3119 def getter(attr):
3120 if crossOriginOnly and not attr.getExtendedAttribute("CrossOriginReadable"):
3121 return "nullptr, nullptr"
3122 if static:
3123 if attr.type.isPromise():
3124 raise TypeError(
3125 "Don't know how to handle "
3126 "static Promise-returning "
3127 "attribute %s.%s" % (descriptor.name, attr.identifier.name)
3129 accessor = "get_" + IDLToCIdentifier(attr.identifier.name)
3130 jitinfo = "nullptr"
3131 else:
3132 if attr.type.isPromise():
3133 exceptionPolicy = "ConvertExceptionsToPromises"
3134 else:
3135 exceptionPolicy = "ThrowExceptions"
3137 if attr.hasLegacyLenientThis():
3138 if attr.getExtendedAttribute("CrossOriginReadable"):
3139 raise TypeError(
3140 "Can't handle lenient cross-origin "
3141 "readable attribute %s.%s"
3142 % (descriptor.name, attr.identifier.name)
3144 if descriptor.interface.hasDescendantWithCrossOriginMembers:
3145 accessor = (
3146 "GenericGetter<MaybeCrossOriginObjectLenientThisPolicy, %s>"
3147 % exceptionPolicy
3149 else:
3150 accessor = (
3151 "GenericGetter<LenientThisPolicy, %s>" % exceptionPolicy
3153 elif attr.getExtendedAttribute("CrossOriginReadable"):
3154 accessor = (
3155 "GenericGetter<CrossOriginThisPolicy, %s>" % exceptionPolicy
3157 elif descriptor.interface.hasDescendantWithCrossOriginMembers:
3158 accessor = (
3159 "GenericGetter<MaybeCrossOriginObjectThisPolicy, %s>"
3160 % exceptionPolicy
3162 elif descriptor.interface.isOnGlobalProtoChain():
3163 accessor = (
3164 "GenericGetter<MaybeGlobalThisPolicy, %s>" % exceptionPolicy
3166 else:
3167 accessor = "GenericGetter<NormalThisPolicy, %s>" % exceptionPolicy
3168 jitinfo = "&%s_getterinfo" % IDLToCIdentifier(attr.identifier.name)
3169 return "%s, %s" % (accessor, jitinfo)
3171 def setter(attr):
3172 if (
3173 attr.readonly
3174 and attr.getExtendedAttribute("PutForwards") is None
3175 and attr.getExtendedAttribute("Replaceable") is None
3176 and attr.getExtendedAttribute("LegacyLenientSetter") is None
3178 return "nullptr, nullptr"
3179 if crossOriginOnly and not attr.getExtendedAttribute("CrossOriginWritable"):
3180 return "nullptr, nullptr"
3181 if static:
3182 accessor = "set_" + IDLToCIdentifier(attr.identifier.name)
3183 jitinfo = "nullptr"
3184 else:
3185 if attr.hasLegacyLenientThis():
3186 if attr.getExtendedAttribute("CrossOriginWritable"):
3187 raise TypeError(
3188 "Can't handle lenient cross-origin "
3189 "writable attribute %s.%s"
3190 % (descriptor.name, attr.identifier.name)
3192 if descriptor.interface.hasDescendantWithCrossOriginMembers:
3193 accessor = (
3194 "GenericSetter<MaybeCrossOriginObjectLenientThisPolicy>"
3196 else:
3197 accessor = "GenericSetter<LenientThisPolicy>"
3198 elif attr.getExtendedAttribute("CrossOriginWritable"):
3199 accessor = "GenericSetter<CrossOriginThisPolicy>"
3200 elif descriptor.interface.hasDescendantWithCrossOriginMembers:
3201 accessor = "GenericSetter<MaybeCrossOriginObjectThisPolicy>"
3202 elif descriptor.interface.isOnGlobalProtoChain():
3203 accessor = "GenericSetter<MaybeGlobalThisPolicy>"
3204 else:
3205 accessor = "GenericSetter<NormalThisPolicy>"
3206 jitinfo = "&%s_setterinfo" % IDLToCIdentifier(attr.identifier.name)
3207 return "%s, %s" % (accessor, jitinfo)
3209 name, attr, flags = entry["name"], entry["attr"], entry["flags"]
3210 return (name, flags, getter(attr), setter(attr))
3212 @staticmethod
3213 def formatSpec(fields):
3214 return ' JSPropertySpec::nativeAccessors("%s", %s, %s, %s)' % fields
3216 def generateArray(self, array, name):
3217 if len(array) == 0:
3218 return ""
3220 return self.generatePrefableArray(
3221 array,
3222 name,
3223 self.formatSpec,
3224 " JS_PS_END",
3225 "JSPropertySpec",
3226 self.condition,
3227 functools.partial(self.specData, static=self.static),
3231 class ConstDefiner(PropertyDefiner):
3233 A class for definining constants on the interface object
3236 def __init__(self, descriptor, name):
3237 PropertyDefiner.__init__(self, descriptor, name)
3238 self.name = name
3239 constants = [m for m in descriptor.interface.members if m.isConst()]
3240 self.chrome = [m for m in constants if isChromeOnly(m)]
3241 self.regular = [m for m in constants if not isChromeOnly(m)]
3243 def generateArray(self, array, name):
3244 if len(array) == 0:
3245 return ""
3247 def specData(const, descriptor):
3248 return (const.identifier.name, convertConstIDLValueToJSVal(const.value))
3250 return self.generatePrefableArray(
3251 array,
3252 name,
3253 lambda fields: ' { "%s", %s }' % fields,
3254 " { 0, JS::UndefinedValue() }",
3255 "ConstantSpec",
3256 PropertyDefiner.getControllingCondition,
3257 specData,
3261 class PropertyArrays:
3262 def __init__(self, descriptor, crossOriginOnly=False):
3263 self.staticMethods = MethodDefiner(
3264 descriptor, "StaticMethods", crossOriginOnly, static=True
3266 self.staticAttrs = AttrDefiner(
3267 descriptor, "StaticAttributes", crossOriginOnly, static=True
3269 self.methods = MethodDefiner(
3270 descriptor, "Methods", crossOriginOnly, static=False
3272 self.attrs = AttrDefiner(
3273 descriptor, "Attributes", crossOriginOnly, static=False
3275 self.unforgeableMethods = MethodDefiner(
3276 descriptor,
3277 "UnforgeableMethods",
3278 crossOriginOnly,
3279 static=False,
3280 unforgeable=True,
3282 self.unforgeableAttrs = AttrDefiner(
3283 descriptor,
3284 "UnforgeableAttributes",
3285 crossOriginOnly,
3286 static=False,
3287 unforgeable=True,
3289 self.consts = ConstDefiner(descriptor, "Constants")
3291 @staticmethod
3292 def arrayNames():
3293 return [
3294 "staticMethods",
3295 "staticAttrs",
3296 "methods",
3297 "attrs",
3298 "unforgeableMethods",
3299 "unforgeableAttrs",
3300 "consts",
3303 def hasChromeOnly(self):
3304 return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())
3306 def hasNonChromeOnly(self):
3307 return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames())
3309 def __str__(self):
3310 define = ""
3311 for array in self.arrayNames():
3312 define += str(getattr(self, array))
3313 return define
3316 class CGConstDefinition(CGThing):
3318 Given a const member of an interface, return the C++ static const definition
3319 for the member. Should be part of the interface namespace in the header
3320 file.
3323 def __init__(self, member):
3324 assert (
3325 member.isConst()
3326 and member.value.type.isPrimitive()
3327 and not member.value.type.nullable()
3330 name = CppKeywords.checkMethodName(IDLToCIdentifier(member.identifier.name))
3331 tag = member.value.type.tag()
3332 value = member.value.value
3333 if tag == IDLType.Tags.bool:
3334 value = toStringBool(member.value.value)
3335 self.const = "static const %s %s = %s;" % (builtinNames[tag], name, value)
3337 def declare(self):
3338 return self.const
3340 def define(self):
3341 return ""
3343 def deps(self):
3344 return []
3347 class CGNativeProperties(CGList):
3348 def __init__(self, descriptor, properties):
3349 def generateNativeProperties(name, chrome):
3350 def check(p):
3351 return p.hasChromeOnly() if chrome else p.hasNonChromeOnly()
3353 nativePropsInts = []
3354 nativePropsPtrs = []
3355 nativePropsDuos = []
3357 duosOffset = 0
3358 idsOffset = 0
3359 for array in properties.arrayNames():
3360 propertyArray = getattr(properties, array)
3361 if check(propertyArray):
3362 varName = propertyArray.variableName(chrome)
3363 bitfields = "true, %d /* %s */" % (duosOffset, varName)
3364 duosOffset += 1
3365 nativePropsInts.append(CGGeneric(bitfields))
3367 if propertyArray.usedForXrays():
3368 ids = "&%s_propertyInfos[%d]" % (name, idsOffset)
3369 idsOffset += propertyArray.length(chrome)
3370 else:
3371 ids = "nullptr"
3372 duo = "{ %s, %s }" % (varName, ids)
3373 nativePropsDuos.append(CGGeneric(duo))
3374 else:
3375 bitfields = "false, 0"
3376 nativePropsInts.append(CGGeneric(bitfields))
3378 iteratorAliasIndex = -1
3379 for index, item in enumerate(properties.methods.regular):
3380 if item.get("hasIteratorAlias"):
3381 iteratorAliasIndex = index
3382 break
3383 nativePropsInts.append(CGGeneric(str(iteratorAliasIndex)))
3385 nativePropsDuos = [
3386 CGWrapper(
3387 CGIndenter(CGList(nativePropsDuos, ",\n")), pre="{\n", post="\n}"
3391 pre = "static const NativePropertiesN<%d> %s = {\n" % (duosOffset, name)
3392 post = "\n};\n"
3393 if descriptor.wantsXrays:
3394 pre = fill(
3396 static uint16_t ${name}_sortedPropertyIndices[${size}];
3397 static PropertyInfo ${name}_propertyInfos[${size}];
3399 $*{pre}
3400 """,
3401 name=name,
3402 size=idsOffset,
3403 pre=pre,
3405 if iteratorAliasIndex > 0:
3406 # The iteratorAliasMethodIndex is a signed integer, so the
3407 # max value it can store is 2^(nbits-1)-1.
3408 post = fill(
3410 $*{post}
3411 static_assert(${iteratorAliasIndex} < 1ull << (CHAR_BIT * sizeof(${name}.iteratorAliasMethodIndex) - 1),
3412 "We have an iterator alias index that is oversized");
3413 """,
3414 post=post,
3415 iteratorAliasIndex=iteratorAliasIndex,
3416 name=name,
3418 post = fill(
3420 $*{post}
3421 static_assert(${propertyInfoCount} < 1ull << (CHAR_BIT * sizeof(${name}.propertyInfoCount)),
3422 "We have a property info count that is oversized");
3423 """,
3424 post=post,
3425 propertyInfoCount=idsOffset,
3426 name=name,
3428 nativePropsInts.append(CGGeneric("%d" % idsOffset))
3429 nativePropsPtrs.append(CGGeneric("%s_sortedPropertyIndices" % name))
3430 else:
3431 nativePropsInts.append(CGGeneric("0"))
3432 nativePropsPtrs.append(CGGeneric("nullptr"))
3433 nativeProps = nativePropsInts + nativePropsPtrs + nativePropsDuos
3434 return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")), pre=pre, post=post)
3436 nativeProperties = []
3437 if properties.hasNonChromeOnly():
3438 nativeProperties.append(
3439 generateNativeProperties("sNativeProperties", False)
3441 if properties.hasChromeOnly():
3442 nativeProperties.append(
3443 generateNativeProperties("sChromeOnlyNativeProperties", True)
3446 CGList.__init__(self, nativeProperties, "\n")
3448 def declare(self):
3449 return ""
3451 def define(self):
3452 return CGList.define(self)
3455 class CGCollectJSONAttributesMethod(CGAbstractMethod):
3457 Generate the CollectJSONAttributes method for an interface descriptor
3460 def __init__(self, descriptor, toJSONMethod):
3461 args = [
3462 Argument("JSContext*", "cx"),
3463 Argument("JS::Handle<JSObject*>", "obj"),
3464 Argument("%s*" % descriptor.nativeType, "self"),
3465 Argument("JS::Rooted<JSObject*>&", "result"),
3467 CGAbstractMethod.__init__(
3468 self, descriptor, "CollectJSONAttributes", "bool", args, canRunScript=True
3470 self.toJSONMethod = toJSONMethod
3472 def definition_body(self):
3473 ret = ""
3474 interface = self.descriptor.interface
3475 toJSONCondition = PropertyDefiner.getControllingCondition(
3476 self.toJSONMethod, self.descriptor
3478 needUnwrappedObj = False
3479 for m in interface.members:
3480 if m.isAttr() and not m.isStatic() and m.type.isJSONType():
3481 getAndDefine = fill(
3483 JS::Rooted<JS::Value> temp(cx);
3484 if (!get_${name}(cx, obj, self, JSJitGetterCallArgs(&temp))) {
3485 return false;
3487 if (!JS_DefineProperty(cx, result, "${name}", temp, JSPROP_ENUMERATE)) {
3488 return false;
3490 """,
3491 name=IDLToCIdentifier(m.identifier.name),
3493 # Make sure we don't include things which are supposed to be
3494 # disabled. Things that either don't have disablers or whose
3495 # disablers match the disablers for our toJSON method can't
3496 # possibly be disabled, but other things might be.
3497 condition = PropertyDefiner.getControllingCondition(m, self.descriptor)
3498 if condition.hasDisablers() and condition != toJSONCondition:
3499 needUnwrappedObj = True
3500 ret += fill(
3502 // This is unfortunately a linear scan through sAttributes, but we
3503 // only do it for things which _might_ be disabled, which should
3504 // help keep the performance problems down.
3505 if (IsGetterEnabled(cx, unwrappedObj, (JSJitGetterOp)get_${name}, sAttributes)) {
3506 $*{getAndDefine}
3508 """,
3509 name=IDLToCIdentifier(m.identifier.name),
3510 getAndDefine=getAndDefine,
3512 else:
3513 ret += fill(
3515 { // scope for "temp"
3516 $*{getAndDefine}
3518 """,
3519 getAndDefine=getAndDefine,
3521 ret += "return true;\n"
3523 if needUnwrappedObj:
3524 # If we started allowing cross-origin objects here, we'd need to
3525 # use CheckedUnwrapDynamic and figure out whether it makes sense.
3526 # But in practice no one is trying to add toJSON methods to those,
3527 # so let's just guard against it.
3528 assert not self.descriptor.isMaybeCrossOriginObject()
3529 ret = fill(
3531 JS::Rooted<JSObject*> unwrappedObj(cx, js::CheckedUnwrapStatic(obj));
3532 if (!unwrappedObj) {
3533 // How did that happen? We managed to get called with that
3534 // object as "this"! Just give up on sanity.
3535 return false;
3538 $*{ret}
3539 """,
3540 ret=ret,
3543 return ret
3546 class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
3548 Generate the CreateInterfaceObjects method for an interface descriptor.
3550 properties should be a PropertyArrays instance.
3553 def __init__(
3554 self, descriptor, properties, haveUnscopables, haveLegacyWindowAliases, static
3556 args = [
3557 Argument("JSContext*", "aCx"),
3558 Argument("JS::Handle<JSObject*>", "aGlobal"),
3559 Argument("ProtoAndIfaceCache&", "aProtoAndIfaceCache"),
3560 Argument("DefineInterfaceProperty", "aDefineOnGlobal"),
3562 CGAbstractMethod.__init__(
3563 self, descriptor, "CreateInterfaceObjects", "void", args, static=static
3565 self.properties = properties
3566 self.haveUnscopables = haveUnscopables
3567 self.haveLegacyWindowAliases = haveLegacyWindowAliases
3569 def definition_body(self):
3570 needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
3571 if needInterfaceObject and self.descriptor.isExposedConditionally():
3572 # This code might be called when we're trying to create an object
3573 # in a non-system compartment, for example when system code is
3574 # calling a constructor through Xrays. In that case we do want to
3575 # create an interface object in the non-system compartment, but we
3576 # don't want to expose the name on the non-system global if the
3577 # interface itself is marked as ChromeOnly.
3578 defineOnGlobal = (
3579 "ShouldExpose<%s::ConstructorEnabled>(aCx, aGlobal, aDefineOnGlobal)"
3580 % toBindingNamespace(self.descriptor.name)
3582 else:
3583 defineOnGlobal = "aDefineOnGlobal != DefineInterfaceProperty::No"
3584 if needInterfaceObject:
3585 if self.descriptor.interface.isNamespace():
3586 if self.descriptor.interface.getExtendedAttribute("ProtoObjectHack"):
3587 getConstructorProto = "GetHackedNamespaceProtoObject"
3588 else:
3589 getConstructorProto = "JS::GetRealmObjectPrototype"
3590 getConstructorProto = "aCx, " + getConstructorProto
3591 constructorProtoType = "Rooted"
3592 else:
3593 getConstructorProto = InterfaceObjectProtoGetter(self.descriptor)
3594 constructorProtoType = "Handle"
3596 getConstructorProto = fill(
3598 JS::${type}<JSObject*> constructorProto(${getConstructorProto}(aCx));
3599 if (!constructorProto) {
3600 return;
3602 """,
3603 type=constructorProtoType,
3604 getConstructorProto=getConstructorProto,
3607 interfaceInfo = "&sInterfaceObjectInfo"
3608 interfaceCache = (
3609 "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)"
3610 % self.descriptor.name
3612 getConstructorProto = CGGeneric(getConstructorProto)
3613 constructorProto = "constructorProto"
3614 else:
3615 # We don't have slots to store the legacy factory functions.
3616 assert len(self.descriptor.interface.legacyFactoryFunctions) == 0
3617 interfaceInfo = "nullptr"
3618 interfaceCache = "nullptr"
3619 getConstructorProto = None
3620 constructorProto = "nullptr"
3622 if self.properties.hasNonChromeOnly():
3623 properties = "sNativeProperties.Upcast()"
3624 else:
3625 properties = "nullptr"
3626 if self.properties.hasChromeOnly():
3627 chromeProperties = "sChromeOnlyNativeProperties.Upcast()"
3628 else:
3629 chromeProperties = "nullptr"
3631 # We use getClassName here. This should be the right thing to pass as
3632 # the name argument to CreateInterfaceObjects. This is generally the
3633 # interface identifier, except for the synthetic interfaces created for
3634 # the default iterator objects. If needInterfaceObject is true then
3635 # we'll use the name to install a property on the global object, so
3636 # there shouldn't be any spaces in the name.
3637 name = self.descriptor.interface.getClassName()
3638 assert not (needInterfaceObject and " " in name)
3640 if self.descriptor.interface.isNamespace():
3641 # If we don't need to create anything, why are we generating this?
3642 assert needInterfaceObject
3644 call = fill(
3646 JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
3647 dom::CreateNamespaceObject(aCx, aGlobal, ${constructorProto},
3648 sNamespaceObjectClass,
3649 interfaceCache,
3650 ${properties},
3651 ${chromeProperties},
3652 "${name}",
3653 ${defineOnGlobal});
3654 """,
3655 interfaceCache=interfaceCache,
3656 constructorProto=constructorProto,
3657 properties=properties,
3658 chromeProperties=chromeProperties,
3659 name=name,
3660 defineOnGlobal=defineOnGlobal,
3662 return CGList(
3664 getConstructorProto,
3665 CGGeneric(call),
3667 "\n",
3668 ).define()
3670 needInterfacePrototypeObject = (
3671 self.descriptor.interface.hasInterfacePrototypeObject()
3674 # If we don't need to create anything, why are we generating this?
3675 assert needInterfaceObject or needInterfacePrototypeObject
3677 if needInterfacePrototypeObject:
3678 (protoGetter, protoHandleGetter) = InterfacePrototypeObjectProtoGetter(
3679 self.descriptor
3681 if protoHandleGetter is None:
3682 parentProtoType = "Rooted"
3683 getParentProto = "aCx, " + protoGetter
3684 else:
3685 parentProtoType = "Handle"
3686 getParentProto = protoHandleGetter
3688 getParentProto = fill(
3690 JS::${type}<JSObject*> parentProto(${getParentProto}(aCx));
3691 if (!parentProto) {
3692 return;
3694 """,
3695 type=parentProtoType,
3696 getParentProto=getParentProto,
3699 protoClass = "&sPrototypeClass"
3700 protoCache = (
3701 "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)"
3702 % self.descriptor.name
3704 parentProto = "parentProto"
3705 getParentProto = CGGeneric(getParentProto)
3706 else:
3707 protoClass = "nullptr"
3708 protoCache = "nullptr"
3709 parentProto = "nullptr"
3710 getParentProto = None
3712 if self.descriptor.interface.ctor():
3713 constructArgs = methodLength(self.descriptor.interface.ctor())
3714 isConstructorChromeOnly = isChromeOnly(self.descriptor.interface.ctor())
3715 else:
3716 constructArgs = 0
3717 isConstructorChromeOnly = False
3718 if len(self.descriptor.interface.legacyFactoryFunctions) > 0:
3719 legacyFactoryFunctions = "Span(legacyFactoryFunctions)"
3720 else:
3721 legacyFactoryFunctions = "Span<const LegacyFactoryFunction, 0>{}"
3723 isGlobal = self.descriptor.isGlobal() is not None
3725 ensureCaches = fill(
3727 JS::Heap<JSObject*>* protoCache = ${protoCache};
3728 JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
3729 """,
3730 protoCache=protoCache,
3731 interfaceCache=interfaceCache,
3733 call = fill(
3735 dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
3736 ${protoClass}, protoCache,
3737 ${constructorProto}, ${interfaceInfo}, ${constructArgs}, ${isConstructorChromeOnly}, ${legacyFactoryFunctions},
3738 interfaceCache,
3739 ${properties},
3740 ${chromeProperties},
3741 "${name}",
3742 ${defineOnGlobal},
3743 ${unscopableNames},
3744 ${isGlobal},
3745 ${legacyWindowAliases});
3746 """,
3747 protoClass=protoClass,
3748 parentProto=parentProto,
3749 constructorProto=constructorProto,
3750 interfaceInfo=interfaceInfo,
3751 constructArgs=constructArgs,
3752 isConstructorChromeOnly=toStringBool(isConstructorChromeOnly),
3753 legacyFactoryFunctions=legacyFactoryFunctions,
3754 properties=properties,
3755 chromeProperties=chromeProperties,
3756 name=name,
3757 defineOnGlobal=defineOnGlobal,
3758 unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr",
3759 isGlobal=toStringBool(isGlobal),
3760 legacyWindowAliases=(
3761 "legacyWindowAliases" if self.haveLegacyWindowAliases else "nullptr"
3765 # If we fail after here, we must clear interface and prototype caches
3766 # using this code: intermediate failure must not expose the interface in
3767 # partially-constructed state. Note that every case after here needs an
3768 # interface prototype object.
3769 failureCode = dedent(
3771 *protoCache = nullptr;
3772 if (interfaceCache) {
3773 *interfaceCache = nullptr;
3775 return;
3779 needProtoVar = False
3781 aliasedMembers = [
3782 m for m in self.descriptor.interface.members if m.isMethod() and m.aliases
3784 if aliasedMembers:
3785 assert needInterfacePrototypeObject
3787 def defineAlias(alias):
3788 if alias == "@@iterator" or alias == "@@asyncIterator":
3789 name = alias[2:]
3791 symbolJSID = (
3792 "JS::GetWellKnownSymbolKey(aCx, JS::SymbolCode::%s)" % name
3794 prop = "%sId" % name
3795 getSymbolJSID = CGGeneric(
3796 fill(
3797 "JS::Rooted<jsid> ${prop}(aCx, ${symbolJSID});",
3798 prop=prop,
3799 symbolJSID=symbolJSID,
3802 defineFn = "JS_DefinePropertyById"
3803 enumFlags = "0" # Not enumerable, per spec.
3804 elif alias.startswith("@@"):
3805 raise TypeError(
3806 "Can't handle any well-known Symbol other than @@iterator and @@asyncIterator"
3808 else:
3809 getSymbolJSID = None
3810 defineFn = "JS_DefineProperty"
3811 prop = '"%s"' % alias
3812 # XXX If we ever create non-enumerable properties that can
3813 # be aliased, we should consider making the aliases
3814 # match the enumerability of the property being aliased.
3815 enumFlags = "JSPROP_ENUMERATE"
3816 return CGList(
3818 getSymbolJSID,
3819 CGGeneric(
3820 fill(
3822 if (!${defineFn}(aCx, proto, ${prop}, aliasedVal, ${enumFlags})) {
3823 $*{failureCode}
3825 """,
3826 defineFn=defineFn,
3827 prop=prop,
3828 enumFlags=enumFlags,
3829 failureCode=failureCode,
3833 "\n",
3836 def defineAliasesFor(m):
3837 return CGList(
3839 CGGeneric(
3840 fill(
3842 if (!JS_GetProperty(aCx, proto, \"${prop}\", &aliasedVal)) {
3843 $*{failureCode}
3845 """,
3846 failureCode=failureCode,
3847 prop=m.identifier.name,
3851 + [defineAlias(alias) for alias in sorted(m.aliases)]
3854 defineAliases = CGList(
3856 CGGeneric(
3857 dedent(
3859 // Set up aliases on the interface prototype object we just created.
3863 CGGeneric("JS::Rooted<JS::Value> aliasedVal(aCx);\n\n"),
3866 defineAliasesFor(m)
3867 for m in sorted(aliasedMembers, key=lambda m: m.identifier.name)
3870 needProtoVar = True
3871 else:
3872 defineAliases = None
3874 # Globals handle unforgeables directly in Wrap() instead of
3875 # via a holder.
3876 if (
3877 self.descriptor.hasLegacyUnforgeableMembers
3878 and not self.descriptor.isGlobal()
3880 assert needInterfacePrototypeObject
3882 # We want to use the same JSClass and prototype as the object we'll
3883 # end up defining the unforgeable properties on in the end, so that
3884 # we can use JS_InitializePropertiesFromCompatibleNativeObject to do
3885 # a fast copy. In the case of proxies that's null, because the
3886 # expando object is a vanilla object, but in the case of other DOM
3887 # objects it's whatever our class is.
3888 if self.descriptor.proxy:
3889 holderClass = "nullptr"
3890 holderProto = "nullptr"
3891 else:
3892 holderClass = "sClass.ToJSClass()"
3893 holderProto = "proto"
3894 needProtoVar = True
3895 createUnforgeableHolder = CGGeneric(
3896 fill(
3898 JS::Rooted<JSObject*> unforgeableHolder(
3899 aCx, JS_NewObjectWithoutMetadata(aCx, ${holderClass}, ${holderProto}));
3900 if (!unforgeableHolder) {
3901 $*{failureCode}
3903 """,
3904 holderProto=holderProto,
3905 holderClass=holderClass,
3906 failureCode=failureCode,
3909 defineUnforgeables = InitUnforgeablePropertiesOnHolder(
3910 self.descriptor, self.properties, failureCode
3912 createUnforgeableHolder = CGList(
3913 [createUnforgeableHolder, defineUnforgeables]
3916 installUnforgeableHolder = CGGeneric(
3917 dedent(
3919 if (*protoCache) {
3920 JS::SetReservedSlot(*protoCache, DOM_INTERFACE_PROTO_SLOTS_BASE,
3921 JS::ObjectValue(*unforgeableHolder));
3927 unforgeableHolderSetup = CGList(
3928 [createUnforgeableHolder, installUnforgeableHolder], "\n"
3930 else:
3931 unforgeableHolderSetup = None
3933 if (
3934 self.descriptor.interface.isOnGlobalProtoChain()
3935 and needInterfacePrototypeObject
3937 makeProtoPrototypeImmutable = CGGeneric(
3938 fill(
3941 bool succeeded;
3942 if (!JS_SetImmutablePrototype(aCx, proto, &succeeded)) {
3943 $*{failureCode}
3946 MOZ_ASSERT(succeeded,
3947 "making a fresh prototype object's [[Prototype]] "
3948 "immutable can internally fail, but it should "
3949 "never be unsuccessful");
3951 """,
3952 protoCache=protoCache,
3953 failureCode=failureCode,
3956 needProtoVar = True
3957 else:
3958 makeProtoPrototypeImmutable = None
3960 if needProtoVar:
3961 defineProtoVar = CGGeneric(
3962 fill(
3964 JS::AssertObjectIsNotGray(*protoCache);
3965 JS::Handle<JSObject*> proto = JS::Handle<JSObject*>::fromMarkedLocation(protoCache->unsafeAddress());
3966 if (!proto) {
3967 $*{failureCode}
3969 """,
3970 failureCode=failureCode,
3973 else:
3974 defineProtoVar = None
3976 # ensureCaches needs to come first as it crashes on failure (like OOM).
3977 # We want to make sure that the caches do exist before we try to return
3978 # to the caller, so it can rely on that (and detect other failures by
3979 # checking for null in the caches).
3980 return CGList(
3982 CGGeneric(ensureCaches),
3983 getParentProto,
3984 getConstructorProto,
3985 CGGeneric(call),
3986 defineProtoVar,
3987 defineAliases,
3988 unforgeableHolderSetup,
3989 makeProtoPrototypeImmutable,
3991 "\n",
3992 ).define()
3995 class CGCreateAndDefineOnGlobalMethod(CGAbstractMethod):
3997 A method for creating the interface or namespace object and defining
3998 properties for it on the global.
4001 def __init__(self, descriptor):
4002 CGAbstractMethod.__init__(
4003 self,
4004 descriptor,
4005 "CreateAndDefineOnGlobal",
4006 "bool",
4008 Argument("JSContext*", "aCx"),
4010 inline=True,
4013 def definition_body(self):
4014 return fill(
4016 // Get the interface or namespace object for this class. This will
4017 // create the object as needed and always define the properties for
4018 // it on the global. The caller should make sure the interface or
4019 // namespace is exposed on the global before calling this.
4020 return GetPerInterfaceObjectHandle(aCx, constructors::id::${name},
4021 &CreateInterfaceObjects,
4022 DefineInterfaceProperty::Always);
4024 """,
4025 name=self.descriptor.name,
4029 class CGGetProtoObjectHandleMethod(CGAbstractMethod):
4031 A method for getting the interface prototype object.
4034 def __init__(self, descriptor, static, signatureOnly=False):
4035 CGAbstractMethod.__init__(
4036 self,
4037 descriptor,
4038 "GetProtoObjectHandle",
4039 "JS::Handle<JSObject*>",
4040 [Argument("JSContext*", "aCx")],
4041 static=static,
4042 signatureOnly=signatureOnly,
4045 def definition_body(self):
4046 return fill(
4048 /* Get the interface prototype object for this class. This will create the
4049 object as needed. */
4050 return GetPerInterfaceObjectHandle(aCx, prototypes::id::${name},
4051 &CreateInterfaceObjects,
4052 DefineInterfaceProperty::CheckExposure);
4054 """,
4055 name=self.descriptor.name,
4059 class CGGetProtoObjectMethod(CGAbstractMethod):
4061 A method for getting the interface prototype object.
4064 def __init__(self, descriptor):
4065 CGAbstractMethod.__init__(
4066 self,
4067 descriptor,
4068 "GetProtoObject",
4069 "JSObject*",
4070 [Argument("JSContext*", "aCx")],
4073 def definition_body(self):
4074 return "return GetProtoObjectHandle(aCx);\n"
4077 class CGGetConstructorObjectHandleMethod(CGAbstractMethod):
4079 A method for getting the interface constructor object.
4082 def __init__(self, descriptor):
4083 CGAbstractMethod.__init__(
4084 self,
4085 descriptor,
4086 "GetConstructorObjectHandle",
4087 "JS::Handle<JSObject*>",
4089 Argument("JSContext*", "aCx"),
4093 def definition_body(self):
4094 return fill(
4096 /* Get the interface object for this class. This will create the object as
4097 needed. */
4099 return GetPerInterfaceObjectHandle(aCx, constructors::id::${name},
4100 &CreateInterfaceObjects,
4101 DefineInterfaceProperty::CheckExposure);
4102 """,
4103 name=self.descriptor.name,
4107 class CGGetNamedPropertiesObjectMethod(CGAbstractStaticMethod):
4108 def __init__(self, descriptor):
4109 args = [Argument("JSContext*", "aCx")]
4110 CGAbstractStaticMethod.__init__(
4111 self, descriptor, "GetNamedPropertiesObject", "JSObject*", args
4114 def definition_body(self):
4115 parentProtoName = self.descriptor.parentPrototypeName
4116 if parentProtoName is None:
4117 getParentProto = ""
4118 parentProto = "nullptr"
4119 else:
4120 getParentProto = fill(
4122 JS::Rooted<JSObject*> parentProto(aCx, ${parent}::GetProtoObjectHandle(aCx));
4123 if (!parentProto) {
4124 return nullptr;
4126 """,
4127 parent=toBindingNamespace(parentProtoName),
4129 parentProto = "parentProto"
4130 return fill(
4132 /* Make sure our global is sane. Hopefully we can remove this sometime */
4133 JSObject* global = JS::CurrentGlobalOrNull(aCx);
4134 if (!(JS::GetClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
4135 return nullptr;
4138 /* Check to see whether the named properties object has already been created */
4139 ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
4141 JS::Heap<JSObject*>& namedPropertiesObject = protoAndIfaceCache.EntrySlotOrCreate(namedpropertiesobjects::id::${ifaceName});
4142 if (!namedPropertiesObject) {
4143 $*{getParentProto}
4144 namedPropertiesObject = ${nativeType}::CreateNamedPropertiesObject(aCx, ${parentProto});
4145 DebugOnly<const DOMIfaceAndProtoJSClass*> clasp =
4146 DOMIfaceAndProtoJSClass::FromJSClass(JS::GetClass(namedPropertiesObject));
4147 MOZ_ASSERT(clasp->mType == eNamedPropertiesObject,
4148 "Expected ${nativeType}::CreateNamedPropertiesObject to return a named properties object");
4149 MOZ_ASSERT(clasp->mNativeHooks,
4150 "The named properties object for ${nativeType} should have NativePropertyHooks.");
4151 MOZ_ASSERT(!clasp->mNativeHooks->mIndexedOrNamedNativeProperties ||
4152 !clasp->mNativeHooks->mIndexedOrNamedNativeProperties->mResolveOwnProperty,
4153 "Shouldn't resolve the properties of the named properties object for ${nativeType} for Xrays.");
4154 MOZ_ASSERT(!clasp->mNativeHooks->mIndexedOrNamedNativeProperties ||
4155 !clasp->mNativeHooks->mIndexedOrNamedNativeProperties->mEnumerateOwnProperties,
4156 "Shouldn't enumerate the properties of the named properties object for ${nativeType} for Xrays.");
4158 return namedPropertiesObject.get();
4159 """,
4160 getParentProto=getParentProto,
4161 ifaceName=self.descriptor.name,
4162 parentProto=parentProto,
4163 nativeType=self.descriptor.nativeType,
4167 def getRawConditionList(idlobj, cxName, objName, ignoreSecureContext=False):
4169 Get the list of conditions for idlobj (to be used in "is this enabled"
4170 checks). This will be returned as a CGList with " &&\n" as the separator,
4171 for readability.
4173 objName is the name of the object that we're working with, because some of
4174 our test functions want that.
4176 ignoreSecureContext is used only for constructors in which the WebIDL interface
4177 itself is already marked as [SecureContext]. There is no need to do the work twice.
4179 conditions = []
4180 pref = idlobj.getExtendedAttribute("Pref")
4181 if pref:
4182 assert isinstance(pref, list) and len(pref) == 1
4183 conditions.append("StaticPrefs::%s()" % prefIdentifier(pref[0]))
4184 if isChromeOnly(idlobj):
4185 conditions.append("nsContentUtils::ThreadsafeIsSystemCaller(%s)" % cxName)
4186 func = idlobj.getExtendedAttribute("Func")
4187 if func:
4188 assert isinstance(func, list) and len(func) == 1
4189 conditions.append("%s(%s, %s)" % (func[0], cxName, objName))
4190 trial = idlobj.getExtendedAttribute("Trial")
4191 if trial:
4192 assert isinstance(trial, list) and len(trial) == 1
4193 conditions.append(
4194 "OriginTrials::IsEnabled(%s, %s, OriginTrial::%s)"
4195 % (cxName, objName, trial[0])
4197 if not ignoreSecureContext and idlobj.getExtendedAttribute("SecureContext"):
4198 conditions.append(
4199 "mozilla::dom::IsSecureContextOrObjectIsFromSecureContext(%s, %s)"
4200 % (cxName, objName)
4202 return conditions
4205 def getConditionList(idlobj, cxName, objName, ignoreSecureContext=False):
4207 Get the list of conditions from getRawConditionList
4208 See comment on getRawConditionList above for more info about arguments.
4210 The return value is a possibly-empty conjunctive CGList of conditions.
4212 conditions = getRawConditionList(idlobj, cxName, objName, ignoreSecureContext)
4213 return CGList((CGGeneric(cond) for cond in conditions), " &&\n")
4216 class CGConstructorEnabled(CGAbstractMethod):
4218 A method for testing whether we should be exposing this interface object.
4219 This can perform various tests depending on what conditions are specified
4220 on the interface.
4223 def __init__(self, descriptor):
4224 CGAbstractMethod.__init__(
4225 self,
4226 descriptor,
4227 "ConstructorEnabled",
4228 "bool",
4229 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
4232 def definition_body(self):
4233 body = CGList([], "\n")
4235 iface = self.descriptor.interface
4237 if not iface.isExposedInWindow():
4238 exposedInWindowCheck = dedent(
4240 MOZ_ASSERT(!NS_IsMainThread(), "Why did we even get called?");
4243 body.append(CGGeneric(exposedInWindowCheck))
4245 if iface.isExposedInSomeButNotAllWorkers():
4246 workerGlobals = sorted(iface.getWorkerExposureSet())
4247 workerCondition = CGList(
4249 CGGeneric('strcmp(name, "%s")' % workerGlobal)
4250 for workerGlobal in workerGlobals
4252 " && ",
4254 exposedInWorkerCheck = fill(
4256 const char* name = JS::GetClass(aObj)->name;
4257 if (${workerCondition}) {
4258 return false;
4260 """,
4261 workerCondition=workerCondition.define(),
4263 exposedInWorkerCheck = CGGeneric(exposedInWorkerCheck)
4264 if iface.isExposedInWindow():
4265 exposedInWorkerCheck = CGIfWrapper(
4266 exposedInWorkerCheck, "!NS_IsMainThread()"
4268 body.append(exposedInWorkerCheck)
4270 conditions = getConditionList(iface, "aCx", "aObj")
4272 # We should really have some conditions
4273 assert len(body) or len(conditions)
4275 conditionsWrapper = ""
4276 if len(conditions):
4277 conditionsWrapper = CGWrapper(
4278 conditions, pre="return ", post=";\n", reindent=True
4280 else:
4281 conditionsWrapper = CGGeneric("return true;\n")
4283 body.append(conditionsWrapper)
4284 return body.define()
4287 def StructuredCloneTag(name):
4288 return "SCTAG_DOM_%s" % name.upper()
4291 class CGSerializer(CGAbstractStaticMethod):
4293 Implementation of serialization for things marked [Serializable].
4294 This gets stored in our DOMJSClass, so it can be static.
4296 The caller is expected to pass in the object whose DOMJSClass it
4297 used to get the serializer.
4300 def __init__(self, descriptor):
4301 args = [
4302 Argument("JSContext*", "aCx"),
4303 Argument("JSStructuredCloneWriter*", "aWriter"),
4304 Argument("JS::Handle<JSObject*>", "aObj"),
4306 CGAbstractStaticMethod.__init__(self, descriptor, "Serialize", "bool", args)
4308 def definition_body(self):
4309 return fill(
4311 MOZ_ASSERT(IsDOMObject(aObj), "Non-DOM object passed");
4312 MOZ_ASSERT(GetDOMClass(aObj)->mSerializer == &Serialize,
4313 "Wrong object passed");
4314 return JS_WriteUint32Pair(aWriter, ${tag}, 0) &&
4315 UnwrapDOMObject<${type}>(aObj)->WriteStructuredClone(aCx, aWriter);
4316 """,
4317 tag=StructuredCloneTag(self.descriptor.name),
4318 type=self.descriptor.nativeType,
4322 class CGDeserializer(CGAbstractMethod):
4324 Implementation of deserialization for things marked [Serializable].
4325 This will need to be accessed from WebIDLSerializable, so can't be static.
4328 def __init__(self, descriptor):
4329 args = [
4330 Argument("JSContext*", "aCx"),
4331 Argument("nsIGlobalObject*", "aGlobal"),
4332 Argument("JSStructuredCloneReader*", "aReader"),
4334 CGAbstractMethod.__init__(self, descriptor, "Deserialize", "JSObject*", args)
4336 def definition_body(self):
4337 # WrapObject has different signatures depending on whether
4338 # the object is wrappercached.
4339 if self.descriptor.wrapperCache:
4340 wrapCall = dedent(
4342 result = obj->WrapObject(aCx, nullptr);
4343 if (!result) {
4344 return nullptr;
4348 else:
4349 wrapCall = dedent(
4351 if (!obj->WrapObject(aCx, nullptr, &result)) {
4352 return nullptr;
4357 return fill(
4359 // Protect the result from a moving GC in ~RefPtr
4360 JS::Rooted<JSObject*> result(aCx);
4361 { // Scope for the RefPtr
4362 RefPtr<${type}> obj = ${type}::ReadStructuredClone(aCx, aGlobal, aReader);
4363 if (!obj) {
4364 return nullptr;
4366 $*{wrapCall}
4368 return result;
4369 """,
4370 type=self.descriptor.nativeType,
4371 wrapCall=wrapCall,
4375 def CreateBindingJSObject(descriptor):
4376 objDecl = "BindingJSObjectCreator<%s> creator(aCx);\n" % descriptor.nativeType
4378 # We don't always need to root obj, but there are a variety
4379 # of cases where we do, so for simplicity, just always root it.
4380 if descriptor.proxy:
4381 if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
4382 assert not descriptor.isMaybeCrossOriginObject()
4383 create = dedent(
4385 aObject->mExpandoAndGeneration.expando.setUndefined();
4386 JS::Rooted<JS::Value> expandoValue(aCx, JS::PrivateValue(&aObject->mExpandoAndGeneration));
4387 creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(),
4388 proto, /* aLazyProto = */ false, aObject,
4389 expandoValue, aReflector);
4392 else:
4393 if descriptor.isMaybeCrossOriginObject():
4394 proto = "nullptr"
4395 lazyProto = "true"
4396 else:
4397 proto = "proto"
4398 lazyProto = "false"
4399 create = fill(
4401 creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(),
4402 ${proto}, /* aLazyProto = */ ${lazyProto},
4403 aObject, JS::UndefinedHandleValue, aReflector);
4404 """,
4405 proto=proto,
4406 lazyProto=lazyProto,
4408 else:
4409 create = dedent(
4411 creator.CreateObject(aCx, sClass.ToJSClass(), proto, aObject, aReflector);
4414 return (
4415 objDecl
4416 + create
4417 + dedent(
4419 if (!aReflector) {
4420 return false;
4427 def InitUnforgeablePropertiesOnHolder(
4428 descriptor, properties, failureCode, holderName="unforgeableHolder"
4431 Define the unforgeable properties on the unforgeable holder for
4432 the interface represented by descriptor.
4434 properties is a PropertyArrays instance.
4437 assert (
4438 properties.unforgeableAttrs.hasNonChromeOnly()
4439 or properties.unforgeableAttrs.hasChromeOnly()
4440 or properties.unforgeableMethods.hasNonChromeOnly()
4441 or properties.unforgeableMethods.hasChromeOnly()
4444 unforgeables = []
4446 defineUnforgeableAttrs = fill(
4448 if (!DefineLegacyUnforgeableAttributes(aCx, ${holderName}, %s)) {
4449 $*{failureCode}
4451 """,
4452 failureCode=failureCode,
4453 holderName=holderName,
4455 defineUnforgeableMethods = fill(
4457 if (!DefineLegacyUnforgeableMethods(aCx, ${holderName}, %s)) {
4458 $*{failureCode}
4460 """,
4461 failureCode=failureCode,
4462 holderName=holderName,
4465 unforgeableMembers = [
4466 (defineUnforgeableAttrs, properties.unforgeableAttrs),
4467 (defineUnforgeableMethods, properties.unforgeableMethods),
4469 for template, array in unforgeableMembers:
4470 if array.hasNonChromeOnly():
4471 unforgeables.append(CGGeneric(template % array.variableName(False)))
4472 if array.hasChromeOnly():
4473 unforgeables.append(
4474 CGIfWrapper(
4475 CGGeneric(template % array.variableName(True)),
4476 "nsContentUtils::ThreadsafeIsSystemCaller(aCx)",
4480 if descriptor.interface.getExtendedAttribute("LegacyUnforgeable"):
4481 # We do our undefined toPrimitive here, not as a regular property
4482 # because we don't have a concept of value props anywhere in IDL.
4483 unforgeables.append(
4484 CGGeneric(
4485 fill(
4487 JS::Rooted<JS::PropertyKey> toPrimitive(aCx,
4488 JS::GetWellKnownSymbolKey(aCx, JS::SymbolCode::toPrimitive));
4489 if (!JS_DefinePropertyById(aCx, ${holderName}, toPrimitive,
4490 JS::UndefinedHandleValue,
4491 JSPROP_READONLY | JSPROP_PERMANENT)) {
4492 $*{failureCode}
4494 """,
4495 failureCode=failureCode,
4496 holderName=holderName,
4501 return CGWrapper(CGList(unforgeables), pre="\n")
4504 def CopyUnforgeablePropertiesToInstance(descriptor, failureCode):
4506 Copy the unforgeable properties from the unforgeable holder for
4507 this interface to the instance object we have.
4509 assert not descriptor.isGlobal()
4511 if not descriptor.hasLegacyUnforgeableMembers:
4512 return ""
4514 copyCode = [
4515 CGGeneric(
4516 dedent(
4518 // Important: do unforgeable property setup after we have handed
4519 // over ownership of the C++ object to obj as needed, so that if
4520 // we fail and it ends up GCed it won't have problems in the
4521 // finalizer trying to drop its ownership of the C++ object.
4527 # For proxies, we want to define on the expando object, not directly on the
4528 # reflector, so we can make sure we don't get confused by named getters.
4529 if descriptor.proxy:
4530 copyCode.append(
4531 CGGeneric(
4532 fill(
4534 JS::Rooted<JSObject*> expando(aCx,
4535 DOMProxyHandler::EnsureExpandoObject(aCx, aReflector));
4536 if (!expando) {
4537 $*{failureCode}
4539 """,
4540 failureCode=failureCode,
4544 obj = "expando"
4545 else:
4546 obj = "aReflector"
4548 copyCode.append(
4549 CGGeneric(
4550 fill(
4552 JS::Rooted<JSObject*> unforgeableHolder(aCx,
4553 &JS::GetReservedSlot(canonicalProto, DOM_INTERFACE_PROTO_SLOTS_BASE).toObject());
4554 if (!JS_InitializePropertiesFromCompatibleNativeObject(aCx, ${obj}, unforgeableHolder)) {
4555 $*{failureCode}
4557 """,
4558 obj=obj,
4559 failureCode=failureCode,
4564 return CGWrapper(CGList(copyCode), pre="\n").define()
4567 def AssertInheritanceChain(descriptor):
4568 # We can skip the reinterpret_cast check for the descriptor's nativeType
4569 # if aObject is a pointer of that type.
4570 asserts = fill(
4572 static_assert(std::is_same_v<decltype(aObject), ${nativeType}*>);
4573 """,
4574 nativeType=descriptor.nativeType,
4576 iface = descriptor.interface
4577 while iface.parent:
4578 iface = iface.parent
4579 desc = descriptor.getDescriptor(iface.identifier.name)
4580 asserts += (
4581 "MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
4582 " reinterpret_cast<%s*>(aObject),\n"
4583 ' "Multiple inheritance for %s is broken.");\n'
4584 % (desc.nativeType, desc.nativeType, desc.nativeType)
4586 asserts += "MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n"
4587 return asserts
4590 def InitMemberSlots(descriptor, failureCode):
4592 Initialize member slots on our JS object if we're supposed to have some.
4594 Note that this is called after the SetWrapper() call in the
4595 wrapperCache case, since that can affect how our getters behave
4596 and we plan to invoke them here. So if we fail, we need to
4597 ClearWrapper.
4599 if not descriptor.interface.hasMembersInSlots():
4600 return ""
4601 return fill(
4603 if (!UpdateMemberSlots(aCx, aReflector, aObject)) {
4604 $*{failureCode}
4606 """,
4607 failureCode=failureCode,
4611 def DeclareProto(descriptor, noGivenProto=False):
4613 Declare the canonicalProto and proto we have for our wrapping operation.
4615 getCanonical = dedent(
4617 JS::Handle<JSObject*> ${canonicalProto} = GetProtoObjectHandle(aCx);
4618 if (!${canonicalProto}) {
4619 return false;
4624 if noGivenProto:
4625 return fill(getCanonical, canonicalProto="proto")
4627 getCanonical = fill(getCanonical, canonicalProto="canonicalProto")
4629 preamble = getCanonical + dedent(
4631 JS::Rooted<JSObject*> proto(aCx);
4634 if descriptor.isMaybeCrossOriginObject():
4635 return preamble + dedent(
4637 MOZ_ASSERT(!aGivenProto,
4638 "Shouldn't have constructors on cross-origin objects");
4639 // Set proto to canonicalProto to avoid preserving our wrapper if
4640 // we don't have to.
4641 proto = canonicalProto;
4645 return preamble + dedent(
4647 if (aGivenProto) {
4648 proto = aGivenProto;
4649 // Unfortunately, while aGivenProto was in the compartment of aCx
4650 // coming in, we changed compartments to that of "parent" so may need
4651 // to wrap the proto here.
4652 if (js::GetContextCompartment(aCx) != JS::GetCompartment(proto)) {
4653 if (!JS_WrapObject(aCx, &proto)) {
4654 return false;
4657 } else {
4658 proto = canonicalProto;
4664 class CGWrapWithCacheMethod(CGAbstractMethod):
4666 Create a wrapper JSObject for a given native that implements nsWrapperCache.
4669 def __init__(self, descriptor):
4670 assert descriptor.interface.hasInterfacePrototypeObject()
4671 args = [
4672 Argument("JSContext*", "aCx"),
4673 Argument(descriptor.nativeType + "*", "aObject"),
4674 Argument("nsWrapperCache*", "aCache"),
4675 Argument("JS::Handle<JSObject*>", "aGivenProto"),
4676 Argument("JS::MutableHandle<JSObject*>", "aReflector"),
4678 CGAbstractMethod.__init__(self, descriptor, "Wrap", "bool", args)
4680 def definition_body(self):
4681 failureCode = dedent(
4683 aCache->ReleaseWrapper(aObject);
4684 aCache->ClearWrapper();
4685 return false;
4689 if self.descriptor.proxy:
4690 finalize = "DOMProxyHandler::getInstance()->finalize"
4691 else:
4692 finalize = FINALIZE_HOOK_NAME
4694 return fill(
4696 static_assert(!std::is_base_of_v<NonRefcountedDOMObject, ${nativeType}>,
4697 "Shouldn't have wrappercached things that are not refcounted.");
4698 $*{assertInheritance}
4699 MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx));
4700 MOZ_ASSERT(!aCache->GetWrapper(),
4701 "You should probably not be using Wrap() directly; use "
4702 "GetOrCreateDOMReflector instead");
4704 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
4705 "nsISupports must be on our primary inheritance chain");
4707 // If the wrapper cache contains a dead reflector then finalize that
4708 // now, ensuring that the finalizer for the old reflector always
4709 // runs before the new reflector is created and attached. This
4710 // avoids the awkward situation where there are multiple reflector
4711 // objects that contain pointers to the same native.
4713 if (JSObject* oldReflector = aCache->GetWrapperMaybeDead()) {
4714 ${finalize}(nullptr /* unused */, oldReflector);
4715 MOZ_ASSERT(!aCache->GetWrapperMaybeDead());
4718 JS::Rooted<JSObject*> global(aCx, FindAssociatedGlobal(aCx, aObject->GetParentObject()));
4719 if (!global) {
4720 return false;
4722 MOZ_ASSERT(JS_IsGlobalObject(global));
4723 JS::AssertObjectIsNotGray(global);
4725 // That might have ended up wrapping us already, due to the wonders
4726 // of XBL. Check for that, and bail out as needed.
4727 aReflector.set(aCache->GetWrapper());
4728 if (aReflector) {
4729 #ifdef DEBUG
4730 AssertReflectorHasGivenProto(aCx, aReflector, aGivenProto);
4731 #endif // DEBUG
4732 return true;
4735 JSAutoRealm ar(aCx, global);
4736 $*{declareProto}
4738 $*{createObject}
4740 aCache->SetWrapper(aReflector);
4741 $*{unforgeable}
4742 $*{slots}
4743 creator.InitializationSucceeded();
4745 MOZ_ASSERT(aCache->GetWrapperPreserveColor() &&
4746 aCache->GetWrapperPreserveColor() == aReflector);
4747 // If proto != canonicalProto, we have to preserve our wrapper;
4748 // otherwise we won't be able to properly recreate it later, since
4749 // we won't know what proto to use. Note that we don't check
4750 // aGivenProto here, since it's entirely possible (and even
4751 // somewhat common) to have a non-null aGivenProto which is the
4752 // same as canonicalProto.
4753 if (proto != canonicalProto) {
4754 PreserveWrapper(aObject);
4757 return true;
4758 """,
4759 nativeType=self.descriptor.nativeType,
4760 assertInheritance=AssertInheritanceChain(self.descriptor),
4761 declareProto=DeclareProto(self.descriptor),
4762 createObject=CreateBindingJSObject(self.descriptor),
4763 unforgeable=CopyUnforgeablePropertiesToInstance(
4764 self.descriptor, failureCode
4766 slots=InitMemberSlots(self.descriptor, failureCode),
4767 finalize=finalize,
4771 class CGWrapMethod(CGAbstractMethod):
4772 def __init__(self, descriptor):
4773 # XXX can we wrap if we don't have an interface prototype object?
4774 assert descriptor.interface.hasInterfacePrototypeObject()
4775 args = [
4776 Argument("JSContext*", "aCx"),
4777 Argument("T*", "aObject"),
4778 Argument("JS::Handle<JSObject*>", "aGivenProto"),
4780 CGAbstractMethod.__init__(
4781 self,
4782 descriptor,
4783 "Wrap",
4784 "JSObject*",
4785 args,
4786 inline=True,
4787 templateArgs=["class T"],
4790 def definition_body(self):
4791 return dedent(
4793 JS::Rooted<JSObject*> reflector(aCx);
4794 return Wrap(aCx, aObject, aObject, aGivenProto, &reflector) ? reflector.get() : nullptr;
4799 class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
4801 Create a wrapper JSObject for a given native that does not implement
4802 nsWrapperCache.
4805 def __init__(self, descriptor, static=False, signatureOnly=False):
4806 # XXX can we wrap if we don't have an interface prototype object?
4807 assert descriptor.interface.hasInterfacePrototypeObject()
4808 self.noGivenProto = (
4809 descriptor.interface.isIteratorInterface()
4810 or descriptor.interface.isAsyncIteratorInterface()
4812 args = [
4813 Argument("JSContext*", "aCx"),
4814 Argument(descriptor.nativeType + "*", "aObject"),
4816 if not self.noGivenProto:
4817 args.append(Argument("JS::Handle<JSObject*>", "aGivenProto"))
4818 args.append(Argument("JS::MutableHandle<JSObject*>", "aReflector"))
4819 CGAbstractMethod.__init__(
4820 self,
4821 descriptor,
4822 "Wrap",
4823 "bool",
4824 args,
4825 static=static,
4826 signatureOnly=signatureOnly,
4829 def definition_body(self):
4830 failureCode = "return false;\n"
4832 declareProto = DeclareProto(self.descriptor, noGivenProto=self.noGivenProto)
4833 if self.noGivenProto:
4834 assertGivenProto = ""
4835 else:
4836 assertGivenProto = dedent(
4838 MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx));
4841 return fill(
4843 $*{assertions}
4844 $*{assertGivenProto}
4846 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
4847 $*{declareProto}
4849 $*{createObject}
4851 $*{unforgeable}
4853 $*{slots}
4855 creator.InitializationSucceeded();
4856 return true;
4857 """,
4858 assertions=AssertInheritanceChain(self.descriptor),
4859 assertGivenProto=assertGivenProto,
4860 declareProto=declareProto,
4861 createObject=CreateBindingJSObject(self.descriptor),
4862 unforgeable=CopyUnforgeablePropertiesToInstance(
4863 self.descriptor, failureCode
4865 slots=InitMemberSlots(self.descriptor, failureCode),
4869 class CGWrapGlobalMethod(CGAbstractMethod):
4871 Create a wrapper JSObject for a global. The global must implement
4872 nsWrapperCache.
4874 properties should be a PropertyArrays instance.
4877 def __init__(self, descriptor, properties):
4878 assert (
4879 descriptor.interface.hasInterfacePrototypeObject()
4880 or descriptor.hasOrdinaryObjectPrototype()
4882 args = [
4883 Argument("JSContext*", "aCx"),
4884 Argument(descriptor.nativeType + "*", "aObject"),
4885 Argument("nsWrapperCache*", "aCache"),
4886 Argument("JS::RealmOptions&", "aOptions"),
4887 Argument("JSPrincipals*", "aPrincipal"),
4888 Argument("JS::MutableHandle<JSObject*>", "aReflector"),
4890 CGAbstractMethod.__init__(self, descriptor, "Wrap", "bool", args)
4891 self.descriptor = descriptor
4892 self.properties = properties
4894 def definition_body(self):
4895 if self.properties.hasNonChromeOnly():
4896 properties = "sNativeProperties.Upcast()"
4897 else:
4898 properties = "nullptr"
4899 if self.properties.hasChromeOnly():
4900 chromeProperties = "nsContentUtils::ThreadsafeIsSystemCaller(aCx) ? sChromeOnlyNativeProperties.Upcast() : nullptr"
4901 else:
4902 chromeProperties = "nullptr"
4904 failureCode = dedent(
4906 aCache->ReleaseWrapper(aObject);
4907 aCache->ClearWrapper();
4908 return false;
4912 if self.descriptor.hasLegacyUnforgeableMembers:
4913 unforgeable = InitUnforgeablePropertiesOnHolder(
4914 self.descriptor, self.properties, failureCode, "aReflector"
4915 ).define()
4916 else:
4917 unforgeable = ""
4919 if self.descriptor.hasOrdinaryObjectPrototype():
4920 getProto = "JS::GetRealmObjectPrototypeHandle"
4921 else:
4922 getProto = "GetProtoObjectHandle"
4923 return fill(
4925 $*{assertions}
4926 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
4927 "nsISupports must be on our primary inheritance chain");
4929 if (!CreateGlobal<${nativeType}, ${getProto}>(aCx,
4930 aObject,
4931 aCache,
4932 sClass.ToJSClass(),
4933 aOptions,
4934 aPrincipal,
4935 aReflector)) {
4936 $*{failureCode}
4939 // aReflector is a new global, so has a new realm. Enter it
4940 // before doing anything with it.
4941 JSAutoRealm ar(aCx, aReflector);
4943 if (!DefineProperties(aCx, aReflector, ${properties}, ${chromeProperties})) {
4944 $*{failureCode}
4946 $*{unforgeable}
4948 $*{slots}
4950 return true;
4951 """,
4952 assertions=AssertInheritanceChain(self.descriptor),
4953 nativeType=self.descriptor.nativeType,
4954 getProto=getProto,
4955 properties=properties,
4956 chromeProperties=chromeProperties,
4957 failureCode=failureCode,
4958 unforgeable=unforgeable,
4959 slots=InitMemberSlots(self.descriptor, failureCode),
4963 class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod):
4964 def __init__(self, descriptor):
4965 args = [
4966 Argument("JSContext*", "aCx"),
4967 Argument("JS::Handle<JSObject*>", "aWrapper"),
4968 Argument(descriptor.nativeType + "*", "aObject"),
4970 CGAbstractStaticMethod.__init__(
4971 self, descriptor, "UpdateMemberSlots", "bool", args
4974 def definition_body(self):
4975 body = "JS::Rooted<JS::Value> temp(aCx);\n" "JSJitGetterCallArgs args(&temp);\n"
4976 for m in self.descriptor.interface.members:
4977 if m.isAttr() and m.getExtendedAttribute("StoreInSlot"):
4978 # Skip doing this for the "window" and "self" attributes on the
4979 # Window interface, because those can't be gotten safely until
4980 # we have hooked it up correctly to the outer window. The
4981 # window code handles doing the get itself.
4982 if self.descriptor.interface.identifier.name == "Window" and (
4983 m.identifier.name == "window" or m.identifier.name == "self"
4985 continue
4986 body += fill(
4989 static_assert(${slot} < JS::shadow::Object::MAX_FIXED_SLOTS,
4990 "Not enough fixed slots to fit '${interface}.${member}. Ion's visitGetDOMMemberV/visitGetDOMMemberT assume StoreInSlot things are all in fixed slots.");
4991 if (!get_${member}(aCx, aWrapper, aObject, args)) {
4992 return false;
4994 // Getter handled setting our reserved slots
4995 """,
4996 slot=memberReservedSlot(m, self.descriptor),
4997 interface=self.descriptor.interface.identifier.name,
4998 member=m.identifier.name,
5001 body += "\nreturn true;\n"
5002 return body
5005 class CGClearCachedValueMethod(CGAbstractMethod):
5006 def __init__(self, descriptor, member):
5007 self.member = member
5008 # If we're StoreInSlot, we'll need to call the getter
5009 if member.getExtendedAttribute("StoreInSlot"):
5010 args = [Argument("JSContext*", "aCx")]
5011 returnType = "bool"
5012 else:
5013 args = []
5014 returnType = "void"
5015 args.append(Argument(descriptor.nativeType + "*", "aObject"))
5016 name = MakeClearCachedValueNativeName(member)
5017 CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
5019 def definition_body(self):
5020 slotIndex = memberReservedSlot(self.member, self.descriptor)
5021 clearCachedValue = fill(
5023 JS::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue());
5024 """,
5025 slotIndex=slotIndex,
5027 if self.member.getExtendedAttribute("StoreInSlot"):
5028 # We have to root things and save the old value in case
5029 # regetting fails, so we can restore it.
5030 declObj = "JS::Rooted<JSObject*> obj(aCx);\n"
5031 noopRetval = " true"
5032 saveMember = (
5033 "JS::Rooted<JS::Value> oldValue(aCx, JS::GetReservedSlot(obj, %s));\n"
5034 % slotIndex
5036 regetMember = fill(
5038 JS::Rooted<JS::Value> temp(aCx);
5039 JSJitGetterCallArgs args(&temp);
5040 JSAutoRealm ar(aCx, obj);
5041 if (!get_${name}(aCx, obj, aObject, args)) {
5042 JS::SetReservedSlot(obj, ${slotIndex}, oldValue);
5043 return false;
5045 return true;
5046 """,
5047 name=self.member.identifier.name,
5048 slotIndex=slotIndex,
5050 else:
5051 declObj = "JSObject* obj;\n"
5052 noopRetval = ""
5053 saveMember = ""
5054 if self.member.getExtendedAttribute(
5055 "ReflectedHTMLAttributeReturningFrozenArray"
5057 clearCachedValue = fill(
5059 ReflectedHTMLAttributeSlots::Clear(obj, ${arrayIndex});
5060 """,
5061 arrayIndex=reflectedHTMLAttributesArrayIndex(
5062 self.descriptor, self.member
5065 regetMember = ""
5067 if self.descriptor.wantsXrays:
5068 if self.member.getExtendedAttribute("StoreInSlot"):
5069 cx = "JS::RootingContext::get(aCx)"
5070 else:
5071 cx = "RootingCx()"
5072 if self.member.getExtendedAttribute(
5073 "ReflectedHTMLAttributeReturningFrozenArray"
5075 clearXrayExpandoSlots = fill(
5077 ReflectedHTMLAttributeSlots::ClearInXrays(${cx}, obj, ${arrayIndex});
5078 """,
5079 cx=cx,
5080 arrayIndex=reflectedHTMLAttributesArrayIndex(
5081 self.descriptor, self.member
5084 else:
5085 clearXrayExpandoSlots = fill(
5087 ClearXrayExpandoSlots(${cx}, obj, ${xraySlotIndex});
5088 """,
5089 cx=cx,
5090 xraySlotIndex=memberXrayExpandoReservedSlot(
5091 self.member, self.descriptor
5094 else:
5095 clearXrayExpandoSlots = ""
5097 return fill(
5099 $*{declObj}
5100 obj = aObject->GetWrapper();
5101 if (!obj) {
5102 return${noopRetval};
5104 $*{saveMember}
5105 $*{clearCachedValue}
5106 $*{clearXrayExpandoSlots}
5107 $*{regetMember}
5108 """,
5109 declObj=declObj,
5110 noopRetval=noopRetval,
5111 saveMember=saveMember,
5112 slotIndex=slotIndex,
5113 clearCachedValue=clearCachedValue,
5114 clearXrayExpandoSlots=clearXrayExpandoSlots,
5115 regetMember=regetMember,
5119 class CGCrossOriginProperties(CGThing):
5120 def __init__(self, descriptor):
5121 attrs = []
5122 chromeOnlyAttrs = []
5123 methods = []
5124 chromeOnlyMethods = []
5125 for m in descriptor.interface.members:
5126 if m.isAttr() and (
5127 m.getExtendedAttribute("CrossOriginReadable")
5128 or m.getExtendedAttribute("CrossOriginWritable")
5130 if m.isStatic():
5131 raise TypeError(
5132 "Don't know how to deal with static method %s"
5133 % m.identifier.name
5135 if PropertyDefiner.getControllingCondition(
5136 m, descriptor
5137 ).hasDisablers():
5138 raise TypeError(
5139 "Don't know how to deal with disabler for %s"
5140 % m.identifier.name
5142 if len(m.bindingAliases) > 0:
5143 raise TypeError(
5144 "Don't know how to deal with aliases for %s" % m.identifier.name
5146 if m.getExtendedAttribute("ChromeOnly") is not None:
5147 chromeOnlyAttrs.extend(AttrDefiner.attrData(m, overrideFlags="0"))
5148 else:
5149 attrs.extend(AttrDefiner.attrData(m, overrideFlags="0"))
5150 elif m.isMethod() and m.getExtendedAttribute("CrossOriginCallable"):
5151 if m.isStatic():
5152 raise TypeError(
5153 "Don't know how to deal with static method %s"
5154 % m.identifier.name
5156 if PropertyDefiner.getControllingCondition(
5157 m, descriptor
5158 ).hasDisablers():
5159 raise TypeError(
5160 "Don't know how to deal with disabler for %s"
5161 % m.identifier.name
5163 if len(m.aliases) > 0:
5164 raise TypeError(
5165 "Don't know how to deal with aliases for %s" % m.identifier.name
5167 if m.getExtendedAttribute("ChromeOnly") is not None:
5168 chromeOnlyMethods.append(
5169 MethodDefiner.methodData(
5170 m, descriptor, overrideFlags="JSPROP_READONLY"
5173 else:
5174 methods.append(
5175 MethodDefiner.methodData(
5176 m, descriptor, overrideFlags="JSPROP_READONLY"
5180 if len(attrs) > 0:
5181 self.attributeSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
5182 attrs,
5183 descriptor,
5184 AttrDefiner.formatSpec,
5185 " JS_PS_END\n",
5186 AttrDefiner.condition,
5187 functools.partial(AttrDefiner.specData, crossOriginOnly=True),
5189 else:
5190 self.attributeSpecs = [" JS_PS_END\n"]
5191 if len(methods) > 0:
5192 self.methodSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
5193 methods,
5194 descriptor,
5195 MethodDefiner.formatSpec,
5196 " JS_FS_END\n",
5197 MethodDefiner.condition,
5198 MethodDefiner.specData,
5200 else:
5201 self.methodSpecs = [" JS_FS_END\n"]
5203 if len(chromeOnlyAttrs) > 0:
5205 self.chromeOnlyAttributeSpecs,
5207 ) = PropertyDefiner.generatePrefableArrayValues(
5208 chromeOnlyAttrs,
5209 descriptor,
5210 AttrDefiner.formatSpec,
5211 " JS_PS_END\n",
5212 AttrDefiner.condition,
5213 functools.partial(AttrDefiner.specData, crossOriginOnly=True),
5215 else:
5216 self.chromeOnlyAttributeSpecs = []
5217 if len(chromeOnlyMethods) > 0:
5218 self.chromeOnlyMethodSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
5219 chromeOnlyMethods,
5220 descriptor,
5221 MethodDefiner.formatSpec,
5222 " JS_FS_END\n",
5223 MethodDefiner.condition,
5224 MethodDefiner.specData,
5226 else:
5227 self.chromeOnlyMethodSpecs = []
5229 def declare(self):
5230 return dedent(
5232 extern const CrossOriginProperties sCrossOriginProperties;
5236 def define(self):
5237 def defineChromeOnly(name, specs, specType):
5238 if len(specs) == 0:
5239 return ("", "nullptr")
5240 name = "sChromeOnlyCrossOrigin" + name
5241 define = fill(
5243 static const ${specType} ${name}[] = {
5244 $*{specs}
5246 """,
5247 specType=specType,
5248 name=name,
5249 specs=",\n".join(specs),
5251 return (define, name)
5253 chromeOnlyAttributes = defineChromeOnly(
5254 "Attributes", self.chromeOnlyAttributeSpecs, "JSPropertySpec"
5256 chromeOnlyMethods = defineChromeOnly(
5257 "Methods", self.chromeOnlyMethodSpecs, "JSFunctionSpec"
5259 return fill(
5261 static const JSPropertySpec sCrossOriginAttributes[] = {
5262 $*{attributeSpecs}
5264 static const JSFunctionSpec sCrossOriginMethods[] = {
5265 $*{methodSpecs}
5267 $*{chromeOnlyAttributeSpecs}
5268 $*{chromeOnlyMethodSpecs}
5269 const CrossOriginProperties sCrossOriginProperties = {
5270 sCrossOriginAttributes,
5271 sCrossOriginMethods,
5272 ${chromeOnlyAttributes},
5273 ${chromeOnlyMethods}
5275 """,
5276 attributeSpecs=",\n".join(self.attributeSpecs),
5277 methodSpecs=",\n".join(self.methodSpecs),
5278 chromeOnlyAttributeSpecs=chromeOnlyAttributes[0],
5279 chromeOnlyMethodSpecs=chromeOnlyMethods[0],
5280 chromeOnlyAttributes=chromeOnlyAttributes[1],
5281 chromeOnlyMethods=chromeOnlyMethods[1],
5285 class CGCycleCollectionTraverseForOwningUnionMethod(CGAbstractMethod):
5287 ImplCycleCollectionUnlink for owning union type.
5290 def __init__(self, type):
5291 self.type = type
5292 args = [
5293 Argument("nsCycleCollectionTraversalCallback&", "aCallback"),
5294 Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion"),
5295 Argument("const char*", "aName"),
5296 Argument("uint32_t", "aFlags", "0"),
5298 CGAbstractMethod.__init__(
5299 self, None, "ImplCycleCollectionTraverse", "void", args
5302 def deps(self):
5303 return self.type.getDeps()
5305 def definition_body(self):
5306 memberNames = [
5307 getUnionMemberName(t)
5308 for t in self.type.flatMemberTypes
5309 if idlTypeNeedsCycleCollection(t)
5311 assert memberNames
5313 conditionTemplate = "aUnion.Is%s()"
5314 functionCallTemplate = (
5315 'ImplCycleCollectionTraverse(aCallback, aUnion.GetAs%s(), "m%s", aFlags);\n'
5318 ifStaments = (
5319 CGIfWrapper(CGGeneric(functionCallTemplate % (m, m)), conditionTemplate % m)
5320 for m in memberNames
5323 return CGElseChain(ifStaments).define()
5326 class CGCycleCollectionUnlinkForOwningUnionMethod(CGAbstractMethod):
5328 ImplCycleCollectionUnlink for owning union type.
5331 def __init__(self, type):
5332 self.type = type
5333 args = [Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion")]
5334 CGAbstractMethod.__init__(self, None, "ImplCycleCollectionUnlink", "void", args)
5336 def deps(self):
5337 return self.type.getDeps()
5339 def definition_body(self):
5340 return "aUnion.Uninit();\n"
5343 builtinNames = {
5344 IDLType.Tags.bool: "bool",
5345 IDLType.Tags.int8: "int8_t",
5346 IDLType.Tags.int16: "int16_t",
5347 IDLType.Tags.int32: "int32_t",
5348 IDLType.Tags.int64: "int64_t",
5349 IDLType.Tags.uint8: "uint8_t",
5350 IDLType.Tags.uint16: "uint16_t",
5351 IDLType.Tags.uint32: "uint32_t",
5352 IDLType.Tags.uint64: "uint64_t",
5353 IDLType.Tags.unrestricted_float: "float",
5354 IDLType.Tags.float: "float",
5355 IDLType.Tags.unrestricted_double: "double",
5356 IDLType.Tags.double: "double",
5359 numericSuffixes = {
5360 IDLType.Tags.int8: "",
5361 IDLType.Tags.uint8: "",
5362 IDLType.Tags.int16: "",
5363 IDLType.Tags.uint16: "",
5364 IDLType.Tags.int32: "",
5365 IDLType.Tags.uint32: "U",
5366 IDLType.Tags.int64: "LL",
5367 IDLType.Tags.uint64: "ULL",
5368 IDLType.Tags.unrestricted_float: "F",
5369 IDLType.Tags.float: "F",
5370 IDLType.Tags.unrestricted_double: "",
5371 IDLType.Tags.double: "",
5375 def numericValue(t, v):
5376 if t == IDLType.Tags.unrestricted_double or t == IDLType.Tags.unrestricted_float:
5377 typeName = builtinNames[t]
5378 if v == float("inf"):
5379 return "mozilla::PositiveInfinity<%s>()" % typeName
5380 if v == float("-inf"):
5381 return "mozilla::NegativeInfinity<%s>()" % typeName
5382 if math.isnan(v):
5383 return "mozilla::UnspecifiedNaN<%s>()" % typeName
5384 return "%s%s" % (v, numericSuffixes[t])
5387 class CastableObjectUnwrapper:
5389 A class for unwrapping an object stored in a JS Value (or
5390 MutableHandle<Value> or Handle<Value>) named by the "source" and
5391 "mutableSource" arguments based on the passed-in descriptor and storing it
5392 in a variable called by the name in the "target" argument. The "source"
5393 argument should be able to produce a Value or Handle<Value>; the
5394 "mutableSource" argument should be able to produce a MutableHandle<Value>
5396 codeOnFailure is the code to run if unwrapping fails.
5398 If isCallbackReturnValue is "JSImpl" and our descriptor is also
5399 JS-implemented, fall back to just creating the right object if what we
5400 have isn't one already.
5403 def __init__(
5404 self,
5405 descriptor,
5406 source,
5407 mutableSource,
5408 target,
5409 codeOnFailure,
5410 exceptionCode=None,
5411 isCallbackReturnValue=False,
5413 self.substitution = {
5414 "type": descriptor.nativeType,
5415 "protoID": "prototypes::id::" + descriptor.name,
5416 "target": target,
5417 "codeOnFailure": codeOnFailure,
5418 "source": source,
5419 "mutableSource": mutableSource,
5422 if isCallbackReturnValue == "JSImpl" and descriptor.interface.isJSImplemented():
5423 exceptionCode = exceptionCode or codeOnFailure
5424 self.substitution["codeOnFailure"] = fill(
5426 // Be careful to not wrap random DOM objects here, even if
5427 // they're wrapped in opaque security wrappers for some reason.
5428 // XXXbz Wish we could check for a JS-implemented object
5429 // that already has a content reflection...
5430 if (!IsDOMObject(js::UncheckedUnwrap(&${source}.toObject()))) {
5431 nsCOMPtr<nsIGlobalObject> contentGlobal;
5432 JS::Rooted<JSObject*> callback(cx, CallbackOrNull());
5433 if (!callback ||
5434 !GetContentGlobalForJSImplementedObject(cx, callback, getter_AddRefs(contentGlobal))) {
5435 $*{exceptionCode}
5437 JS::Rooted<JSObject*> jsImplSourceObj(cx, &${source}.toObject());
5438 MOZ_RELEASE_ASSERT(!js::IsWrapper(jsImplSourceObj),
5439 "Don't return JS implementations from other compartments");
5440 JS::Rooted<JSObject*> jsImplSourceGlobal(cx, JS::GetNonCCWObjectGlobal(jsImplSourceObj));
5441 ${target} = new ${type}(jsImplSourceObj, jsImplSourceGlobal, contentGlobal);
5442 } else {
5443 $*{codeOnFailure}
5445 """,
5446 exceptionCode=exceptionCode,
5447 **self.substitution,
5449 else:
5450 self.substitution["codeOnFailure"] = codeOnFailure
5452 def __str__(self):
5453 substitution = self.substitution.copy()
5454 substitution["codeOnFailure"] %= {
5455 "securityError": "rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO"
5457 return fill(
5460 // Our JSContext should be in the right global to do unwrapping in.
5461 nsresult rv = UnwrapObject<${protoID}, ${type}>(${mutableSource}, ${target}, cx);
5462 if (NS_FAILED(rv)) {
5463 $*{codeOnFailure}
5466 """,
5467 **substitution,
5471 class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
5473 As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
5476 def __init__(
5477 self,
5478 descriptor,
5479 source,
5480 mutableSource,
5481 target,
5482 exceptionCode,
5483 isCallbackReturnValue,
5484 sourceDescription,
5486 CastableObjectUnwrapper.__init__(
5487 self,
5488 descriptor,
5489 source,
5490 mutableSource,
5491 target,
5492 'cx.ThrowErrorMessage<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("%s", "%s");\n'
5493 "%s"
5494 % (sourceDescription, descriptor.interface.identifier.name, exceptionCode),
5495 exceptionCode,
5496 isCallbackReturnValue,
5500 def getCallbackConversionInfo(
5501 type, idlObject, isMember, isCallbackReturnValue, isOptional
5504 Returns a tuple containing the declType, declArgs, and basic
5505 conversion for the given callback type, with the given callback
5506 idl object in the given context (isMember/isCallbackReturnValue/isOptional).
5508 name = idlObject.identifier.name
5510 # We can't use fast callbacks if isOptional because then we get an
5511 # Optional<RootedCallback> thing, which is not transparent to consumers.
5512 useFastCallback = (
5513 (not isMember or isMember == "Union")
5514 and not isCallbackReturnValue
5515 and not isOptional
5517 if useFastCallback:
5518 name = "binding_detail::Fast%s" % name
5519 rootArgs = ""
5520 args = "&${val}.toObject(), JS::CurrentGlobalOrNull(cx)"
5521 else:
5522 rootArgs = dedent(
5524 JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject());
5525 JS::Rooted<JSObject*> tempGlobalRoot(cx, JS::CurrentGlobalOrNull(cx));
5528 args = "cx, tempRoot, tempGlobalRoot, GetIncumbentGlobal()"
5530 if type.nullable() or isCallbackReturnValue:
5531 declType = CGGeneric("RefPtr<%s>" % name)
5532 else:
5533 declType = CGGeneric("OwningNonNull<%s>" % name)
5535 if useFastCallback:
5536 declType = CGTemplatedType("RootedCallback", declType)
5537 declArgs = "cx"
5538 else:
5539 declArgs = None
5541 conversion = fill(
5543 { // scope for tempRoot and tempGlobalRoot if needed
5544 $*{rootArgs}
5545 $${declName} = new ${name}(${args});
5547 """,
5548 rootArgs=rootArgs,
5549 name=name,
5550 args=args,
5552 return (declType, declArgs, conversion)
5555 class JSToNativeConversionInfo:
5557 An object representing information about a JS-to-native conversion.
5560 def __init__(
5561 self,
5562 template,
5563 declType=None,
5564 holderType=None,
5565 dealWithOptional=False,
5566 declArgs=None,
5567 holderArgs=None,
5570 template: A string representing the conversion code. This will have
5571 template substitution performed on it as follows:
5573 ${val} is a handle to the JS::Value in question
5574 ${maybeMutableVal} May be a mutable handle to the JS::Value in
5575 question. This is only OK to use if ${val} is
5576 known to not be undefined.
5577 ${holderName} replaced by the holder's name, if any
5578 ${declName} replaced by the declaration's name
5579 ${haveValue} replaced by an expression that evaluates to a boolean
5580 for whether we have a JS::Value. Only used when
5581 defaultValue is not None or when True is passed for
5582 checkForValue to instantiateJSToNativeConversion.
5583 This expression may not be already-parenthesized, so if
5584 you use it with && or || make sure to put parens
5585 around it.
5586 ${passedToJSImpl} replaced by an expression that evaluates to a boolean
5587 for whether this value is being passed to a JS-
5588 implemented interface.
5590 declType: A CGThing representing the native C++ type we're converting
5591 to. This is allowed to be None if the conversion code is
5592 supposed to be used as-is.
5594 holderType: A CGThing representing the type of a "holder" which will
5595 hold a possible reference to the C++ thing whose type we
5596 returned in declType, or None if no such holder is needed.
5598 dealWithOptional: A boolean indicating whether the caller has to do
5599 optional-argument handling. This should only be set
5600 to true if the JS-to-native conversion is being done
5601 for an optional argument or dictionary member with no
5602 default value and if the returned template expects
5603 both declType and holderType to be wrapped in
5604 Optional<>, with ${declName} and ${holderName}
5605 adjusted to point to the Value() of the Optional, and
5606 Construct() calls to be made on the Optional<>s as
5607 needed.
5609 declArgs: If not None, the arguments to pass to the ${declName}
5610 constructor. These will have template substitution performed
5611 on them so you can use things like ${val}. This is a
5612 single string, not a list of strings.
5614 holderArgs: If not None, the arguments to pass to the ${holderName}
5615 constructor. These will have template substitution
5616 performed on them so you can use things like ${val}.
5617 This is a single string, not a list of strings.
5619 ${declName} must be in scope before the code from 'template' is entered.
5621 If holderType is not None then ${holderName} must be in scope before
5622 the code from 'template' is entered.
5624 assert isinstance(template, str)
5625 assert declType is None or isinstance(declType, CGThing)
5626 assert holderType is None or isinstance(holderType, CGThing)
5627 self.template = template
5628 self.declType = declType
5629 self.holderType = holderType
5630 self.dealWithOptional = dealWithOptional
5631 self.declArgs = declArgs
5632 self.holderArgs = holderArgs
5635 def getHandleDefault(defaultValue):
5636 tag = defaultValue.type.tag()
5637 if tag in numericSuffixes:
5638 # Some numeric literals require a suffix to compile without warnings
5639 return numericValue(tag, defaultValue.value)
5640 assert tag == IDLType.Tags.bool
5641 return toStringBool(defaultValue.value)
5644 def handleDefaultStringValue(defaultValue, method):
5646 Returns a string which ends up calling 'method' with a (char_t*, length)
5647 pair that sets this string default value. This string is suitable for
5648 passing as the second argument of handleDefault.
5650 assert (
5651 defaultValue.type.isDOMString()
5652 or defaultValue.type.isUSVString()
5653 or defaultValue.type.isUTF8String()
5654 or defaultValue.type.isByteString()
5656 # There shouldn't be any non-ASCII or embedded nulls in here; if
5657 # it ever sneaks in we will need to think about how to properly
5658 # represent that in the C++.
5659 assert all(ord(c) < 128 and ord(c) > 0 for c in defaultValue.value)
5660 if defaultValue.type.isByteString() or defaultValue.type.isUTF8String():
5661 prefix = ""
5662 else:
5663 prefix = "u"
5664 return fill(
5666 ${method}(${prefix}"${value}");
5667 """,
5668 method=method,
5669 prefix=prefix,
5670 value=defaultValue.value,
5674 def recordKeyType(recordType):
5675 assert recordType.keyType.isString()
5676 if recordType.keyType.isByteString() or recordType.keyType.isUTF8String():
5677 return "nsCString"
5678 return "nsString"
5681 def recordKeyDeclType(recordType):
5682 return CGGeneric(recordKeyType(recordType))
5685 def initializerForType(type):
5687 Get the right initializer for the given type for a data location where we
5688 plan to then initialize it from a JS::Value. Some types need to always be
5689 initialized even before we start the JS::Value-to-IDL-value conversion.
5691 Returns a string or None if no initialization is needed.
5693 if type.isObject():
5694 return "nullptr"
5695 # We could probably return CGDictionary.getNonInitializingCtorArg() for the
5696 # dictionary case, but code outside DictionaryBase subclasses can't use
5697 # that, so we can't do it across the board.
5698 return None
5701 # If this function is modified, modify CGNativeMember.getArg and
5702 # CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype
5703 # and holdertype we end up using, because it needs to be able to return the code
5704 # that will convert those to the actual return value of the callback function.
5705 def getJSToNativeConversionInfo(
5706 type,
5707 descriptorProvider,
5708 failureCode=None,
5709 isDefinitelyObject=False,
5710 isMember=False,
5711 isOptional=False,
5712 invalidEnumValueFatal=True,
5713 defaultValue=None,
5714 isNullOrUndefined=False,
5715 isKnownMissing=False,
5716 exceptionCode=None,
5717 lenientFloatCode=None,
5718 allowTreatNonCallableAsNull=False,
5719 isCallbackReturnValue=False,
5720 sourceDescription="value",
5721 nestingLevel="",
5724 Get a template for converting a JS value to a native object based on the
5725 given type and descriptor. If failureCode is given, then we're actually
5726 testing whether we can convert the argument to the desired type. That
5727 means that failures to convert due to the JS value being the wrong type of
5728 value need to use failureCode instead of throwing exceptions. Failures to
5729 convert that are due to JS exceptions (from toString or valueOf methods) or
5730 out of memory conditions need to throw exceptions no matter what
5731 failureCode is. However what actually happens when throwing an exception
5732 can be controlled by exceptionCode. The only requirement on that is that
5733 exceptionCode must end up doing a return, and every return from this
5734 function must happen via exceptionCode if exceptionCode is not None.
5736 If isDefinitelyObject is True, that means we have a value and the value
5737 tests true for isObject(), so we have no need to recheck that.
5739 If isNullOrUndefined is True, that means we have a value and the value
5740 tests true for isNullOrUndefined(), so we have no need to recheck that.
5742 If isKnownMissing is True, that means that we are known-missing, and for
5743 cases when we have a default value we only need to output the default value.
5745 if isMember is not False, we're being converted from a property of some JS
5746 object, not from an actual method argument, so we can't rely on our jsval
5747 being rooted or outliving us in any way. Callers can pass "Dictionary",
5748 "Variadic", "Sequence", "Union", or "OwningUnion" to indicate that the conversion
5749 is for something that is a dictionary member, a variadic argument, a sequence,
5750 an union, or an owning union respectively.
5751 XXX Once we swtich *Rooter to Rooted* for Record and Sequence type entirely,
5752 we could remove "Union" from isMember.
5754 If isOptional is true, then we are doing conversion of an optional
5755 argument with no default value.
5757 invalidEnumValueFatal controls whether an invalid enum value conversion
5758 attempt will throw (if true) or simply return without doing anything (if
5759 false).
5761 If defaultValue is not None, it's the IDL default value for this conversion
5763 If isEnforceRange is true, we're converting an integer and throwing if the
5764 value is out of range.
5766 If isClamp is true, we're converting an integer and clamping if the
5767 value is out of range.
5769 If isAllowShared is false, we're converting a buffer source and throwing if
5770 it is a SharedArrayBuffer or backed by a SharedArrayBuffer.
5772 If lenientFloatCode is not None, it should be used in cases when
5773 we're a non-finite float that's not unrestricted.
5775 If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] and
5776 [LegacyTreatNonObjectAsNull] extended attributes on nullable callback functions
5777 will be honored.
5779 If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be
5780 adjusted to make it easier to return from a callback. Since that type is
5781 never directly observable by any consumers of the callback code, this is OK.
5782 Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior
5783 of the FailureFatalCastableObjectUnwrapper conversion; this is used for
5784 implementing auto-wrapping of JS-implemented return values from a
5785 JS-implemented interface.
5787 sourceDescription is a description of what this JS value represents, to be
5788 used in error reporting. Callers should assume that it might get placed in
5789 the middle of a sentence. If it ends up at the beginning of a sentence, its
5790 first character will be automatically uppercased.
5792 The return value from this function is a JSToNativeConversionInfo.
5794 # If we have a defaultValue then we're not actually optional for
5795 # purposes of what we need to be declared as.
5796 assert defaultValue is None or not isOptional
5798 # Also, we should not have a defaultValue if we know we're an object
5799 assert not isDefinitelyObject or defaultValue is None
5801 # And we can't both be an object and be null or undefined
5802 assert not isDefinitelyObject or not isNullOrUndefined
5804 isClamp = type.hasClamp()
5805 isEnforceRange = type.hasEnforceRange()
5806 isAllowShared = type.hasAllowShared()
5808 # If exceptionCode is not set, we'll just rethrow the exception we got.
5809 # Note that we can't just set failureCode to exceptionCode, because setting
5810 # failureCode will prevent pending exceptions from being set in cases when
5811 # they really should be!
5812 if exceptionCode is None:
5813 exceptionCode = "return false;\n"
5815 # Unfortunately, .capitalize() on a string will lowercase things inside the
5816 # string, which we do not want.
5817 def firstCap(string):
5818 return string[0].upper() + string[1:]
5820 # Helper functions for dealing with failures due to the JS value being the
5821 # wrong type of value
5822 def onFailureNotAnObject(failureCode):
5823 return CGGeneric(
5824 failureCode
5825 or (
5826 'cx.ThrowErrorMessage<MSG_NOT_OBJECT>("%s");\n'
5827 "%s" % (firstCap(sourceDescription), exceptionCode)
5831 def onFailureBadType(failureCode, typeName):
5832 return CGGeneric(
5833 failureCode
5834 or (
5835 'cx.ThrowErrorMessage<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("%s", "%s");\n'
5836 "%s" % (firstCap(sourceDescription), typeName, exceptionCode)
5840 # It's a failure in the committed-to conversion, not a failure to match up
5841 # to a type, so we don't want to use failureCode in here. We want to just
5842 # throw an exception unconditionally.
5843 def onFailureIsShared():
5844 return CGGeneric(
5845 'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_SHARED>("%s");\n'
5846 "%s" % (firstCap(sourceDescription), exceptionCode)
5849 def onFailureIsLarge():
5850 return CGGeneric(
5851 'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_LARGE>("%s");\n'
5852 "%s" % (firstCap(sourceDescription), exceptionCode)
5855 def onFailureIsResizable():
5856 return CGGeneric(
5857 'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_RESIZABLE>("%s");\n'
5858 "%s" % (firstCap(sourceDescription), exceptionCode)
5861 def onFailureNotCallable(failureCode):
5862 return CGGeneric(
5863 failureCode
5864 or (
5865 'cx.ThrowErrorMessage<MSG_NOT_CALLABLE>("%s");\n'
5866 "%s" % (firstCap(sourceDescription), exceptionCode)
5870 # A helper function for handling default values. Takes a template
5871 # body and the C++ code to set the default value and wraps the
5872 # given template body in handling for the default value.
5873 def handleDefault(template, setDefault):
5874 if defaultValue is None:
5875 return template
5876 if isKnownMissing:
5877 return fill(
5880 // scope for any temporaries our default value setting needs.
5881 $*{setDefault}
5883 """,
5884 setDefault=setDefault,
5886 return fill(
5888 if ($${haveValue}) {
5889 $*{templateBody}
5890 } else {
5891 $*{setDefault}
5893 """,
5894 templateBody=template,
5895 setDefault=setDefault,
5898 # A helper function for wrapping up the template body for
5899 # possibly-nullable objecty stuff
5900 def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None):
5901 if isNullOrUndefined and type.nullable():
5902 # Just ignore templateBody and set ourselves to null.
5903 # Note that we don't have to worry about default values
5904 # here either, since we already examined this value.
5905 return codeToSetNull
5907 if not isDefinitelyObject:
5908 # Handle the non-object cases by wrapping up the whole
5909 # thing in an if cascade.
5910 if type.nullable():
5911 elifLine = "} else if (${val}.isNullOrUndefined()) {\n"
5912 elifBody = codeToSetNull
5913 else:
5914 elifLine = ""
5915 elifBody = ""
5917 # Note that $${val} below expands to ${val}. This string is
5918 # used as a template later, and val will be filled in then.
5919 templateBody = fill(
5921 if ($${val}.isObject()) {
5922 $*{templateBody}
5923 $*{elifLine}
5924 $*{elifBody}
5925 } else {
5926 $*{failureBody}
5928 """,
5929 templateBody=templateBody,
5930 elifLine=elifLine,
5931 elifBody=elifBody,
5932 failureBody=onFailureNotAnObject(failureCode).define(),
5935 if isinstance(defaultValue, IDLNullValue):
5936 assert type.nullable() # Parser should enforce this
5937 templateBody = handleDefault(templateBody, codeToSetNull)
5938 elif isinstance(defaultValue, IDLEmptySequenceValue):
5939 # Our caller will handle it
5940 pass
5941 else:
5942 assert defaultValue is None
5944 return templateBody
5946 # A helper function for converting things that look like a JSObject*.
5947 def handleJSObjectType(
5948 type, isMember, failureCode, exceptionCode, sourceDescription
5950 if not isMember or isMember == "Union":
5951 if isOptional:
5952 # We have a specialization of Optional that will use a
5953 # Rooted for the storage here.
5954 declType = CGGeneric("JS::Handle<JSObject*>")
5955 else:
5956 declType = CGGeneric("JS::Rooted<JSObject*>")
5957 declArgs = "cx"
5958 else:
5959 assert isMember in (
5960 "Sequence",
5961 "Variadic",
5962 "Dictionary",
5963 "OwningUnion",
5964 "Record",
5966 # We'll get traced by the sequence or dictionary or union tracer
5967 declType = CGGeneric("JSObject*")
5968 declArgs = None
5969 templateBody = "${declName} = &${val}.toObject();\n"
5971 # For JS-implemented APIs, we refuse to allow passing objects that the
5972 # API consumer does not subsume. The extra parens around
5973 # ($${passedToJSImpl}) suppress unreachable code warnings when
5974 # $${passedToJSImpl} is the literal `false`. But Apple is shipping a
5975 # buggy clang (clang 3.9) in Xcode 8.3, so there even the parens are not
5976 # enough. So we manually disable some warnings in clang.
5977 if (
5978 not isinstance(descriptorProvider, Descriptor)
5979 or descriptorProvider.interface.isJSImplemented()
5981 templateBody = (
5982 fill(
5984 #ifdef __clang__
5985 #pragma clang diagnostic push
5986 #pragma clang diagnostic ignored "-Wunreachable-code"
5987 #pragma clang diagnostic ignored "-Wunreachable-code-return"
5988 #endif // __clang__
5989 if (($${passedToJSImpl}) && !CallerSubsumes($${val})) {
5990 cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}");
5991 $*{exceptionCode}
5993 #ifdef __clang__
5994 #pragma clang diagnostic pop
5995 #endif // __clang__
5996 """,
5997 sourceDescription=sourceDescription,
5998 exceptionCode=exceptionCode,
6000 + templateBody
6003 setToNullCode = "${declName} = nullptr;\n"
6004 template = wrapObjectTemplate(templateBody, type, setToNullCode, failureCode)
6005 return JSToNativeConversionInfo(
6006 template, declType=declType, dealWithOptional=isOptional, declArgs=declArgs
6009 def incrementNestingLevel():
6010 if nestingLevel == "":
6011 return 1
6012 return nestingLevel + 1
6014 assert not (isEnforceRange and isClamp) # These are mutually exclusive
6016 if type.isSequence() or type.isObservableArray():
6017 assert not isEnforceRange and not isClamp and not isAllowShared
6019 if failureCode is None:
6020 notSequence = (
6021 'cx.ThrowErrorMessage<MSG_CONVERSION_ERROR>("%s", "%s");\n'
6022 "%s"
6024 firstCap(sourceDescription),
6025 "sequence" if type.isSequence() else "observable array",
6026 exceptionCode,
6029 else:
6030 notSequence = failureCode
6032 nullable = type.nullable()
6033 # Be very careful not to change "type": we need it later
6034 if nullable:
6035 elementType = type.inner.inner
6036 else:
6037 elementType = type.inner
6039 # We want to use auto arrays if we can, but we have to be careful with
6040 # reallocation behavior for arrays. In particular, if we use auto
6041 # arrays for sequences and have a sequence of elements which are
6042 # themselves sequences or have sequences as members, we have a problem.
6043 # In that case, resizing the outermost AutoTArray to the right size
6044 # will memmove its elements, but AutoTArrays are not memmovable and
6045 # hence will end up with pointers to bogus memory, which is bad. To
6046 # deal with this, we typically map WebIDL sequences to our Sequence
6047 # type, which is in fact memmovable. The one exception is when we're
6048 # passing in a sequence directly as an argument without any sort of
6049 # optional or nullable complexity going on. In that situation, we can
6050 # use an AutoSequence instead. We have to keep using Sequence in the
6051 # nullable and optional cases because we don't want to leak the
6052 # AutoSequence type to consumers, which would be unavoidable with
6053 # Nullable<AutoSequence> or Optional<AutoSequence>.
6054 if (
6055 (isMember and isMember != "Union")
6056 or isOptional
6057 or nullable
6058 or isCallbackReturnValue
6060 sequenceClass = "Sequence"
6061 else:
6062 sequenceClass = "binding_detail::AutoSequence"
6064 # XXXbz we can't include the index in the sourceDescription, because
6065 # we don't really have a way to pass one in dynamically at runtime...
6066 elementInfo = getJSToNativeConversionInfo(
6067 elementType,
6068 descriptorProvider,
6069 isMember="Sequence",
6070 exceptionCode=exceptionCode,
6071 lenientFloatCode=lenientFloatCode,
6072 isCallbackReturnValue=isCallbackReturnValue,
6073 sourceDescription="element of %s" % sourceDescription,
6074 nestingLevel=incrementNestingLevel(),
6076 if elementInfo.dealWithOptional:
6077 raise TypeError("Shouldn't have optional things in sequences")
6078 if elementInfo.holderType is not None:
6079 raise TypeError("Shouldn't need holders for sequences")
6081 typeName = CGTemplatedType(sequenceClass, elementInfo.declType)
6082 sequenceType = typeName.define()
6084 if isMember == "Union" and typeNeedsRooting(type):
6085 assert not nullable
6086 typeName = CGTemplatedType(
6087 "binding_detail::RootedAutoSequence", elementInfo.declType
6089 elif nullable:
6090 typeName = CGTemplatedType("Nullable", typeName)
6092 if nullable:
6093 arrayRef = "${declName}.SetValue()"
6094 else:
6095 arrayRef = "${declName}"
6097 elementConversion = string.Template(elementInfo.template).substitute(
6099 "val": "temp" + str(nestingLevel),
6100 "maybeMutableVal": "&temp" + str(nestingLevel),
6101 "declName": "slot" + str(nestingLevel),
6102 # We only need holderName here to handle isExternal()
6103 # interfaces, which use an internal holder for the
6104 # conversion even when forceOwningType ends up true.
6105 "holderName": "tempHolder" + str(nestingLevel),
6106 "passedToJSImpl": "${passedToJSImpl}",
6110 elementInitializer = initializerForType(elementType)
6111 if elementInitializer is None:
6112 elementInitializer = ""
6113 else:
6114 elementInitializer = elementInitializer + ", "
6116 # NOTE: Keep this in sync with variadic conversions as needed
6117 templateBody = fill(
6119 JS::ForOfIterator iter${nestingLevel}(cx);
6120 if (!iter${nestingLevel}.init($${val}, JS::ForOfIterator::AllowNonIterable)) {
6121 $*{exceptionCode}
6123 if (!iter${nestingLevel}.valueIsIterable()) {
6124 $*{notSequence}
6126 ${sequenceType} &arr${nestingLevel} = ${arrayRef};
6127 JS::Rooted<JS::Value> temp${nestingLevel}(cx);
6128 while (true) {
6129 bool done${nestingLevel};
6130 if (!iter${nestingLevel}.next(&temp${nestingLevel}, &done${nestingLevel})) {
6131 $*{exceptionCode}
6133 if (done${nestingLevel}) {
6134 break;
6136 ${elementType}* slotPtr${nestingLevel} = arr${nestingLevel}.AppendElement(${elementInitializer}mozilla::fallible);
6137 if (!slotPtr${nestingLevel}) {
6138 JS_ReportOutOfMemory(cx);
6139 $*{exceptionCode}
6141 ${elementType}& slot${nestingLevel} = *slotPtr${nestingLevel};
6142 $*{elementConversion}
6144 """,
6145 exceptionCode=exceptionCode,
6146 notSequence=notSequence,
6147 sequenceType=sequenceType,
6148 arrayRef=arrayRef,
6149 elementType=elementInfo.declType.define(),
6150 elementConversion=elementConversion,
6151 elementInitializer=elementInitializer,
6152 nestingLevel=str(nestingLevel),
6155 templateBody = wrapObjectTemplate(
6156 templateBody, type, "${declName}.SetNull();\n", notSequence
6158 if isinstance(defaultValue, IDLEmptySequenceValue):
6159 if type.nullable():
6160 codeToSetEmpty = "${declName}.SetValue();\n"
6161 else:
6162 codeToSetEmpty = (
6163 "/* ${declName} array is already empty; nothing to do */\n"
6165 templateBody = handleDefault(templateBody, codeToSetEmpty)
6167 declArgs = None
6168 holderType = None
6169 holderArgs = None
6170 # Sequence arguments that might contain traceable things need
6171 # to get traced
6172 if typeNeedsRooting(elementType):
6173 if not isMember:
6174 holderType = CGTemplatedType("SequenceRooter", elementInfo.declType)
6175 # If our sequence is nullable, this will set the Nullable to be
6176 # not-null, but that's ok because we make an explicit SetNull() call
6177 # on it as needed if our JS value is actually null.
6178 holderArgs = "cx, &%s" % arrayRef
6179 elif isMember == "Union":
6180 declArgs = "cx"
6182 return JSToNativeConversionInfo(
6183 templateBody,
6184 declType=typeName,
6185 declArgs=declArgs,
6186 holderType=holderType,
6187 dealWithOptional=isOptional,
6188 holderArgs=holderArgs,
6191 if type.isRecord():
6192 assert not isEnforceRange and not isClamp and not isAllowShared
6193 if failureCode is None:
6194 notRecord = 'cx.ThrowErrorMessage<MSG_NOT_OBJECT>("%s");\n' "%s" % (
6195 firstCap(sourceDescription),
6196 exceptionCode,
6198 else:
6199 notRecord = failureCode
6201 nullable = type.nullable()
6202 # Be very careful not to change "type": we need it later
6203 if nullable:
6204 recordType = type.inner
6205 else:
6206 recordType = type
6207 valueType = recordType.inner
6209 valueInfo = getJSToNativeConversionInfo(
6210 valueType,
6211 descriptorProvider,
6212 isMember="Record",
6213 exceptionCode=exceptionCode,
6214 lenientFloatCode=lenientFloatCode,
6215 isCallbackReturnValue=isCallbackReturnValue,
6216 sourceDescription="value in %s" % sourceDescription,
6217 nestingLevel=incrementNestingLevel(),
6219 if valueInfo.dealWithOptional:
6220 raise TypeError("Shouldn't have optional things in record")
6221 if valueInfo.holderType is not None:
6222 raise TypeError("Shouldn't need holders for record")
6224 declType = CGTemplatedType(
6225 "Record", [recordKeyDeclType(recordType), valueInfo.declType]
6227 typeName = declType.define()
6229 if isMember == "Union" and typeNeedsRooting(type):
6230 assert not nullable
6231 declType = CGTemplatedType(
6232 "RootedRecord", [recordKeyDeclType(recordType), valueInfo.declType]
6234 elif nullable:
6235 declType = CGTemplatedType("Nullable", declType)
6237 if nullable:
6238 recordRef = "${declName}.SetValue()"
6239 else:
6240 recordRef = "${declName}"
6242 valueConversion = string.Template(valueInfo.template).substitute(
6244 "val": "temp",
6245 "maybeMutableVal": "&temp",
6246 "declName": "slot",
6247 # We only need holderName here to handle isExternal()
6248 # interfaces, which use an internal holder for the
6249 # conversion even when forceOwningType ends up true.
6250 "holderName": "tempHolder",
6251 "passedToJSImpl": "${passedToJSImpl}",
6255 keyType = recordKeyType(recordType)
6256 if recordType.keyType.isJSString():
6257 raise TypeError(
6258 "Have do deal with JSString record type, but don't know how"
6260 if recordType.keyType.isByteString() or recordType.keyType.isUTF8String():
6261 hashKeyType = "nsCStringHashKey"
6262 if recordType.keyType.isByteString():
6263 keyConversionFunction = "ConvertJSValueToByteString"
6264 else:
6265 keyConversionFunction = "ConvertJSValueToString"
6267 else:
6268 hashKeyType = "nsStringHashKey"
6269 if recordType.keyType.isDOMString():
6270 keyConversionFunction = "ConvertJSValueToString"
6271 else:
6272 assert recordType.keyType.isUSVString()
6273 keyConversionFunction = "ConvertJSValueToUSVString"
6275 templateBody = fill(
6277 auto& recordEntries = ${recordRef}.Entries();
6279 JS::Rooted<JSObject*> recordObj(cx, &$${val}.toObject());
6280 JS::RootedVector<jsid> ids(cx);
6281 if (!js::GetPropertyKeys(cx, recordObj,
6282 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &ids)) {
6283 $*{exceptionCode}
6285 if (!recordEntries.SetCapacity(ids.length(), mozilla::fallible)) {
6286 JS_ReportOutOfMemory(cx);
6287 $*{exceptionCode}
6289 JS::Rooted<JS::Value> propNameValue(cx);
6290 JS::Rooted<JS::Value> temp(cx);
6291 JS::Rooted<jsid> curId(cx);
6292 JS::Rooted<JS::Value> idVal(cx);
6293 // Use a hashset to keep track of ids seen, to avoid
6294 // introducing nasty O(N^2) behavior scanning for them all the
6295 // time. Ideally we'd use a data structure with O(1) lookup
6296 // _and_ ordering for the MozMap, but we don't have one lying
6297 // around.
6298 nsTHashtable<${hashKeyType}> idsSeen;
6299 for (size_t i = 0; i < ids.length(); ++i) {
6300 curId = ids[i];
6302 JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> desc(cx);
6303 if (!JS_GetOwnPropertyDescriptorById(cx, recordObj, curId,
6304 &desc)) {
6305 $*{exceptionCode}
6308 if (desc.isNothing() || !desc->enumerable()) {
6309 continue;
6312 idVal = js::IdToValue(curId);
6313 ${keyType} propName;
6314 // This will just throw if idVal is a Symbol, like the spec says
6315 // to do.
6316 if (!${keyConversionFunction}(cx, idVal, "key of ${sourceDescription}", propName)) {
6317 $*{exceptionCode}
6320 if (!JS_GetPropertyById(cx, recordObj, curId, &temp)) {
6321 $*{exceptionCode}
6324 ${typeName}::EntryType* entry;
6325 if (!idsSeen.EnsureInserted(propName)) {
6326 // Find the existing entry.
6327 auto idx = recordEntries.IndexOf(propName);
6328 MOZ_ASSERT(idx != recordEntries.NoIndex,
6329 "Why is it not found?");
6330 // Now blow it away to make it look like it was just added
6331 // to the array, because it's not obvious that it's
6332 // safe to write to its already-initialized mValue via our
6333 // normal codegen conversions. For example, the value
6334 // could be a union and this would change its type, but
6335 // codegen assumes we won't do that.
6336 entry = recordEntries.ReconstructElementAt(idx);
6337 } else {
6338 // Safe to do an infallible append here, because we did a
6339 // SetCapacity above to the right capacity.
6340 entry = recordEntries.AppendElement();
6342 entry->mKey = propName;
6343 ${valueType}& slot = entry->mValue;
6344 $*{valueConversion}
6346 """,
6347 exceptionCode=exceptionCode,
6348 recordRef=recordRef,
6349 hashKeyType=hashKeyType,
6350 keyType=keyType,
6351 keyConversionFunction=keyConversionFunction,
6352 sourceDescription=sourceDescription,
6353 typeName=typeName,
6354 valueType=valueInfo.declType.define(),
6355 valueConversion=valueConversion,
6358 templateBody = wrapObjectTemplate(
6359 templateBody, type, "${declName}.SetNull();\n", notRecord
6362 declArgs = None
6363 holderType = None
6364 holderArgs = None
6365 # record arguments that might contain traceable things need
6366 # to get traced
6367 if not isMember and isCallbackReturnValue:
6368 # Go ahead and just convert directly into our actual return value
6369 declType = CGWrapper(declType, post="&")
6370 declArgs = "aRetVal"
6371 elif typeNeedsRooting(valueType):
6372 if not isMember:
6373 holderType = CGTemplatedType(
6374 "RecordRooter", [recordKeyDeclType(recordType), valueInfo.declType]
6376 # If our record is nullable, this will set the Nullable to be
6377 # not-null, but that's ok because we make an explicit SetNull() call
6378 # on it as needed if our JS value is actually null.
6379 holderArgs = "cx, &%s" % recordRef
6380 elif isMember == "Union":
6381 declArgs = "cx"
6383 return JSToNativeConversionInfo(
6384 templateBody,
6385 declType=declType,
6386 declArgs=declArgs,
6387 holderType=holderType,
6388 dealWithOptional=isOptional,
6389 holderArgs=holderArgs,
6392 if type.isUnion():
6393 nullable = type.nullable()
6394 if nullable:
6395 type = type.inner
6397 isOwningUnion = (isMember and isMember != "Union") or isCallbackReturnValue
6398 unionArgumentObj = "${declName}"
6399 if nullable:
6400 if isOptional and not isOwningUnion:
6401 unionArgumentObj += ".Value()"
6402 # If we're owning, we're a Nullable, which hasn't been told it has
6403 # a value. Otherwise we're an already-constructed Maybe.
6404 unionArgumentObj += ".SetValue()"
6406 templateBody = CGIfWrapper(
6407 CGGeneric(exceptionCode),
6408 '!%s.Init(cx, ${val}, "%s", ${passedToJSImpl})'
6409 % (unionArgumentObj, firstCap(sourceDescription)),
6412 if type.hasNullableType:
6413 assert not nullable
6414 # Make sure to handle a null default value here
6415 if defaultValue and isinstance(defaultValue, IDLNullValue):
6416 assert defaultValue.type == type
6417 templateBody = CGIfElseWrapper(
6418 "!(${haveValue})",
6419 CGGeneric("%s.SetNull();\n" % unionArgumentObj),
6420 templateBody,
6423 typeName = CGUnionStruct.unionTypeDecl(type, isOwningUnion)
6424 argumentTypeName = typeName + "Argument"
6425 if nullable:
6426 typeName = "Nullable<" + typeName + " >"
6428 declType = CGGeneric(typeName)
6429 if isOwningUnion:
6430 holderType = None
6431 else:
6432 holderType = CGGeneric(argumentTypeName)
6433 if nullable:
6434 holderType = CGTemplatedType("Maybe", holderType)
6436 # If we're isOptional and not nullable the normal optional handling will
6437 # handle lazy construction of our holder. If we're nullable and not
6438 # owning we do it all by hand because we do not want our holder
6439 # constructed if we're null. But if we're owning we don't have a
6440 # holder anyway, so we can do the normal Optional codepath.
6441 declLoc = "${declName}"
6442 constructDecl = None
6443 if nullable:
6444 if isOptional and not isOwningUnion:
6445 declType = CGTemplatedType("Optional", declType)
6446 constructDecl = CGGeneric("${declName}.Construct();\n")
6447 declLoc = "${declName}.Value()"
6449 if not isMember and isCallbackReturnValue:
6450 declType = CGWrapper(declType, post="&")
6451 declArgs = "aRetVal"
6452 else:
6453 declArgs = None
6455 if (
6456 defaultValue
6457 and not isinstance(defaultValue, IDLNullValue)
6458 and not isinstance(defaultValue, IDLDefaultDictionaryValue)
6460 tag = defaultValue.type.tag()
6462 if tag in numericSuffixes or tag is IDLType.Tags.bool:
6463 defaultStr = getHandleDefault(defaultValue)
6464 # Make sure we actually construct the thing inside the nullable.
6465 value = declLoc + (".SetValue()" if nullable else "")
6466 name = getUnionMemberName(defaultValue.type)
6467 default = CGGeneric(
6468 "%s.RawSetAs%s() = %s;\n" % (value, name, defaultStr)
6470 elif isinstance(defaultValue, IDLEmptySequenceValue):
6471 name = getUnionMemberName(defaultValue.type)
6472 # Make sure we actually construct the thing inside the nullable.
6473 value = declLoc + (".SetValue()" if nullable else "")
6474 if not isOwningUnion and typeNeedsRooting(defaultValue.type):
6475 ctorArgs = "cx"
6476 else:
6477 ctorArgs = ""
6478 # It's enough to set us to the right type; that will
6479 # create an empty array, which is all we need here.
6480 default = CGGeneric(
6481 "Unused << %s.RawSetAs%s(%s);\n" % (value, name, ctorArgs)
6483 elif defaultValue.type.isEnum():
6484 name = getUnionMemberName(defaultValue.type)
6485 # Make sure we actually construct the thing inside the nullable.
6486 value = declLoc + (".SetValue()" if nullable else "")
6487 default = CGGeneric(
6488 "%s.RawSetAs%s() = %s::%s;\n"
6490 value,
6491 name,
6492 defaultValue.type.inner.identifier.name,
6493 getEnumValueName(defaultValue.value),
6496 else:
6497 default = CGGeneric(
6498 handleDefaultStringValue(
6499 defaultValue, "%s.SetStringLiteral" % unionArgumentObj
6503 templateBody = CGIfElseWrapper("!(${haveValue})", default, templateBody)
6505 if nullable:
6506 assert not type.hasNullableType
6507 if defaultValue:
6508 if isinstance(defaultValue, IDLNullValue):
6509 extraConditionForNull = "!(${haveValue}) || "
6510 else:
6511 extraConditionForNull = "(${haveValue}) && "
6512 else:
6513 extraConditionForNull = ""
6515 hasUndefinedType = any(t.isUndefined() for t in type.flatMemberTypes)
6516 assert not hasUndefinedType or defaultValue is None
6518 nullTest = (
6519 "${val}.isNull()" if hasUndefinedType else "${val}.isNullOrUndefined()"
6521 templateBody = CGIfElseWrapper(
6522 extraConditionForNull + nullTest,
6523 CGGeneric("%s.SetNull();\n" % declLoc),
6524 templateBody,
6526 elif (
6527 not type.hasNullableType
6528 and defaultValue
6529 and isinstance(defaultValue, IDLDefaultDictionaryValue)
6531 assert type.hasDictionaryType()
6532 assert defaultValue.type.isDictionary()
6533 if not isOwningUnion and typeNeedsRooting(defaultValue.type):
6534 ctorArgs = "cx"
6535 else:
6536 ctorArgs = ""
6537 initDictionaryWithNull = CGIfWrapper(
6538 CGGeneric("return false;\n"),
6540 '!%s.RawSetAs%s(%s).Init(cx, JS::NullHandleValue, "Member of %s")'
6542 declLoc,
6543 getUnionMemberName(defaultValue.type),
6544 ctorArgs,
6545 type.prettyName(),
6549 templateBody = CGIfElseWrapper(
6550 "!(${haveValue})", initDictionaryWithNull, templateBody
6553 templateBody = CGList([constructDecl, templateBody])
6555 return JSToNativeConversionInfo(
6556 templateBody.define(),
6557 declType=declType,
6558 declArgs=declArgs,
6559 dealWithOptional=isOptional and (not nullable or isOwningUnion),
6562 if type.isPromise():
6563 assert not type.nullable()
6564 assert defaultValue is None
6566 # We always have to hold a strong ref to Promise here, because
6567 # Promise::resolve returns an addrefed thing.
6568 argIsPointer = isCallbackReturnValue
6569 if argIsPointer:
6570 declType = CGGeneric("RefPtr<Promise>")
6571 else:
6572 declType = CGGeneric("OwningNonNull<Promise>")
6574 # Per spec, what we're supposed to do is take the original
6575 # Promise.resolve and call it with the original Promise as this
6576 # value to make a Promise out of whatever value we actually have
6577 # here. The question is which global we should use. There are
6578 # several cases to consider:
6580 # 1) Normal call to API with a Promise argument. This is a case the
6581 # spec covers, and we should be using the current Realm's
6582 # Promise. That means the current compartment.
6583 # 2) Call to API with a Promise argument over Xrays. In practice,
6584 # this sort of thing seems to be used for giving an API
6585 # implementation a way to wait for conclusion of an asyc
6586 # operation, _not_ to expose the Promise to content code. So we
6587 # probably want to allow callers to use such an API in a
6588 # "natural" way, by passing chrome-side promises; indeed, that
6589 # may be all that the caller has to represent their async
6590 # operation. That means we really need to do the
6591 # Promise.resolve() in the caller (chrome) compartment: if we do
6592 # it in the content compartment, we will try to call .then() on
6593 # the chrome promise while in the content compartment, which will
6594 # throw and we'll just get a rejected Promise. Note that this is
6595 # also the reason why a caller who has a chrome Promise
6596 # representing an async operation can't itself convert it to a
6597 # content-side Promise (at least not without some serious
6598 # gyrations).
6599 # 3) Promise return value from a callback or callback interface.
6600 # Per spec, this should use the Realm of the callback object. In
6601 # our case, that's the compartment of the underlying callback,
6602 # not the current compartment (which may be the compartment of
6603 # some cross-compartment wrapper around said callback).
6604 # 4) Return value from a JS-implemented interface. In this case we
6605 # have a problem. Our current compartment is the compartment of
6606 # the JS implementation. But if the JS implementation returned
6607 # a page-side Promise (which is a totally sane thing to do, and
6608 # in fact the right thing to do given that this return value is
6609 # going right to content script) then we don't want to
6610 # Promise.resolve with our current compartment Promise, because
6611 # that will wrap it up in a chrome-side Promise, which is
6612 # decidedly _not_ what's desired here. So in that case we
6613 # should really unwrap the return value and use the global of
6614 # the result. CheckedUnwrapStatic should be good enough for that;
6615 # if it fails, then we're failing unwrap while in a
6616 # system-privileged compartment, so presumably we have a dead
6617 # object wrapper. Just error out. Do NOT fall back to using
6618 # the current compartment instead: that will return a
6619 # system-privileged rejected (because getting .then inside
6620 # resolve() failed) Promise to the caller, which they won't be
6621 # able to touch. That's not helpful. If we error out, on the
6622 # other hand, they will get a content-side rejected promise.
6623 # Same thing if the value returned is not even an object.
6624 if isCallbackReturnValue == "JSImpl":
6625 # Case 4 above. Note that globalObj defaults to the current
6626 # compartment global. Note that we don't use $*{exceptionCode}
6627 # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED)
6628 # which we don't really want here.
6629 assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n"
6630 getPromiseGlobal = fill(
6632 if (!$${val}.isObject()) {
6633 aRv.ThrowTypeError<MSG_NOT_OBJECT>("${sourceDescription}");
6634 return nullptr;
6636 JSObject* unwrappedVal = js::CheckedUnwrapStatic(&$${val}.toObject());
6637 if (!unwrappedVal) {
6638 // A slight lie, but not much of one, for a dead object wrapper.
6639 aRv.ThrowTypeError<MSG_NOT_OBJECT>("${sourceDescription}");
6640 return nullptr;
6642 globalObj = JS::GetNonCCWObjectGlobal(unwrappedVal);
6643 """,
6644 sourceDescription=sourceDescription,
6646 elif isCallbackReturnValue == "Callback":
6647 getPromiseGlobal = dedent(
6649 // We basically want our entry global here. Play it safe
6650 // and use GetEntryGlobal() to get it, with whatever
6651 // principal-clamping it ends up doing.
6652 globalObj = GetEntryGlobal()->GetGlobalJSObject();
6655 else:
6656 getPromiseGlobal = dedent(
6658 globalObj = JS::CurrentGlobalOrNull(cx);
6662 templateBody = fill(
6664 { // Scope for our GlobalObject, FastErrorResult, JSAutoRealm,
6665 // etc.
6667 JS::Rooted<JSObject*> globalObj(cx);
6668 $*{getPromiseGlobal}
6669 JSAutoRealm ar(cx, globalObj);
6670 GlobalObject promiseGlobal(cx, globalObj);
6671 if (promiseGlobal.Failed()) {
6672 $*{exceptionCode}
6675 JS::Rooted<JS::Value> valueToResolve(cx, $${val});
6676 if (!JS_WrapValue(cx, &valueToResolve)) {
6677 $*{exceptionCode}
6679 binding_detail::FastErrorResult promiseRv;
6680 nsCOMPtr<nsIGlobalObject> global =
6681 do_QueryInterface(promiseGlobal.GetAsSupports());
6682 if (!global) {
6683 promiseRv.Throw(NS_ERROR_UNEXPECTED);
6684 MOZ_ALWAYS_TRUE(promiseRv.MaybeSetPendingException(cx));
6685 $*{exceptionCode}
6687 $${declName} = Promise::Resolve(global, cx, valueToResolve,
6688 promiseRv);
6689 if (promiseRv.MaybeSetPendingException(cx)) {
6690 $*{exceptionCode}
6693 """,
6694 getPromiseGlobal=getPromiseGlobal,
6695 exceptionCode=exceptionCode,
6698 return JSToNativeConversionInfo(
6699 templateBody, declType=declType, dealWithOptional=isOptional
6702 if type.isGeckoInterface():
6703 assert not isEnforceRange and not isClamp and not isAllowShared
6705 descriptor = descriptorProvider.getDescriptor(
6706 type.unroll().inner.identifier.name
6709 assert descriptor.nativeType != "JSObject"
6711 if descriptor.interface.isCallback():
6712 (declType, declArgs, conversion) = getCallbackConversionInfo(
6713 type, descriptor.interface, isMember, isCallbackReturnValue, isOptional
6715 template = wrapObjectTemplate(
6716 conversion, type, "${declName} = nullptr;\n", failureCode
6718 return JSToNativeConversionInfo(
6719 template,
6720 declType=declType,
6721 declArgs=declArgs,
6722 dealWithOptional=isOptional,
6725 if descriptor.interface.identifier.name == "WindowProxy":
6726 declType = CGGeneric("mozilla::dom::WindowProxyHolder")
6727 if type.nullable():
6728 declType = CGTemplatedType("Nullable", declType)
6729 windowProxyHolderRef = "${declName}.SetValue()"
6730 else:
6731 windowProxyHolderRef = "${declName}"
6733 failureCode = onFailureBadType(
6734 failureCode, descriptor.interface.identifier.name
6735 ).define()
6736 templateBody = fill(
6738 JS::Rooted<JSObject*> source(cx, &$${val}.toObject());
6739 if (NS_FAILED(UnwrapWindowProxyArg(cx, source, ${windowProxyHolderRef}))) {
6740 $*{onFailure}
6742 """,
6743 windowProxyHolderRef=windowProxyHolderRef,
6744 onFailure=failureCode,
6746 templateBody = wrapObjectTemplate(
6747 templateBody, type, "${declName}.SetNull();\n", failureCode
6749 return JSToNativeConversionInfo(
6750 templateBody, declType=declType, dealWithOptional=isOptional
6753 # This is an interface that we implement as a concrete class
6754 # or an XPCOM interface.
6756 # Allow null pointers for nullable types and old-binding classes, and
6757 # use an RefPtr or raw pointer for callback return values to make
6758 # them easier to return.
6759 argIsPointer = (
6760 type.nullable() or type.unroll().inner.isExternal() or isCallbackReturnValue
6763 # Sequence and dictionary members, as well as owning unions (which can
6764 # appear here as return values in JS-implemented interfaces) have to
6765 # hold a strong ref to the thing being passed down. Those all set
6766 # isMember.
6768 # Also, callback return values always end up addrefing anyway, so there
6769 # is no point trying to avoid it here and it makes other things simpler
6770 # since we can assume the return value is a strong ref.
6771 assert not descriptor.interface.isCallback()
6772 forceOwningType = (isMember and isMember != "Union") or isCallbackReturnValue
6774 typeName = descriptor.nativeType
6775 typePtr = typeName + "*"
6777 # Compute a few things:
6778 # - declType is the type we want to return as the first element of our
6779 # tuple.
6780 # - holderType is the type we want to return as the third element
6781 # of our tuple.
6783 # Set up some sensible defaults for these things insofar as we can.
6784 holderType = None
6785 if argIsPointer:
6786 if forceOwningType:
6787 declType = "RefPtr<" + typeName + ">"
6788 else:
6789 declType = typePtr
6790 else:
6791 if forceOwningType:
6792 declType = "OwningNonNull<" + typeName + ">"
6793 else:
6794 declType = "NonNull<" + typeName + ">"
6796 templateBody = ""
6797 if forceOwningType:
6798 templateBody += fill(
6800 static_assert(IsRefcounted<${typeName}>::value, "We can only store refcounted classes.");
6801 """,
6802 typeName=typeName,
6805 if not descriptor.interface.isExternal():
6806 if failureCode is not None:
6807 templateBody += str(
6808 CastableObjectUnwrapper(
6809 descriptor,
6810 "${val}",
6811 "${maybeMutableVal}",
6812 "${declName}",
6813 failureCode,
6816 else:
6817 templateBody += str(
6818 FailureFatalCastableObjectUnwrapper(
6819 descriptor,
6820 "${val}",
6821 "${maybeMutableVal}",
6822 "${declName}",
6823 exceptionCode,
6824 isCallbackReturnValue,
6825 firstCap(sourceDescription),
6828 else:
6829 # External interface. We always have a holder for these, because we
6830 # don't actually know whether we have to addref when unwrapping or not.
6831 # So we just pass an getter_AddRefs(RefPtr) to XPConnect and if we'll
6832 # need a release it'll put a non-null pointer in there.
6833 if forceOwningType:
6834 # Don't return a holderType in this case; our declName
6835 # will just own stuff.
6836 templateBody += "RefPtr<" + typeName + "> ${holderName};\n"
6837 else:
6838 holderType = "RefPtr<" + typeName + ">"
6839 templateBody += (
6840 "JS::Rooted<JSObject*> source(cx, &${val}.toObject());\n"
6841 + "if (NS_FAILED(UnwrapArg<"
6842 + typeName
6843 + ">(cx, source, getter_AddRefs(${holderName})))) {\n"
6845 templateBody += CGIndenter(
6846 onFailureBadType(failureCode, descriptor.interface.identifier.name)
6847 ).define()
6848 templateBody += "}\n" "MOZ_ASSERT(${holderName});\n"
6850 # And store our value in ${declName}
6851 templateBody += "${declName} = ${holderName};\n"
6853 # Just pass failureCode, not onFailureBadType, here, so we'll report
6854 # the thing as not an object as opposed to not implementing whatever
6855 # our interface is.
6856 templateBody = wrapObjectTemplate(
6857 templateBody, type, "${declName} = nullptr;\n", failureCode
6860 declType = CGGeneric(declType)
6861 if holderType is not None:
6862 holderType = CGGeneric(holderType)
6863 return JSToNativeConversionInfo(
6864 templateBody,
6865 declType=declType,
6866 holderType=holderType,
6867 dealWithOptional=isOptional,
6870 if type.isSpiderMonkeyInterface():
6871 assert not isEnforceRange and not isClamp
6872 name = type.unroll().name # unroll() because it may be nullable
6873 interfaceType = CGGeneric(name)
6874 declType = interfaceType
6875 if type.nullable():
6876 declType = CGTemplatedType("Nullable", declType)
6877 objRef = "${declName}.SetValue()"
6878 else:
6879 objRef = "${declName}"
6881 # Again, this is a bit strange since we are actually building a
6882 # template string here. ${objRef} and $*{badType} below are filled in
6883 # right now; $${val} expands to ${val}, to be filled in later.
6884 template = fill(
6886 if (!${objRef}.Init(&$${val}.toObject())) {
6887 $*{badType}
6889 """,
6890 objRef=objRef,
6891 badType=onFailureBadType(failureCode, type.name).define(),
6893 if type.isBufferSource():
6894 if type.isArrayBuffer():
6895 isSharedMethod = "JS::IsSharedArrayBufferObject"
6896 isLargeMethod = "JS::IsLargeArrayBufferMaybeShared"
6897 isResizableMethod = "JS::IsResizableArrayBufferMaybeShared"
6898 else:
6899 assert type.isArrayBufferView() or type.isTypedArray()
6900 isSharedMethod = "JS::IsArrayBufferViewShared"
6901 isLargeMethod = "JS::IsLargeArrayBufferView"
6902 isResizableMethod = "JS::IsResizableArrayBufferView"
6903 if not isAllowShared:
6904 template += fill(
6906 if (${isSharedMethod}(${objRef}.Obj())) {
6907 $*{badType}
6909 """,
6910 isSharedMethod=isSharedMethod,
6911 objRef=objRef,
6912 badType=onFailureIsShared().define(),
6914 # For now reject large (> 2 GB) ArrayBuffers and ArrayBufferViews.
6915 # Supporting this will require changing dom::TypedArray and
6916 # consumers.
6917 template += fill(
6919 if (${isLargeMethod}(${objRef}.Obj())) {
6920 $*{badType}
6922 """,
6923 isLargeMethod=isLargeMethod,
6924 objRef=objRef,
6925 badType=onFailureIsLarge().define(),
6927 # For now reject resizable ArrayBuffers and growable
6928 # SharedArrayBuffers. Supporting this will require changing
6929 # dom::TypedArray and consumers.
6930 template += fill(
6932 if (${isResizableMethod}(${objRef}.Obj())) {
6933 $*{badType}
6935 """,
6936 isResizableMethod=isResizableMethod,
6937 objRef=objRef,
6938 badType=onFailureIsResizable().define(),
6940 template = wrapObjectTemplate(
6941 template, type, "${declName}.SetNull();\n", failureCode
6943 if not isMember or isMember == "Union":
6944 # This is a bit annoying. In a union we don't want to have a
6945 # holder, since unions don't support that. But if we're optional we
6946 # want to have a holder, so that the callee doesn't see
6947 # Optional<RootedSpiderMonkeyInterface<InterfaceType>>. So do a
6948 # holder if we're optional and use a RootedSpiderMonkeyInterface
6949 # otherwise.
6950 if isOptional:
6951 holderType = CGTemplatedType(
6952 "SpiderMonkeyInterfaceRooter", interfaceType
6954 # If our SpiderMonkey interface is nullable, this will set the
6955 # Nullable to be not-null, but that's ok because we make an
6956 # explicit SetNull() call on it as needed if our JS value is
6957 # actually null. XXXbz Because "Maybe" takes const refs for
6958 # constructor arguments, we can't pass a reference here; have
6959 # to pass a pointer.
6960 holderArgs = "cx, &%s" % objRef
6961 declArgs = None
6962 else:
6963 holderType = None
6964 holderArgs = None
6965 declType = CGTemplatedType("RootedSpiderMonkeyInterface", declType)
6966 declArgs = "cx"
6967 else:
6968 holderType = None
6969 holderArgs = None
6970 declArgs = None
6971 return JSToNativeConversionInfo(
6972 template,
6973 declType=declType,
6974 holderType=holderType,
6975 dealWithOptional=isOptional,
6976 declArgs=declArgs,
6977 holderArgs=holderArgs,
6980 if type.isJSString():
6981 assert not isEnforceRange and not isClamp and not isAllowShared
6982 if type.nullable():
6983 raise TypeError("Nullable JSString not supported")
6985 declArgs = "cx"
6986 if isMember:
6987 raise TypeError("JSString not supported as member")
6988 else:
6989 declType = "JS::Rooted<JSString*>"
6991 if isOptional:
6992 raise TypeError("JSString not supported as optional")
6993 templateBody = fill(
6995 if (!($${declName} = ConvertJSValueToJSString(cx, $${val}))) {
6996 $*{exceptionCode}
6998 """,
6999 exceptionCode=exceptionCode,
7002 if defaultValue is not None:
7003 assert not isinstance(defaultValue, IDLNullValue)
7004 defaultCode = fill(
7006 static const char data[] = { ${data} };
7007 $${declName} = JS_NewStringCopyN(cx, data, std::size(data) - 1);
7008 if (!$${declName}) {
7009 $*{exceptionCode}
7011 """,
7012 data=", ".join(
7013 ["'" + char + "'" for char in defaultValue.value] + ["0"]
7015 exceptionCode=exceptionCode,
7018 templateBody = handleDefault(templateBody, defaultCode)
7019 return JSToNativeConversionInfo(
7020 templateBody, declType=CGGeneric(declType), declArgs=declArgs
7023 if type.isDOMString() or type.isUSVString() or type.isUTF8String():
7024 assert not isEnforceRange and not isClamp and not isAllowShared
7026 treatAs = {
7027 "Default": "eStringify",
7028 "EmptyString": "eEmpty",
7029 "Null": "eNull",
7031 if type.nullable():
7032 # For nullable strings null becomes a null string.
7033 treatNullAs = "Null"
7034 # For nullable strings undefined also becomes a null string.
7035 undefinedBehavior = "eNull"
7036 else:
7037 undefinedBehavior = "eStringify"
7038 if type.legacyNullToEmptyString:
7039 treatNullAs = "EmptyString"
7040 else:
7041 treatNullAs = "Default"
7042 nullBehavior = treatAs[treatNullAs]
7044 def getConversionCode(varName):
7045 normalizeCode = ""
7046 if type.isUSVString():
7047 normalizeCode = fill(
7049 if (!NormalizeUSVString(${var})) {
7050 JS_ReportOutOfMemory(cx);
7051 $*{exceptionCode}
7053 """,
7054 var=varName,
7055 exceptionCode=exceptionCode,
7058 conversionCode = fill(
7060 if (!ConvertJSValueToString(cx, $${val}, ${nullBehavior}, ${undefinedBehavior}, ${varName})) {
7061 $*{exceptionCode}
7063 $*{normalizeCode}
7064 """,
7065 nullBehavior=nullBehavior,
7066 undefinedBehavior=undefinedBehavior,
7067 varName=varName,
7068 exceptionCode=exceptionCode,
7069 normalizeCode=normalizeCode,
7072 if defaultValue is None:
7073 return conversionCode
7075 if isinstance(defaultValue, IDLNullValue):
7076 assert type.nullable()
7077 defaultCode = "%s.SetIsVoid(true);\n" % varName
7078 else:
7079 defaultCode = handleDefaultStringValue(
7080 defaultValue, "%s.AssignLiteral" % varName
7082 return handleDefault(conversionCode, defaultCode)
7084 if isMember and isMember != "Union":
7085 # Convert directly into the ns[C]String member we have.
7086 if type.isUTF8String():
7087 declType = "nsCString"
7088 else:
7089 declType = "nsString"
7090 return JSToNativeConversionInfo(
7091 getConversionCode("${declName}"),
7092 declType=CGGeneric(declType),
7093 dealWithOptional=isOptional,
7096 if isOptional:
7097 if type.isUTF8String():
7098 declType = "Optional<nsACString>"
7099 holderType = CGGeneric("binding_detail::FakeString<char>")
7100 else:
7101 declType = "Optional<nsAString>"
7102 holderType = CGGeneric("binding_detail::FakeString<char16_t>")
7103 conversionCode = "%s" "${declName} = &${holderName};\n" % getConversionCode(
7104 "${holderName}"
7106 else:
7107 if type.isUTF8String():
7108 declType = "binding_detail::FakeString<char>"
7109 else:
7110 declType = "binding_detail::FakeString<char16_t>"
7111 holderType = None
7112 conversionCode = getConversionCode("${declName}")
7114 # No need to deal with optional here; we handled it already
7115 return JSToNativeConversionInfo(
7116 conversionCode, declType=CGGeneric(declType), holderType=holderType
7119 if type.isByteString():
7120 assert not isEnforceRange and not isClamp and not isAllowShared
7122 nullable = toStringBool(type.nullable())
7124 conversionCode = fill(
7126 if (!ConvertJSValueToByteString(cx, $${val}, ${nullable}, "${sourceDescription}", $${declName})) {
7127 $*{exceptionCode}
7129 """,
7130 nullable=nullable,
7131 sourceDescription=sourceDescription,
7132 exceptionCode=exceptionCode,
7135 if defaultValue is not None:
7136 if isinstance(defaultValue, IDLNullValue):
7137 assert type.nullable()
7138 defaultCode = "${declName}.SetIsVoid(true);\n"
7139 else:
7140 defaultCode = handleDefaultStringValue(
7141 defaultValue, "${declName}.AssignLiteral"
7143 conversionCode = handleDefault(conversionCode, defaultCode)
7145 return JSToNativeConversionInfo(
7146 conversionCode, declType=CGGeneric("nsCString"), dealWithOptional=isOptional
7149 if type.isEnum():
7150 assert not isEnforceRange and not isClamp and not isAllowShared
7152 enumName = type.unroll().inner.identifier.name
7153 declType = CGGeneric(enumName)
7154 if type.nullable():
7155 declType = CGTemplatedType("Nullable", declType)
7156 declType = declType.define()
7157 enumLoc = "${declName}.SetValue()"
7158 else:
7159 enumLoc = "${declName}"
7160 declType = declType.define()
7162 if invalidEnumValueFatal:
7163 handleInvalidEnumValueCode = "MOZ_ASSERT(index >= 0);\n"
7164 else:
7165 # invalidEnumValueFatal is false only for attributes. So we won't
7166 # have a non-default exceptionCode here unless attribute "arg
7167 # conversion" code starts passing in an exceptionCode. At which
7168 # point we'll need to figure out what that even means.
7169 assert exceptionCode == "return false;\n"
7170 handleInvalidEnumValueCode = dedent(
7172 if (index < 0) {
7173 return true;
7178 template = fill(
7181 int index;
7182 if (!binding_detail::FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val},
7183 binding_detail::EnumStrings<${enumtype}>::Values,
7184 "${enumtype}", "${sourceDescription}",
7185 &index)) {
7186 $*{exceptionCode}
7188 $*{handleInvalidEnumValueCode}
7189 ${enumLoc} = static_cast<${enumtype}>(index);
7191 """,
7192 enumtype=enumName,
7193 invalidEnumValueFatal=toStringBool(invalidEnumValueFatal),
7194 handleInvalidEnumValueCode=handleInvalidEnumValueCode,
7195 exceptionCode=exceptionCode,
7196 enumLoc=enumLoc,
7197 sourceDescription=sourceDescription,
7200 setNull = "${declName}.SetNull();\n"
7202 if type.nullable():
7203 template = CGIfElseWrapper(
7204 "${val}.isNullOrUndefined()", CGGeneric(setNull), CGGeneric(template)
7205 ).define()
7207 if defaultValue is not None:
7208 if isinstance(defaultValue, IDLNullValue):
7209 assert type.nullable()
7210 template = handleDefault(template, setNull)
7211 else:
7212 assert defaultValue.type.tag() == IDLType.Tags.domstring
7213 template = handleDefault(
7214 template,
7216 "%s = %s::%s;\n"
7217 % (enumLoc, enumName, getEnumValueName(defaultValue.value))
7220 return JSToNativeConversionInfo(
7221 template, declType=CGGeneric(declType), dealWithOptional=isOptional
7224 if type.isCallback():
7225 assert not isEnforceRange and not isClamp and not isAllowShared
7226 assert not type.treatNonCallableAsNull() or type.nullable()
7227 assert not type.treatNonObjectAsNull() or type.nullable()
7228 assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
7230 callback = type.unroll().callback
7231 name = callback.identifier.name
7232 (declType, declArgs, conversion) = getCallbackConversionInfo(
7233 type, callback, isMember, isCallbackReturnValue, isOptional
7236 if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
7237 haveCallable = "JS::IsCallable(&${val}.toObject())"
7238 if not isDefinitelyObject:
7239 haveCallable = "${val}.isObject() && " + haveCallable
7240 if defaultValue is not None:
7241 assert isinstance(defaultValue, IDLNullValue)
7242 haveCallable = "(${haveValue}) && " + haveCallable
7243 template = (
7244 ("if (%s) {\n" % haveCallable) + conversion + "} else {\n"
7245 " ${declName} = nullptr;\n"
7246 "}\n"
7248 elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull():
7249 if not isDefinitelyObject:
7250 haveObject = "${val}.isObject()"
7251 if defaultValue is not None:
7252 assert isinstance(defaultValue, IDLNullValue)
7253 haveObject = "(${haveValue}) && " + haveObject
7254 template = CGIfElseWrapper(
7255 haveObject,
7256 CGGeneric(conversion),
7257 CGGeneric("${declName} = nullptr;\n"),
7258 ).define()
7259 else:
7260 template = conversion
7261 else:
7262 template = wrapObjectTemplate(
7263 "if (JS::IsCallable(&${val}.toObject())) {\n"
7264 + conversion
7265 + "} else {\n"
7266 + indent(onFailureNotCallable(failureCode).define())
7267 + "}\n",
7268 type,
7269 "${declName} = nullptr;\n",
7270 failureCode,
7272 return JSToNativeConversionInfo(
7273 template, declType=declType, declArgs=declArgs, dealWithOptional=isOptional
7276 if type.isAny():
7277 assert not isEnforceRange and not isClamp and not isAllowShared
7279 declArgs = None
7280 if isMember in ("Variadic", "Sequence", "Dictionary", "Record"):
7281 # Rooting is handled by the sequence and dictionary tracers.
7282 declType = "JS::Value"
7283 else:
7284 assert not isMember
7285 declType = "JS::Rooted<JS::Value>"
7286 declArgs = "cx"
7288 assert not isOptional
7289 templateBody = "${declName} = ${val};\n"
7291 # For JS-implemented APIs, we refuse to allow passing objects that the
7292 # API consumer does not subsume. The extra parens around
7293 # ($${passedToJSImpl}) suppress unreachable code warnings when
7294 # $${passedToJSImpl} is the literal `false`. But Apple is shipping a
7295 # buggy clang (clang 3.9) in Xcode 8.3, so there even the parens are not
7296 # enough. So we manually disable some warnings in clang.
7297 if (
7298 not isinstance(descriptorProvider, Descriptor)
7299 or descriptorProvider.interface.isJSImplemented()
7301 templateBody = (
7302 fill(
7304 #ifdef __clang__
7305 #pragma clang diagnostic push
7306 #pragma clang diagnostic ignored "-Wunreachable-code"
7307 #pragma clang diagnostic ignored "-Wunreachable-code-return"
7308 #endif // __clang__
7309 if (($${passedToJSImpl}) && !CallerSubsumes($${val})) {
7310 cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}");
7311 $*{exceptionCode}
7313 #ifdef __clang__
7314 #pragma clang diagnostic pop
7315 #endif // __clang__
7316 """,
7317 sourceDescription=sourceDescription,
7318 exceptionCode=exceptionCode,
7320 + templateBody
7323 # We may not have a default value if we're being converted for
7324 # a setter, say.
7325 if defaultValue:
7326 if isinstance(defaultValue, IDLNullValue):
7327 defaultHandling = "${declName} = JS::NullValue();\n"
7328 else:
7329 assert isinstance(defaultValue, IDLUndefinedValue)
7330 defaultHandling = "${declName} = JS::UndefinedValue();\n"
7331 templateBody = handleDefault(templateBody, defaultHandling)
7332 return JSToNativeConversionInfo(
7333 templateBody, declType=CGGeneric(declType), declArgs=declArgs
7336 if type.isObject():
7337 assert not isEnforceRange and not isClamp and not isAllowShared
7338 return handleJSObjectType(
7339 type, isMember, failureCode, exceptionCode, sourceDescription
7342 if type.isDictionary():
7343 # There are no nullable dictionary-typed arguments or dictionary-typed
7344 # dictionary members.
7345 assert (
7346 not type.nullable()
7347 or isCallbackReturnValue
7348 or (isMember and isMember != "Dictionary")
7350 # All optional dictionary-typed arguments always have default values,
7351 # but dictionary-typed dictionary members can be optional.
7352 assert not isOptional or isMember == "Dictionary"
7353 # In the callback return value case we never have to worry
7354 # about a default value; we always have a value.
7355 assert not isCallbackReturnValue or defaultValue is None
7357 typeName = CGDictionary.makeDictionaryName(type.unroll().inner)
7358 if (not isMember or isMember == "Union") and not isCallbackReturnValue:
7359 # Since we're not a member and not nullable or optional, no one will
7360 # see our real type, so we can do the fast version of the dictionary
7361 # that doesn't pre-initialize members.
7362 typeName = "binding_detail::Fast" + typeName
7364 declType = CGGeneric(typeName)
7366 # We do manual default value handling here, because we actually do want
7367 # a jsval, and we only handle the default-dictionary case (which we map
7368 # into initialization with the JS value `null`) anyway
7369 # NOTE: if isNullOrUndefined or isDefinitelyObject are true,
7370 # we know we have a value, so we don't have to worry about the
7371 # default value.
7372 if (
7373 not isNullOrUndefined
7374 and not isDefinitelyObject
7375 and defaultValue is not None
7377 assert isinstance(defaultValue, IDLDefaultDictionaryValue)
7378 # Initializing from JS null does the right thing to give
7379 # us a default-initialized dictionary.
7380 val = "(${haveValue}) ? ${val} : JS::NullHandleValue"
7381 else:
7382 val = "${val}"
7384 dictLoc = "${declName}"
7385 if type.nullable():
7386 dictLoc += ".SetValue()"
7388 if type.unroll().inner.needsConversionFromJS:
7389 args = "cx, %s, " % val
7390 else:
7391 # We can end up in this case if a dictionary that does not need
7392 # conversion from JS has a dictionary-typed member with a default
7393 # value of {}.
7394 args = ""
7395 conversionCode = fill(
7397 if (!${dictLoc}.Init(${args}"${desc}", $${passedToJSImpl})) {
7398 $*{exceptionCode}
7400 """,
7401 dictLoc=dictLoc,
7402 args=args,
7403 desc=firstCap(sourceDescription),
7404 exceptionCode=exceptionCode,
7407 if failureCode is not None:
7408 # This means we're part of an overload or union conversion, and
7409 # should simply skip stuff if our value is not convertible to
7410 # dictionary, instead of trying and throwing. If we're either
7411 # isDefinitelyObject or isNullOrUndefined then we're convertible to
7412 # dictionary and don't need to check here.
7413 if isDefinitelyObject or isNullOrUndefined:
7414 template = conversionCode
7415 else:
7416 template = fill(
7418 if (!IsConvertibleToDictionary(${val})) {
7419 $*{failureCode}
7421 $*{conversionCode}
7422 """,
7423 val=val,
7424 failureCode=failureCode,
7425 conversionCode=conversionCode,
7427 else:
7428 template = conversionCode
7430 if type.nullable():
7431 declType = CGTemplatedType("Nullable", declType)
7432 template = CGIfElseWrapper(
7433 "${val}.isNullOrUndefined()",
7434 CGGeneric("${declName}.SetNull();\n"),
7435 CGGeneric(template),
7436 ).define()
7438 # Dictionary arguments that might contain traceable things need to get
7439 # traced
7440 if (not isMember or isMember == "Union") and isCallbackReturnValue:
7441 # Go ahead and just convert directly into our actual return value
7442 declType = CGWrapper(declType, post="&")
7443 declArgs = "aRetVal"
7444 elif (not isMember or isMember == "Union") and typeNeedsRooting(type):
7445 declType = CGTemplatedType("RootedDictionary", declType)
7446 declArgs = "cx"
7447 else:
7448 declArgs = None
7450 return JSToNativeConversionInfo(
7451 template, declType=declType, declArgs=declArgs, dealWithOptional=isOptional
7454 if type.isUndefined():
7455 assert not isOptional
7456 # This one only happens for return values, and its easy: Just
7457 # ignore the jsval.
7458 return JSToNativeConversionInfo("")
7460 if not type.isPrimitive():
7461 raise TypeError("Need conversion for argument type '%s'" % str(type))
7463 typeName = builtinNames[type.tag()]
7465 conversionBehavior = "eDefault"
7466 if isEnforceRange:
7467 assert type.isInteger()
7468 conversionBehavior = "eEnforceRange"
7469 elif isClamp:
7470 assert type.isInteger()
7471 conversionBehavior = "eClamp"
7473 alwaysNull = False
7474 if type.nullable():
7475 declType = CGGeneric("Nullable<" + typeName + ">")
7476 writeLoc = "${declName}.SetValue()"
7477 readLoc = "${declName}.Value()"
7478 nullCondition = "${val}.isNullOrUndefined()"
7479 if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
7480 nullCondition = "!(${haveValue}) || " + nullCondition
7481 if isKnownMissing:
7482 alwaysNull = True
7483 template = dedent(
7485 ${declName}.SetNull();
7488 if not alwaysNull:
7489 template = fill(
7491 if (${nullCondition}) {
7492 $${declName}.SetNull();
7493 } else if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, "${sourceDescription}", &${writeLoc})) {
7494 $*{exceptionCode}
7496 """,
7497 nullCondition=nullCondition,
7498 typeName=typeName,
7499 conversionBehavior=conversionBehavior,
7500 sourceDescription=firstCap(sourceDescription),
7501 writeLoc=writeLoc,
7502 exceptionCode=exceptionCode,
7504 else:
7505 assert defaultValue is None or not isinstance(defaultValue, IDLNullValue)
7506 writeLoc = "${declName}"
7507 readLoc = writeLoc
7508 template = fill(
7510 if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, "${sourceDescription}", &${writeLoc})) {
7511 $*{exceptionCode}
7513 """,
7514 typeName=typeName,
7515 conversionBehavior=conversionBehavior,
7516 sourceDescription=firstCap(sourceDescription),
7517 writeLoc=writeLoc,
7518 exceptionCode=exceptionCode,
7520 declType = CGGeneric(typeName)
7522 if type.isFloat() and not type.isUnrestricted() and not alwaysNull:
7523 if lenientFloatCode is not None:
7524 nonFiniteCode = lenientFloatCode
7525 else:
7526 nonFiniteCode = 'cx.ThrowErrorMessage<MSG_NOT_FINITE>("%s");\n' "%s" % (
7527 firstCap(sourceDescription),
7528 exceptionCode,
7531 # We're appending to an if-block brace, so strip trailing whitespace
7532 # and add an extra space before the else.
7533 template = template.rstrip()
7534 template += fill(
7536 else if (!std::isfinite(${readLoc})) {
7537 $*{nonFiniteCode}
7539 """,
7540 readLoc=readLoc,
7541 nonFiniteCode=nonFiniteCode,
7544 if (
7545 defaultValue is not None
7547 # We already handled IDLNullValue, so just deal with the other ones
7548 not isinstance(defaultValue, IDLNullValue)
7550 tag = defaultValue.type.tag()
7551 defaultStr = getHandleDefault(defaultValue)
7552 template = handleDefault(template, "%s = %s;\n" % (writeLoc, defaultStr))
7554 return JSToNativeConversionInfo(
7555 template, declType=declType, dealWithOptional=isOptional
7559 def instantiateJSToNativeConversion(info, replacements, checkForValue=False):
7561 Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo
7562 and a set of replacements as required by the strings in such an object, and
7563 generate code to convert into stack C++ types.
7565 If checkForValue is True, then the conversion will get wrapped in
7566 a check for ${haveValue}.
7568 templateBody, declType, holderType, dealWithOptional = (
7569 info.template,
7570 info.declType,
7571 info.holderType,
7572 info.dealWithOptional,
7575 if dealWithOptional and not checkForValue:
7576 raise TypeError("Have to deal with optional things, but don't know how")
7577 if checkForValue and declType is None:
7578 raise TypeError(
7579 "Need to predeclare optional things, so they will be "
7580 "outside the check for big enough arg count!"
7583 # We can't precompute our holder constructor arguments, since
7584 # those might depend on ${declName}, which we change below. Just
7585 # compute arguments at the point when we need them as we go.
7586 def getArgsCGThing(args):
7587 return CGGeneric(string.Template(args).substitute(replacements))
7589 result = CGList([])
7590 # Make a copy of "replacements" since we may be about to start modifying it
7591 replacements = dict(replacements)
7592 originalDeclName = replacements["declName"]
7593 if declType is not None:
7594 if dealWithOptional:
7595 replacements["declName"] = "%s.Value()" % originalDeclName
7596 declType = CGTemplatedType("Optional", declType)
7597 declCtorArgs = None
7598 elif info.declArgs is not None:
7599 declCtorArgs = CGWrapper(getArgsCGThing(info.declArgs), pre="(", post=")")
7600 else:
7601 declCtorArgs = None
7602 result.append(
7603 CGList(
7605 declType,
7606 CGGeneric(" "),
7607 CGGeneric(originalDeclName),
7608 declCtorArgs,
7609 CGGeneric(";\n"),
7614 originalHolderName = replacements["holderName"]
7615 if holderType is not None:
7616 if dealWithOptional:
7617 replacements["holderName"] = "%s.ref()" % originalHolderName
7618 holderType = CGTemplatedType("Maybe", holderType)
7619 holderCtorArgs = None
7620 elif info.holderArgs is not None:
7621 holderCtorArgs = CGWrapper(
7622 getArgsCGThing(info.holderArgs), pre="(", post=")"
7624 else:
7625 holderCtorArgs = None
7626 result.append(
7627 CGList(
7629 holderType,
7630 CGGeneric(" "),
7631 CGGeneric(originalHolderName),
7632 holderCtorArgs,
7633 CGGeneric(";\n"),
7638 if "maybeMutableVal" not in replacements:
7639 replacements["maybeMutableVal"] = replacements["val"]
7641 conversion = CGGeneric(string.Template(templateBody).substitute(replacements))
7643 if checkForValue:
7644 if dealWithOptional:
7645 declConstruct = CGIndenter(
7646 CGGeneric(
7647 "%s.Construct(%s);\n"
7649 originalDeclName,
7650 getArgsCGThing(info.declArgs).define() if info.declArgs else "",
7654 if holderType is not None:
7655 holderConstruct = CGIndenter(
7656 CGGeneric(
7657 "%s.emplace(%s);\n"
7659 originalHolderName,
7661 getArgsCGThing(info.holderArgs).define()
7662 if info.holderArgs
7663 else ""
7668 else:
7669 holderConstruct = None
7670 else:
7671 declConstruct = None
7672 holderConstruct = None
7674 conversion = CGList(
7676 CGGeneric(
7677 string.Template("if (${haveValue}) {\n").substitute(replacements)
7679 declConstruct,
7680 holderConstruct,
7681 CGIndenter(conversion),
7682 CGGeneric("}\n"),
7686 result.append(conversion)
7687 return result
7690 def convertConstIDLValueToJSVal(value):
7691 if isinstance(value, IDLNullValue):
7692 return "JS::NullValue()"
7693 if isinstance(value, IDLUndefinedValue):
7694 return "JS::UndefinedValue()"
7695 tag = value.type.tag()
7696 if tag in [
7697 IDLType.Tags.int8,
7698 IDLType.Tags.uint8,
7699 IDLType.Tags.int16,
7700 IDLType.Tags.uint16,
7701 IDLType.Tags.int32,
7703 return "JS::Int32Value(%s)" % (value.value)
7704 if tag == IDLType.Tags.uint32:
7705 return "JS::NumberValue(%sU)" % (value.value)
7706 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
7707 return "JS::CanonicalizedDoubleValue(%s)" % numericValue(tag, value.value)
7708 if tag == IDLType.Tags.bool:
7709 return "JS::BooleanValue(%s)" % (toStringBool(value.value))
7710 if tag in [IDLType.Tags.float, IDLType.Tags.double]:
7711 return "JS::CanonicalizedDoubleValue(%s)" % (value.value)
7712 raise TypeError("Const value of unhandled type: %s" % value.type)
7715 class CGArgumentConverter(CGThing):
7717 A class that takes an IDL argument object and its index in the
7718 argument list and generates code to unwrap the argument to the
7719 right native type.
7721 argDescription is a description of the argument for error-reporting
7722 purposes. Callers should assume that it might get placed in the middle of a
7723 sentence. If it ends up at the beginning of a sentence, its first character
7724 will be automatically uppercased.
7727 def __init__(
7728 self,
7729 argument,
7730 index,
7731 descriptorProvider,
7732 argDescription,
7733 member,
7734 invalidEnumValueFatal=True,
7735 lenientFloatCode=None,
7737 CGThing.__init__(self)
7738 self.argument = argument
7739 self.argDescription = argDescription
7740 assert not argument.defaultValue or argument.optional
7742 replacer = {"index": index, "argc": "args.length()"}
7743 self.replacementVariables = {
7744 "declName": "arg%d" % index,
7745 "holderName": ("arg%d" % index) + "_holder",
7746 "obj": "obj",
7747 "passedToJSImpl": toStringBool(
7748 isJSImplementedDescriptor(descriptorProvider)
7751 # If we have a method generated by the maplike/setlike portion of an
7752 # interface, arguments can possibly be undefined, but will need to be
7753 # converted to the key/value type of the backing object. In this case,
7754 # use .get() instead of direct access to the argument. This won't
7755 # matter for iterable since generated functions for those interface
7756 # don't take arguments.
7757 if member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod():
7758 self.replacementVariables["val"] = string.Template(
7759 "args.get(${index})"
7760 ).substitute(replacer)
7761 self.replacementVariables["maybeMutableVal"] = string.Template(
7762 "args[${index}]"
7763 ).substitute(replacer)
7764 else:
7765 self.replacementVariables["val"] = string.Template(
7766 "args[${index}]"
7767 ).substitute(replacer)
7768 haveValueCheck = string.Template("args.hasDefined(${index})").substitute(
7769 replacer
7771 self.replacementVariables["haveValue"] = haveValueCheck
7772 self.descriptorProvider = descriptorProvider
7773 if self.argument.canHaveMissingValue():
7774 self.argcAndIndex = replacer
7775 else:
7776 self.argcAndIndex = None
7777 self.invalidEnumValueFatal = invalidEnumValueFatal
7778 self.lenientFloatCode = lenientFloatCode
7780 def define(self):
7781 typeConversion = getJSToNativeConversionInfo(
7782 self.argument.type,
7783 self.descriptorProvider,
7784 isOptional=(self.argcAndIndex is not None and not self.argument.variadic),
7785 invalidEnumValueFatal=self.invalidEnumValueFatal,
7786 defaultValue=self.argument.defaultValue,
7787 lenientFloatCode=self.lenientFloatCode,
7788 isMember="Variadic" if self.argument.variadic else False,
7789 allowTreatNonCallableAsNull=self.argument.allowTreatNonCallableAsNull(),
7790 sourceDescription=self.argDescription,
7793 if not self.argument.variadic:
7794 return instantiateJSToNativeConversion(
7795 typeConversion, self.replacementVariables, self.argcAndIndex is not None
7796 ).define()
7798 # Variadic arguments get turned into a sequence.
7799 if typeConversion.dealWithOptional:
7800 raise TypeError("Shouldn't have optional things in variadics")
7801 if typeConversion.holderType is not None:
7802 raise TypeError("Shouldn't need holders for variadics")
7804 replacer = dict(self.argcAndIndex, **self.replacementVariables)
7805 replacer["seqType"] = CGTemplatedType(
7806 "AutoSequence", typeConversion.declType
7807 ).define()
7808 if typeNeedsRooting(self.argument.type):
7809 rooterDecl = (
7810 "SequenceRooter<%s> ${holderName}(cx, &${declName});\n"
7811 % typeConversion.declType.define()
7813 else:
7814 rooterDecl = ""
7815 replacer["elemType"] = typeConversion.declType.define()
7817 replacer["elementInitializer"] = initializerForType(self.argument.type) or ""
7819 # NOTE: Keep this in sync with sequence conversions as needed
7820 variadicConversion = string.Template(
7821 "${seqType} ${declName};\n"
7822 + rooterDecl
7823 + dedent(
7825 if (${argc} > ${index}) {
7826 if (!${declName}.SetCapacity(${argc} - ${index}, mozilla::fallible)) {
7827 JS_ReportOutOfMemory(cx);
7828 return false;
7830 for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) {
7831 // OK to do infallible append here, since we ensured capacity already.
7832 ${elemType}& slot = *${declName}.AppendElement(${elementInitializer});
7835 ).substitute(replacer)
7837 val = string.Template("args[variadicArg]").substitute(replacer)
7838 variadicConversion += indent(
7839 string.Template(typeConversion.template).substitute(
7841 "val": val,
7842 "maybeMutableVal": val,
7843 "declName": "slot",
7844 # We only need holderName here to handle isExternal()
7845 # interfaces, which use an internal holder for the
7846 # conversion even when forceOwningType ends up true.
7847 "holderName": "tempHolder",
7848 # Use the same ${obj} as for the variadic arg itself
7849 "obj": replacer["obj"],
7850 "passedToJSImpl": toStringBool(
7851 isJSImplementedDescriptor(self.descriptorProvider)
7858 variadicConversion += " }\n" "}\n"
7859 return variadicConversion
7862 def getMaybeWrapValueFuncForType(type):
7863 if type.isJSString():
7864 return "MaybeWrapStringValue"
7865 # Callbacks might actually be DOM objects; nothing prevents a page from
7866 # doing that.
7867 if type.isCallback() or type.isCallbackInterface() or type.isObject():
7868 if type.nullable():
7869 return "MaybeWrapObjectOrNullValue"
7870 return "MaybeWrapObjectValue"
7871 # SpiderMonkey interfaces are never DOM objects. Neither are sequences or
7872 # dictionaries, since those are always plain JS objects.
7873 if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence():
7874 if type.nullable():
7875 return "MaybeWrapNonDOMObjectOrNullValue"
7876 return "MaybeWrapNonDOMObjectValue"
7877 if type.isAny():
7878 return "MaybeWrapValue"
7880 # For other types, just go ahead an fall back on MaybeWrapValue for now:
7881 # it's always safe to do, and shouldn't be particularly slow for any of
7882 # them
7883 return "MaybeWrapValue"
7886 sequenceWrapLevel = 0
7887 recordWrapLevel = 0
7890 def getWrapTemplateForType(
7891 type,
7892 descriptorProvider,
7893 result,
7894 successCode,
7895 returnsNewObject,
7896 exceptionCode,
7897 spiderMonkeyInterfacesAreStructs,
7898 isConstructorRetval=False,
7901 Reflect a C++ value stored in "result", of IDL type "type" into JS. The
7902 "successCode" is the code to run once we have successfully done the
7903 conversion and must guarantee that execution of the conversion template
7904 stops once the successCode has executed (e.g. by doing a 'return', or by
7905 doing a 'break' if the entire conversion template is inside a block that
7906 the 'break' will exit).
7908 If spiderMonkeyInterfacesAreStructs is true, then if the type is a
7909 SpiderMonkey interface, "result" is one of the
7910 dom::SpiderMonkeyInterfaceObjectStorage subclasses, not a JSObject*.
7912 The resulting string should be used with string.Template. It
7913 needs the following keys when substituting:
7915 jsvalHandle: something that can be passed to methods taking a
7916 JS::MutableHandle<JS::Value>. This can be a
7917 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
7918 jsvalRef: something that can have .address() called on it to get a
7919 JS::Value* and .set() called on it to set it to a JS::Value.
7920 This can be a JS::MutableHandle<JS::Value> or a
7921 JS::Rooted<JS::Value>.
7922 obj: a JS::Handle<JSObject*>.
7924 Returns (templateString, infallibility of conversion template)
7926 if successCode is None:
7927 successCode = "return true;\n"
7929 def setUndefined():
7930 return _setValue("", setter="setUndefined")
7932 def setNull():
7933 return _setValue("", setter="setNull")
7935 def setInt32(value):
7936 return _setValue(value, setter="setInt32")
7938 def setString(value):
7939 return _setValue(value, wrapAsType=type, setter="setString")
7941 def setObject(value, wrapAsType=None):
7942 return _setValue(value, wrapAsType=wrapAsType, setter="setObject")
7944 def setObjectOrNull(value, wrapAsType=None):
7945 return _setValue(value, wrapAsType=wrapAsType, setter="setObjectOrNull")
7947 def setUint32(value):
7948 return _setValue(value, setter="setNumber")
7950 def setDouble(value):
7951 return _setValue("JS_NumberValue(%s)" % value)
7953 def setBoolean(value):
7954 return _setValue(value, setter="setBoolean")
7956 def _setValue(value, wrapAsType=None, setter="set"):
7958 Returns the code to set the jsval to value.
7960 If wrapAsType is not None, then will wrap the resulting value using the
7961 function that getMaybeWrapValueFuncForType(wrapAsType) returns.
7962 Otherwise, no wrapping will be done.
7964 if wrapAsType is None:
7965 tail = successCode
7966 else:
7967 tail = fill(
7969 if (!${maybeWrap}(cx, $${jsvalHandle})) {
7970 $*{exceptionCode}
7972 $*{successCode}
7973 """,
7974 maybeWrap=getMaybeWrapValueFuncForType(wrapAsType),
7975 exceptionCode=exceptionCode,
7976 successCode=successCode,
7978 return ("${jsvalRef}.%s(%s);\n" % (setter, value)) + tail
7980 def wrapAndSetPtr(wrapCall, failureCode=None):
7982 Returns the code to set the jsval by calling "wrapCall". "failureCode"
7983 is the code to run if calling "wrapCall" fails
7985 if failureCode is None:
7986 failureCode = exceptionCode
7987 return fill(
7989 if (!${wrapCall}) {
7990 $*{failureCode}
7992 $*{successCode}
7993 """,
7994 wrapCall=wrapCall,
7995 failureCode=failureCode,
7996 successCode=successCode,
7999 if type is None or type.isUndefined():
8000 return (setUndefined(), True)
8002 if (type.isSequence() or type.isRecord()) and type.nullable():
8003 # These are both wrapped in Nullable<>
8004 recTemplate, recInfall = getWrapTemplateForType(
8005 type.inner,
8006 descriptorProvider,
8007 "%s.Value()" % result,
8008 successCode,
8009 returnsNewObject,
8010 exceptionCode,
8011 spiderMonkeyInterfacesAreStructs,
8013 code = fill(
8016 if (${result}.IsNull()) {
8017 $*{setNull}
8019 $*{recTemplate}
8020 """,
8021 result=result,
8022 setNull=setNull(),
8023 recTemplate=recTemplate,
8025 return code, recInfall
8027 if type.isSequence():
8028 # Now do non-nullable sequences. Our success code is just to break to
8029 # where we set the element in the array. Note that we bump the
8030 # sequenceWrapLevel around this call so that nested sequence conversions
8031 # will use different iteration variables.
8032 global sequenceWrapLevel
8033 index = "sequenceIdx%d" % sequenceWrapLevel
8034 sequenceWrapLevel += 1
8035 innerTemplate = wrapForType(
8036 type.inner,
8037 descriptorProvider,
8039 "result": "%s[%s]" % (result, index),
8040 "successCode": "break;\n",
8041 "jsvalRef": "tmp",
8042 "jsvalHandle": "&tmp",
8043 "returnsNewObject": returnsNewObject,
8044 "exceptionCode": exceptionCode,
8045 "obj": "returnArray",
8046 "spiderMonkeyInterfacesAreStructs": spiderMonkeyInterfacesAreStructs,
8049 sequenceWrapLevel -= 1
8050 code = fill(
8053 uint32_t length = ${result}.Length();
8054 JS::Rooted<JSObject*> returnArray(cx, JS::NewArrayObject(cx, length));
8055 if (!returnArray) {
8056 $*{exceptionCode}
8058 // Scope for 'tmp'
8060 JS::Rooted<JS::Value> tmp(cx);
8061 for (uint32_t ${index} = 0; ${index} < length; ++${index}) {
8062 // Control block to let us common up the JS_DefineElement calls when there
8063 // are different ways to succeed at wrapping the object.
8064 do {
8065 $*{innerTemplate}
8066 } while (false);
8067 if (!JS_DefineElement(cx, returnArray, ${index}, tmp,
8068 JSPROP_ENUMERATE)) {
8069 $*{exceptionCode}
8073 $*{set}
8074 """,
8075 result=result,
8076 exceptionCode=exceptionCode,
8077 index=index,
8078 innerTemplate=innerTemplate,
8079 set=setObject("*returnArray"),
8082 return (code, False)
8084 if type.isRecord():
8085 # Now do non-nullable record. Our success code is just to break to
8086 # where we define the property on the object. Note that we bump the
8087 # recordWrapLevel around this call so that nested record conversions
8088 # will use different temp value names.
8089 global recordWrapLevel
8090 valueName = "recordValue%d" % recordWrapLevel
8091 recordWrapLevel += 1
8092 innerTemplate = wrapForType(
8093 type.inner,
8094 descriptorProvider,
8096 "result": valueName,
8097 "successCode": "break;\n",
8098 "jsvalRef": "tmp",
8099 "jsvalHandle": "&tmp",
8100 "returnsNewObject": returnsNewObject,
8101 "exceptionCode": exceptionCode,
8102 "obj": "returnObj",
8103 "spiderMonkeyInterfacesAreStructs": spiderMonkeyInterfacesAreStructs,
8106 recordWrapLevel -= 1
8107 if type.keyType.isByteString():
8108 # There is no length-taking JS_DefineProperty. So to keep
8109 # things sane with embedded nulls, we want to byte-inflate
8110 # to an nsAString. The only byte-inflation function we
8111 # have around is AppendASCIItoUTF16, which luckily doesn't
8112 # assert anything about the input being ASCII.
8113 expandedKeyDecl = "NS_ConvertASCIItoUTF16 expandedKey(entry.mKey);\n"
8114 keyName = "expandedKey"
8115 elif type.keyType.isUTF8String():
8116 # We do the same as above for utf8 strings. We could do better if
8117 # we had a DefineProperty API that takes utf-8 property names.
8118 expandedKeyDecl = "NS_ConvertUTF8toUTF16 expandedKey(entry.mKey);\n"
8119 keyName = "expandedKey"
8120 else:
8121 expandedKeyDecl = ""
8122 keyName = "entry.mKey"
8124 code = fill(
8127 JS::Rooted<JSObject*> returnObj(cx, JS_NewPlainObject(cx));
8128 if (!returnObj) {
8129 $*{exceptionCode}
8131 // Scope for 'tmp'
8133 JS::Rooted<JS::Value> tmp(cx);
8134 for (auto& entry : ${result}.Entries()) {
8135 auto& ${valueName} = entry.mValue;
8136 // Control block to let us common up the JS_DefineUCProperty calls when there
8137 // are different ways to succeed at wrapping the value.
8138 do {
8139 $*{innerTemplate}
8140 } while (false);
8141 $*{expandedKeyDecl}
8142 if (!JS_DefineUCProperty(cx, returnObj,
8143 ${keyName}.BeginReading(),
8144 ${keyName}.Length(), tmp,
8145 JSPROP_ENUMERATE)) {
8146 $*{exceptionCode}
8150 $*{set}
8151 """,
8152 result=result,
8153 exceptionCode=exceptionCode,
8154 valueName=valueName,
8155 innerTemplate=innerTemplate,
8156 expandedKeyDecl=expandedKeyDecl,
8157 keyName=keyName,
8158 set=setObject("*returnObj"),
8161 return (code, False)
8163 if type.isPromise():
8164 assert not type.nullable()
8165 # The use of ToJSValue here is a bit annoying because the Promise
8166 # version is not inlined. But we can't put an inline version in either
8167 # ToJSValue.h or BindingUtils.h, because Promise.h includes ToJSValue.h
8168 # and that includes BindingUtils.h, so we'd get an include loop if
8169 # either of those headers included Promise.h. And trying to write the
8170 # conversion by hand here is pretty annoying because we have to handle
8171 # the various RefPtr, rawptr, NonNull, etc cases, which ToJSValue will
8172 # handle for us. So just eat the cost of the function call.
8173 return (wrapAndSetPtr("ToJSValue(cx, %s, ${jsvalHandle})" % result), False)
8175 if type.isGeckoInterface() and not type.isCallbackInterface():
8176 descriptor = descriptorProvider.getDescriptor(
8177 type.unroll().inner.identifier.name
8179 if type.nullable():
8180 if descriptor.interface.identifier.name == "WindowProxy":
8181 template, infal = getWrapTemplateForType(
8182 type.inner,
8183 descriptorProvider,
8184 "%s.Value()" % result,
8185 successCode,
8186 returnsNewObject,
8187 exceptionCode,
8188 spiderMonkeyInterfacesAreStructs,
8190 return (
8191 "if (%s.IsNull()) {\n" % result
8192 + indent(setNull())
8193 + "}\n"
8194 + template,
8195 infal,
8198 wrappingCode = "if (!%s) {\n" % (result) + indent(setNull()) + "}\n"
8199 else:
8200 wrappingCode = ""
8202 if not descriptor.interface.isExternal():
8203 if descriptor.wrapperCache:
8204 wrapMethod = "GetOrCreateDOMReflector"
8205 wrapArgs = "cx, %s, ${jsvalHandle}" % result
8206 else:
8207 wrapMethod = "WrapNewBindingNonWrapperCachedObject"
8208 wrapArgs = "cx, ${obj}, %s, ${jsvalHandle}" % result
8209 if isConstructorRetval:
8210 wrapArgs += ", desiredProto"
8211 wrap = "%s(%s)" % (wrapMethod, wrapArgs)
8212 # Can only fail to wrap as a new-binding object if they already
8213 # threw an exception.
8214 failed = "MOZ_ASSERT(JS_IsExceptionPending(cx));\n" + exceptionCode
8215 else:
8216 if descriptor.notflattened:
8217 getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
8218 else:
8219 getIID = ""
8220 wrap = "WrapObject(cx, %s, %s${jsvalHandle})" % (result, getIID)
8221 failed = None
8223 wrappingCode += wrapAndSetPtr(wrap, failed)
8224 return (wrappingCode, False)
8226 if type.isJSString():
8227 return (setString(result), False)
8229 if type.isDOMString() or type.isUSVString():
8230 if type.nullable():
8231 return (
8232 wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result),
8233 False,
8235 else:
8236 return (
8237 wrapAndSetPtr(
8238 "xpc::NonVoidStringToJsval(cx, %s, ${jsvalHandle})" % result
8240 False,
8243 if type.isByteString():
8244 if type.nullable():
8245 return (
8246 wrapAndSetPtr("ByteStringToJsval(cx, %s, ${jsvalHandle})" % result),
8247 False,
8249 else:
8250 return (
8251 wrapAndSetPtr(
8252 "NonVoidByteStringToJsval(cx, %s, ${jsvalHandle})" % result
8254 False,
8257 if type.isUTF8String():
8258 if type.nullable():
8259 return (
8260 wrapAndSetPtr("UTF8StringToJsval(cx, %s, ${jsvalHandle})" % result),
8261 False,
8263 else:
8264 return (
8265 wrapAndSetPtr(
8266 "NonVoidUTF8StringToJsval(cx, %s, ${jsvalHandle})" % result
8268 False,
8271 if type.isEnum():
8272 if type.nullable():
8273 resultLoc = "%s.Value()" % result
8274 else:
8275 resultLoc = result
8276 conversion = fill(
8278 if (!ToJSValue(cx, ${result}, $${jsvalHandle})) {
8279 $*{exceptionCode}
8281 $*{successCode}
8282 """,
8283 result=resultLoc,
8284 exceptionCode=exceptionCode,
8285 successCode=successCode,
8288 if type.nullable():
8289 conversion = CGIfElseWrapper(
8290 "%s.IsNull()" % result, CGGeneric(setNull()), CGGeneric(conversion)
8291 ).define()
8292 return conversion, False
8294 if type.isCallback() or type.isCallbackInterface():
8295 # Callbacks can store null if we nuked the compartments their
8296 # objects lived in.
8297 wrapCode = setObjectOrNull(
8298 "GetCallbackFromCallbackObject(cx, %(result)s)", wrapAsType=type
8300 if type.nullable():
8301 wrapCode = (
8302 "if (%(result)s) {\n"
8303 + indent(wrapCode)
8304 + "} else {\n"
8305 + indent(setNull())
8306 + "}\n"
8308 wrapCode = wrapCode % {"result": result}
8309 return wrapCode, False
8311 if type.isAny():
8312 # See comments in GetOrCreateDOMReflector explaining why we need
8313 # to wrap here.
8314 # NB: _setValue(..., type-that-is-any) calls JS_WrapValue(), so is fallible
8315 head = "JS::ExposeValueToActiveJS(%s);\n" % result
8316 return (head + _setValue(result, wrapAsType=type), False)
8318 if type.isObject() or (
8319 type.isSpiderMonkeyInterface() and not spiderMonkeyInterfacesAreStructs
8321 # See comments in GetOrCreateDOMReflector explaining why we need
8322 # to wrap here.
8323 if type.nullable():
8324 toValue = "%s"
8325 setter = setObjectOrNull
8326 head = """if (%s) {
8327 JS::ExposeObjectToActiveJS(%s);
8329 """ % (
8330 result,
8331 result,
8333 else:
8334 toValue = "*%s"
8335 setter = setObject
8336 head = "JS::ExposeObjectToActiveJS(%s);\n" % result
8337 # NB: setObject{,OrNull}(..., some-object-type) calls JS_WrapValue(), so is fallible
8338 return (head + setter(toValue % result, wrapAsType=type), False)
8340 if type.isObservableArray():
8341 # This first argument isn't used at all for now, the attribute getter
8342 # for ObservableArray type are generated in getObservableArrayGetterBody
8343 # instead.
8344 return "", False
8346 if not (
8347 type.isUnion()
8348 or type.isPrimitive()
8349 or type.isDictionary()
8350 or (type.isSpiderMonkeyInterface() and spiderMonkeyInterfacesAreStructs)
8352 raise TypeError("Need to learn to wrap %s" % type)
8354 if type.nullable():
8355 recTemplate, recInfal = getWrapTemplateForType(
8356 type.inner,
8357 descriptorProvider,
8358 "%s.Value()" % result,
8359 successCode,
8360 returnsNewObject,
8361 exceptionCode,
8362 spiderMonkeyInterfacesAreStructs,
8364 return (
8365 "if (%s.IsNull()) {\n" % result + indent(setNull()) + "}\n" + recTemplate,
8366 recInfal,
8369 if type.isSpiderMonkeyInterface():
8370 assert spiderMonkeyInterfacesAreStructs
8371 # See comments in GetOrCreateDOMReflector explaining why we need
8372 # to wrap here.
8373 # NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible
8374 return (setObject("*%s.Obj()" % result, wrapAsType=type), False)
8376 if type.isUnion():
8377 return (wrapAndSetPtr("%s.ToJSVal(cx, ${obj}, ${jsvalHandle})" % result), False)
8379 if type.isDictionary():
8380 return (
8381 wrapAndSetPtr("%s.ToObjectInternal(cx, ${jsvalHandle})" % result),
8382 False,
8385 tag = type.tag()
8387 if tag in [
8388 IDLType.Tags.int8,
8389 IDLType.Tags.uint8,
8390 IDLType.Tags.int16,
8391 IDLType.Tags.uint16,
8392 IDLType.Tags.int32,
8394 return (setInt32("int32_t(%s)" % result), True)
8396 elif tag in [
8397 IDLType.Tags.int64,
8398 IDLType.Tags.uint64,
8399 IDLType.Tags.unrestricted_float,
8400 IDLType.Tags.float,
8401 IDLType.Tags.unrestricted_double,
8402 IDLType.Tags.double,
8404 # XXXbz will cast to double do the "even significand" thing that webidl
8405 # calls for for 64-bit ints? Do we care?
8406 return (setDouble("double(%s)" % result), True)
8408 elif tag == IDLType.Tags.uint32:
8409 return (setUint32(result), True)
8411 elif tag == IDLType.Tags.bool:
8412 return (setBoolean(result), True)
8414 else:
8415 raise TypeError("Need to learn to wrap primitive: %s" % type)
8418 def wrapForType(type, descriptorProvider, templateValues):
8420 Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict
8421 that should contain:
8423 * 'jsvalRef': something that can have .address() called on it to get a
8424 JS::Value* and .set() called on it to set it to a JS::Value.
8425 This can be a JS::MutableHandle<JS::Value> or a
8426 JS::Rooted<JS::Value>.
8427 * 'jsvalHandle': something that can be passed to methods taking a
8428 JS::MutableHandle<JS::Value>. This can be a
8429 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
8430 * 'obj' (optional): the name of the variable that contains the JSObject to
8431 use as a scope when wrapping, if not supplied 'obj'
8432 will be used as the name
8433 * 'result' (optional): the name of the variable in which the C++ value is
8434 stored, if not supplied 'result' will be used as
8435 the name
8436 * 'successCode' (optional): the code to run once we have successfully
8437 done the conversion, if not supplied 'return
8438 true;' will be used as the code. The
8439 successCode must ensure that once it runs no
8440 more of the conversion template will be
8441 executed (e.g. by doing a 'return' or 'break'
8442 as appropriate).
8443 * 'returnsNewObject' (optional): If true, we're wrapping for the return
8444 value of a [NewObject] method. Assumed
8445 false if not set.
8446 * 'exceptionCode' (optional): Code to run when a JS exception is thrown.
8447 The default is "return false;". The code
8448 passed here must return.
8449 * 'isConstructorRetval' (optional): If true, we're wrapping a constructor
8450 return value.
8452 wrap = getWrapTemplateForType(
8453 type,
8454 descriptorProvider,
8455 templateValues.get("result", "result"),
8456 templateValues.get("successCode", None),
8457 templateValues.get("returnsNewObject", False),
8458 templateValues.get("exceptionCode", "return false;\n"),
8459 templateValues.get("spiderMonkeyInterfacesAreStructs", False),
8460 isConstructorRetval=templateValues.get("isConstructorRetval", False),
8461 )[0]
8463 defaultValues = {"obj": "obj"}
8464 return string.Template(wrap).substitute(defaultValues, **templateValues)
8467 def infallibleForMember(member, type, descriptorProvider):
8469 Determine the fallibility of changing a C++ value of IDL type "type" into
8470 JS for the given attribute. Apart from returnsNewObject, all the defaults
8471 are used, since the fallbility does not change based on the boolean values,
8472 and the template will be discarded.
8474 CURRENT ASSUMPTIONS:
8475 We assume that successCode for wrapping up return values cannot contain
8476 failure conditions.
8478 return getWrapTemplateForType(
8479 type,
8480 descriptorProvider,
8481 "result",
8482 None,
8483 memberReturnsNewObject(member),
8484 "return false;\n",
8485 False,
8486 )[1]
8489 def leafTypeNeedsCx(type, retVal):
8490 return (
8491 type.isAny()
8492 or type.isObject()
8493 or type.isJSString()
8494 or (retVal and type.isSpiderMonkeyInterface())
8498 def leafTypeNeedsScopeObject(type, retVal):
8499 return retVal and type.isSpiderMonkeyInterface()
8502 def leafTypeNeedsRooting(type):
8503 return leafTypeNeedsCx(type, False) or type.isSpiderMonkeyInterface()
8506 def typeNeedsRooting(type):
8507 return typeMatchesLambda(type, lambda t: leafTypeNeedsRooting(t))
8510 def typeNeedsCx(type, retVal=False):
8511 return typeMatchesLambda(type, lambda t: leafTypeNeedsCx(t, retVal))
8514 def typeNeedsScopeObject(type, retVal=False):
8515 return typeMatchesLambda(type, lambda t: leafTypeNeedsScopeObject(t, retVal))
8518 def typeMatchesLambda(type, func):
8519 if type is None:
8520 return False
8521 if type.nullable():
8522 return typeMatchesLambda(type.inner, func)
8523 if type.isSequence() or type.isRecord():
8524 return typeMatchesLambda(type.inner, func)
8525 if type.isUnion():
8526 return any(typeMatchesLambda(t, func) for t in type.unroll().flatMemberTypes)
8527 if type.isDictionary():
8528 return dictionaryMatchesLambda(type.inner, func)
8529 return func(type)
8532 def dictionaryMatchesLambda(dictionary, func):
8533 return any(typeMatchesLambda(m.type, func) for m in dictionary.members) or (
8534 dictionary.parent and dictionaryMatchesLambda(dictionary.parent, func)
8538 # Whenever this is modified, please update CGNativeMember.getRetvalInfo as
8539 # needed to keep the types compatible.
8540 def getRetvalDeclarationForType(returnType, descriptorProvider, isMember=False):
8542 Returns a tuple containing five things:
8544 1) A CGThing for the type of the return value, or None if there is no need
8545 for a return value.
8547 2) A value indicating the kind of ourparam to pass the value as. Valid
8548 options are None to not pass as an out param at all, "ref" (to pass a
8549 reference as an out param), and "ptr" (to pass a pointer as an out
8550 param).
8552 3) A CGThing for a tracer for the return value, or None if no tracing is
8553 needed.
8555 4) An argument string to pass to the retval declaration
8556 constructor or None if there are no arguments.
8558 5) The name of a function that needs to be called with the return value
8559 before using it, or None if no function needs to be called.
8561 if returnType is None or returnType.isUndefined():
8562 # Nothing to declare
8563 return None, None, None, None, None
8564 if returnType.isPrimitive() and returnType.tag() in builtinNames:
8565 result = CGGeneric(builtinNames[returnType.tag()])
8566 if returnType.nullable():
8567 result = CGTemplatedType("Nullable", result)
8568 return result, None, None, None, None
8569 if returnType.isJSString():
8570 if isMember:
8571 raise TypeError("JSString not supported as return type member")
8572 return CGGeneric("JS::Rooted<JSString*>"), "ptr", None, "cx", None
8573 if returnType.isDOMString() or returnType.isUSVString():
8574 if isMember:
8575 return CGGeneric("nsString"), "ref", None, None, None
8576 return CGGeneric("DOMString"), "ref", None, None, None
8577 if returnType.isByteString() or returnType.isUTF8String():
8578 if isMember:
8579 return CGGeneric("nsCString"), "ref", None, None, None
8580 return CGGeneric("nsAutoCString"), "ref", None, None, None
8581 if returnType.isEnum():
8582 result = CGGeneric(returnType.unroll().inner.identifier.name)
8583 if returnType.nullable():
8584 result = CGTemplatedType("Nullable", result)
8585 return result, None, None, None, None
8586 if returnType.isGeckoInterface() or returnType.isPromise():
8587 if returnType.isGeckoInterface():
8588 typeName = returnType.unroll().inner.identifier.name
8589 if typeName == "WindowProxy":
8590 result = CGGeneric("WindowProxyHolder")
8591 if returnType.nullable():
8592 result = CGTemplatedType("Nullable", result)
8593 return result, None, None, None, None
8595 typeName = descriptorProvider.getDescriptor(typeName).nativeType
8596 else:
8597 typeName = "Promise"
8598 if isMember:
8599 conversion = None
8600 result = CGGeneric("StrongPtrForMember<%s>" % typeName)
8601 else:
8602 conversion = CGGeneric("StrongOrRawPtr<%s>" % typeName)
8603 result = CGGeneric("auto")
8604 return result, None, None, None, conversion
8605 if returnType.isCallback():
8606 name = returnType.unroll().callback.identifier.name
8607 return CGGeneric("RefPtr<%s>" % name), None, None, None, None
8608 if returnType.isAny():
8609 if isMember:
8610 return CGGeneric("JS::Value"), None, None, None, None
8611 return CGGeneric("JS::Rooted<JS::Value>"), "ptr", None, "cx", None
8612 if returnType.isObject() or returnType.isSpiderMonkeyInterface():
8613 if isMember:
8614 return CGGeneric("JSObject*"), None, None, None, None
8615 return CGGeneric("JS::Rooted<JSObject*>"), "ptr", None, "cx", None
8616 if returnType.isSequence():
8617 nullable = returnType.nullable()
8618 if nullable:
8619 returnType = returnType.inner
8620 result, _, _, _, _ = getRetvalDeclarationForType(
8621 returnType.inner, descriptorProvider, isMember="Sequence"
8623 # While we have our inner type, set up our rooter, if needed
8624 if not isMember and typeNeedsRooting(returnType):
8625 rooter = CGGeneric(
8626 "SequenceRooter<%s > resultRooter(cx, &result);\n" % result.define()
8628 else:
8629 rooter = None
8630 result = CGTemplatedType("nsTArray", result)
8631 if nullable:
8632 result = CGTemplatedType("Nullable", result)
8633 return result, "ref", rooter, None, None
8634 if returnType.isRecord():
8635 nullable = returnType.nullable()
8636 if nullable:
8637 returnType = returnType.inner
8638 result, _, _, _, _ = getRetvalDeclarationForType(
8639 returnType.inner, descriptorProvider, isMember="Record"
8641 # While we have our inner type, set up our rooter, if needed
8642 if not isMember and typeNeedsRooting(returnType):
8643 rooter = CGGeneric(
8644 "RecordRooter<%s> resultRooter(cx, &result);\n"
8645 % ("nsString, " + result.define())
8647 else:
8648 rooter = None
8649 result = CGTemplatedType("Record", [recordKeyDeclType(returnType), result])
8650 if nullable:
8651 result = CGTemplatedType("Nullable", result)
8652 return result, "ref", rooter, None, None
8653 if returnType.isDictionary():
8654 nullable = returnType.nullable()
8655 dictName = CGDictionary.makeDictionaryName(returnType.unroll().inner)
8656 result = CGGeneric(dictName)
8657 if not isMember and typeNeedsRooting(returnType):
8658 if nullable:
8659 result = CGTemplatedType("NullableRootedDictionary", result)
8660 else:
8661 result = CGTemplatedType("RootedDictionary", result)
8662 resultArgs = "cx"
8663 else:
8664 if nullable:
8665 result = CGTemplatedType("Nullable", result)
8666 resultArgs = None
8667 return result, "ref", None, resultArgs, None
8668 if returnType.isUnion():
8669 result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True))
8670 if not isMember and typeNeedsRooting(returnType):
8671 if returnType.nullable():
8672 result = CGTemplatedType("NullableRootedUnion", result)
8673 else:
8674 result = CGTemplatedType("RootedUnion", result)
8675 resultArgs = "cx"
8676 else:
8677 if returnType.nullable():
8678 result = CGTemplatedType("Nullable", result)
8679 resultArgs = None
8680 return result, "ref", None, resultArgs, None
8681 raise TypeError("Don't know how to declare return value for %s" % returnType)
8684 def needCx(returnType, arguments, extendedAttributes, considerTypes, static=False):
8685 return (
8686 not static
8687 and considerTypes
8688 and (
8689 typeNeedsCx(returnType, True) or any(typeNeedsCx(a.type) for a in arguments)
8691 or "implicitJSContext" in extendedAttributes
8695 def needScopeObject(
8696 returnType, arguments, extendedAttributes, isWrapperCached, considerTypes, isMember
8699 isMember should be true if we're dealing with an attribute
8700 annotated as [StoreInSlot].
8702 return (
8703 considerTypes
8704 and not isWrapperCached
8705 and (
8706 (not isMember and typeNeedsScopeObject(returnType, True))
8707 or any(typeNeedsScopeObject(a.type) for a in arguments)
8712 def callerTypeGetterForDescriptor(descriptor):
8713 if descriptor.interface.isExposedInAnyWorker():
8714 systemCallerGetter = "nsContentUtils::ThreadsafeIsSystemCaller"
8715 else:
8716 systemCallerGetter = "nsContentUtils::IsSystemCaller"
8717 return "%s(cx) ? CallerType::System : CallerType::NonSystem" % systemCallerGetter
8720 class CGCallGenerator(CGThing):
8722 A class to generate an actual call to a C++ object. Assumes that the C++
8723 object is stored in a variable whose name is given by the |object| argument.
8725 needsCallerType is a boolean indicating whether the call should receive
8726 a PrincipalType for the caller.
8728 needsErrorResult is a boolean indicating whether the call should be
8729 fallible and thus needs ErrorResult parameter.
8731 resultVar: If the returnType is not void, then the result of the call is
8732 stored in a C++ variable named by resultVar. The caller is responsible for
8733 declaring the result variable. If the caller doesn't care about the result
8734 value, resultVar can be omitted.
8736 context: The context string to pass to MaybeSetPendingException.
8739 def __init__(
8740 self,
8741 needsErrorResult,
8742 needsCallerType,
8743 isChromeOnly,
8744 arguments,
8745 argsPre,
8746 returnType,
8747 extendedAttributes,
8748 descriptor,
8749 nativeMethodName,
8750 static,
8751 object="self",
8752 argsPost=[],
8753 resultVar=None,
8754 context="nullptr",
8756 CGThing.__init__(self)
8759 result,
8760 resultOutParam,
8761 resultRooter,
8762 resultArgs,
8763 resultConversion,
8764 ) = getRetvalDeclarationForType(returnType, descriptor)
8766 args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
8767 for a, name in arguments:
8768 arg = CGGeneric(name)
8770 # Now constify the things that need it
8771 def needsConst(a):
8772 if a.type.isDictionary():
8773 return True
8774 if a.type.isSequence():
8775 return True
8776 if a.type.isRecord():
8777 return True
8778 # isObject() types are always a JS::Rooted, whether
8779 # nullable or not, and it turns out a const JS::Rooted
8780 # is not very helpful at all (in particular, it won't
8781 # even convert to a JS::Handle).
8782 # XXX bz Well, why not???
8783 if a.type.nullable() and not a.type.isObject():
8784 return True
8785 if a.type.isString():
8786 return True
8787 if a.canHaveMissingValue():
8788 # This will need an Optional or it's a variadic;
8789 # in both cases it should be const.
8790 return True
8791 if a.type.isUnion():
8792 return True
8793 if a.type.isSpiderMonkeyInterface():
8794 return True
8795 return False
8797 if needsConst(a):
8798 arg = CGWrapper(arg, pre="Constify(", post=")")
8799 # And convert NonNull<T> to T&
8800 if (
8801 (a.type.isGeckoInterface() or a.type.isCallback() or a.type.isPromise())
8802 and not a.type.nullable()
8803 ) or a.type.isDOMString():
8804 arg = CGWrapper(arg, pre="NonNullHelper(", post=")")
8806 # If it's a refcounted object, let the static analysis know it's
8807 # alive for the duration of the call.
8808 if a.type.isGeckoInterface() or a.type.isCallback():
8809 arg = CGWrapper(arg, pre="MOZ_KnownLive(", post=")")
8811 args.append(arg)
8813 needResultDecl = False
8815 # Build up our actual call
8816 self.cgRoot = CGList([])
8818 # Return values that go in outparams go here
8819 if resultOutParam is not None:
8820 if resultVar is None:
8821 needResultDecl = True
8822 resultVar = "result"
8823 if resultOutParam == "ref":
8824 args.append(CGGeneric(resultVar))
8825 else:
8826 assert resultOutParam == "ptr"
8827 args.append(CGGeneric("&" + resultVar))
8829 needsSubjectPrincipal = "needsSubjectPrincipal" in extendedAttributes
8830 if needsSubjectPrincipal:
8831 needsNonSystemPrincipal = (
8832 "needsNonSystemSubjectPrincipal" in extendedAttributes
8834 if needsNonSystemPrincipal:
8835 principalType = "nsIPrincipal*"
8836 subjectPrincipalArg = "subjectPrincipal"
8837 checkPrincipal = dedent(
8839 if (principal->IsSystemPrincipal()) {
8840 principal = nullptr;
8844 else:
8845 principalType = "NonNull<nsIPrincipal>"
8846 subjectPrincipalArg = "NonNullHelper(subjectPrincipal)"
8847 checkPrincipal = ""
8849 self.cgRoot.append(
8850 CGGeneric(
8851 fill(
8853 ${principalType} subjectPrincipal;
8855 JS::Realm* realm = js::GetContextRealm(cx);
8856 MOZ_ASSERT(realm);
8857 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
8858 nsIPrincipal* principal = nsJSPrincipals::get(principals);
8859 ${checkPrincipal}
8860 subjectPrincipal = principal;
8862 """,
8863 principalType=principalType,
8864 checkPrincipal=checkPrincipal,
8869 args.append(CGGeneric("MOZ_KnownLive(%s)" % subjectPrincipalArg))
8871 if needsCallerType:
8872 if isChromeOnly:
8873 args.append(CGGeneric("SystemCallerGuarantee()"))
8874 else:
8875 args.append(CGGeneric(callerTypeGetterForDescriptor(descriptor)))
8877 canOOM = "canOOM" in extendedAttributes
8878 if needsErrorResult:
8879 args.append(CGGeneric("rv"))
8880 elif canOOM:
8881 args.append(CGGeneric("OOMReporter::From(rv)"))
8882 args.extend(CGGeneric(arg) for arg in argsPost)
8884 call = CGGeneric(nativeMethodName)
8885 if not static:
8886 call = CGWrapper(call, pre="%s->" % object)
8887 call = CGList([call, CGWrapper(args, pre="(", post=")")])
8888 if returnType is None or returnType.isUndefined() or resultOutParam is not None:
8889 assert resultConversion is None
8890 call = CGList(
8892 CGWrapper(
8893 call,
8894 pre=(
8895 "// NOTE: This assert does NOT call the function.\n"
8896 "static_assert(std::is_void_v<decltype("
8898 post=')>, "Should be returning void here");',
8900 call,
8902 "\n",
8904 elif resultConversion is not None:
8905 call = CGList([resultConversion, CGWrapper(call, pre="(", post=")")])
8906 if resultVar is None and result is not None:
8907 needResultDecl = True
8908 resultVar = "result"
8910 if needResultDecl:
8911 if resultArgs is not None:
8912 resultArgsStr = "(%s)" % resultArgs
8913 else:
8914 resultArgsStr = ""
8915 result = CGWrapper(result, post=(" %s%s" % (resultVar, resultArgsStr)))
8916 if resultOutParam is None and resultArgs is None:
8917 call = CGList([result, CGWrapper(call, pre="(", post=")")])
8918 else:
8919 self.cgRoot.append(CGWrapper(result, post=";\n"))
8920 if resultOutParam is None:
8921 call = CGWrapper(call, pre=resultVar + " = ")
8922 if resultRooter is not None:
8923 self.cgRoot.append(resultRooter)
8924 elif result is not None:
8925 assert resultOutParam is None
8926 call = CGWrapper(call, pre=resultVar + " = ")
8928 call = CGWrapper(call, post=";\n")
8929 self.cgRoot.append(call)
8931 if needsErrorResult or canOOM:
8932 self.cgRoot.prepend(CGGeneric("FastErrorResult rv;\n"))
8933 self.cgRoot.append(
8934 CGGeneric(
8935 fill(
8937 if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx, ${context}))) {
8938 return false;
8940 """,
8941 context=context,
8946 self.cgRoot.append(CGGeneric("MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"))
8948 def define(self):
8949 return self.cgRoot.define()
8952 def getUnionMemberName(type):
8953 # Promises can't be in unions, because they're not distinguishable
8954 # from anything else.
8955 assert not type.isPromise()
8956 if type.isGeckoInterface():
8957 return type.inner.identifier.name
8958 if type.isEnum():
8959 return type.inner.identifier.name
8960 return type.name
8963 # A counter for making sure that when we're wrapping up things in
8964 # nested sequences we don't use the same variable name to iterate over
8965 # different sequences.
8966 sequenceWrapLevel = 0
8967 recordWrapLevel = 0
8970 def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
8972 Take the thing named by "value" and if it contains "any",
8973 "object", or spidermonkey-interface types inside return a CGThing
8974 that will wrap them into the current compartment.
8976 if type.isAny():
8977 assert not type.nullable()
8978 if isMember:
8979 value = "JS::MutableHandle<JS::Value>::fromMarkedLocation(&%s)" % value
8980 else:
8981 value = "&" + value
8982 return CGGeneric(
8983 "if (!JS_WrapValue(cx, %s)) {\n" " return false;\n" "}\n" % value
8986 if type.isObject():
8987 if isMember:
8988 value = "JS::MutableHandle<JSObject*>::fromMarkedLocation(&%s)" % value
8989 else:
8990 value = "&" + value
8991 return CGGeneric(
8992 "if (!JS_WrapObject(cx, %s)) {\n" " return false;\n" "}\n" % value
8995 if type.isSpiderMonkeyInterface():
8996 origValue = value
8997 if type.nullable():
8998 value = "%s.Value()" % value
8999 wrapCode = CGGeneric(
9000 "if (!%s.WrapIntoNewCompartment(cx)) {\n" " return false;\n" "}\n" % value
9002 if type.nullable():
9003 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
9004 return wrapCode
9006 if type.isSequence():
9007 origValue = value
9008 origType = type
9009 if type.nullable():
9010 type = type.inner
9011 value = "%s.Value()" % value
9012 global sequenceWrapLevel
9013 index = "indexName%d" % sequenceWrapLevel
9014 sequenceWrapLevel += 1
9015 wrapElement = wrapTypeIntoCurrentCompartment(
9016 type.inner, "%s[%s]" % (value, index)
9018 sequenceWrapLevel -= 1
9019 if not wrapElement:
9020 return None
9021 wrapCode = CGWrapper(
9022 CGIndenter(wrapElement),
9023 pre=(
9024 "for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n"
9025 % (index, index, value, index)
9027 post="}\n",
9029 if origType.nullable():
9030 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
9031 return wrapCode
9033 if type.isRecord():
9034 origType = type
9035 if type.nullable():
9036 type = type.inner
9037 recordRef = "%s.Value()" % value
9038 else:
9039 recordRef = value
9040 global recordWrapLevel
9041 entryRef = "mapEntry%d" % recordWrapLevel
9042 recordWrapLevel += 1
9043 wrapElement = wrapTypeIntoCurrentCompartment(type.inner, "%s.mValue" % entryRef)
9044 recordWrapLevel -= 1
9045 if not wrapElement:
9046 return None
9047 wrapCode = CGWrapper(
9048 CGIndenter(wrapElement),
9049 pre=("for (auto& %s : %s.Entries()) {\n" % (entryRef, recordRef)),
9050 post="}\n",
9052 if origType.nullable():
9053 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % value)
9054 return wrapCode
9056 if type.isDictionary():
9057 assert not type.nullable()
9058 myDict = type.inner
9059 memberWraps = []
9060 while myDict:
9061 for member in myDict.members:
9062 memberWrap = wrapArgIntoCurrentCompartment(
9063 member,
9064 "%s.%s"
9065 % (value, CGDictionary.makeMemberName(member.identifier.name)),
9067 if memberWrap:
9068 memberWraps.append(memberWrap)
9069 myDict = myDict.parent
9070 return CGList(memberWraps) if len(memberWraps) != 0 else None
9072 if type.isUnion():
9073 origValue = value
9074 origType = type
9075 if type.nullable():
9076 type = type.inner
9077 value = "%s.Value()" % value
9078 memberWraps = []
9079 for member in type.flatMemberTypes:
9080 memberName = getUnionMemberName(member)
9081 memberWrap = wrapTypeIntoCurrentCompartment(
9082 member, "%s.GetAs%s()" % (value, memberName)
9084 if memberWrap:
9085 memberWrap = CGIfWrapper(memberWrap, "%s.Is%s()" % (value, memberName))
9086 memberWraps.append(memberWrap)
9087 if len(memberWraps) == 0:
9088 return None
9089 wrapCode = CGList(memberWraps, "else ")
9090 if origType.nullable():
9091 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
9092 return wrapCode
9094 if (
9095 type.isUndefined()
9096 or type.isString()
9097 or type.isPrimitive()
9098 or type.isEnum()
9099 or type.isGeckoInterface()
9100 or type.isCallback()
9101 or type.isPromise()
9103 # All of these don't need wrapping.
9104 return None
9106 raise TypeError(
9107 "Unknown type; we don't know how to wrap it in constructor "
9108 "arguments: %s" % type
9112 def wrapArgIntoCurrentCompartment(arg, value, isMember=True):
9114 As wrapTypeIntoCurrentCompartment but handles things being optional
9116 origValue = value
9117 isOptional = arg.canHaveMissingValue()
9118 if isOptional:
9119 value = value + ".Value()"
9120 wrap = wrapTypeIntoCurrentCompartment(arg.type, value, isMember)
9121 if wrap and isOptional:
9122 wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue)
9123 return wrap
9126 def needsCallerType(m):
9127 return m.getExtendedAttribute("NeedsCallerType")
9130 class CGPerSignatureCall(CGThing):
9132 This class handles the guts of generating code for a particular
9133 call signature. A call signature consists of four things:
9135 1) A return type, which can be None to indicate that there is no
9136 actual return value (e.g. this is an attribute setter) or an
9137 IDLType if there's an IDL type involved (including |void|).
9138 2) An argument list, which is allowed to be empty.
9139 3) A name of a native method to call. It is ignored for methods
9140 annotated with the "[WebExtensionStub=...]" extended attribute.
9141 4) Whether or not this method is static. Note that this only controls how
9142 the method is called (|self->nativeMethodName(...)| vs
9143 |nativeMethodName(...)|).
9145 We also need to know whether this is a method or a getter/setter
9146 to do error reporting correctly.
9148 The idlNode parameter can be either a method or an attr. We can query
9149 |idlNode.identifier| in both cases, so we can be agnostic between the two.
9151 dontSetSlot should be set to True if the value should not be cached in a
9152 slot (even if the attribute is marked as StoreInSlot or Cached in the
9153 WebIDL).
9155 errorReportingLabel can contain a custom label to use for error reporting.
9156 It will be inserted as is in the code, so if it needs to be a literal
9157 string in C++ it should be quoted.
9159 additionalArgsPre contains additional arguments that are added after the
9160 arguments that CGPerSignatureCall itself adds (JSContext, global, …), and
9161 before the actual arguments.
9164 # XXXbz For now each entry in the argument list is either an
9165 # IDLArgument or a FakeArgument, but longer-term we may want to
9166 # have ways of flagging things like JSContext* or optional_argc in
9167 # there.
9169 def __init__(
9170 self,
9171 returnType,
9172 arguments,
9173 nativeMethodName,
9174 static,
9175 descriptor,
9176 idlNode,
9177 argConversionStartsAt=0,
9178 getter=False,
9179 setter=False,
9180 isConstructor=False,
9181 useCounterName=None,
9182 resultVar=None,
9183 objectName="obj",
9184 dontSetSlot=False,
9185 extendedAttributes=None,
9186 errorReportingLabel=None,
9187 additionalArgsPre=[],
9189 assert idlNode.isMethod() == (not getter and not setter)
9190 assert idlNode.isAttr() == (getter or setter)
9191 # Constructors are always static
9192 assert not isConstructor or static
9194 CGThing.__init__(self)
9195 self.returnType = returnType
9196 self.descriptor = descriptor
9197 self.idlNode = idlNode
9198 if extendedAttributes is None:
9199 extendedAttributes = descriptor.getExtendedAttributes(
9200 idlNode, getter=getter, setter=setter
9202 self.extendedAttributes = extendedAttributes
9203 self.arguments = arguments
9204 self.argCount = len(arguments)
9205 self.isConstructor = isConstructor
9206 self.setSlot = (
9207 not dontSetSlot and idlNode.isAttr() and idlNode.slotIndices is not None
9209 cgThings = []
9211 deprecated = idlNode.getExtendedAttribute("Deprecated") or (
9212 idlNode.isStatic()
9213 and descriptor.interface.getExtendedAttribute("Deprecated")
9215 if deprecated:
9216 cgThings.append(
9217 CGGeneric(
9218 dedent(
9220 DeprecationWarning(cx, obj, DeprecatedOperations::e%s);
9222 % deprecated[0]
9227 lenientFloatCode = None
9228 if idlNode.getExtendedAttribute("LenientFloat") is not None and (
9229 setter or idlNode.isMethod()
9231 cgThings.append(
9232 CGGeneric(
9233 dedent(
9235 bool foundNonFiniteFloat = false;
9240 lenientFloatCode = "foundNonFiniteFloat = true;\n"
9242 argsPre = []
9243 if idlNode.isStatic():
9244 # If we're a constructor, "obj" may not be a function, so calling
9245 # XrayAwareCalleeGlobal() on it is not safe. Of course in the
9246 # constructor case either "obj" is an Xray or we're already in the
9247 # content compartment, not the Xray compartment, so just
9248 # constructing the GlobalObject from "obj" is fine.
9249 if isConstructor:
9250 objForGlobalObject = "obj"
9251 else:
9252 objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)"
9253 cgThings.append(
9254 CGGeneric(
9255 fill(
9257 GlobalObject global(cx, ${obj});
9258 if (global.Failed()) {
9259 return false;
9262 """,
9263 obj=objForGlobalObject,
9267 argsPre.append("global")
9269 # For JS-implemented interfaces we do not want to base the
9270 # needsCx decision on the types involved, just on our extended
9271 # attributes. Also, JSContext is not needed for the static case
9272 # since GlobalObject already contains the context.
9273 needsCx = needCx(
9274 returnType,
9275 arguments,
9276 self.extendedAttributes,
9277 not descriptor.interface.isJSImplemented(),
9278 static,
9280 if needsCx:
9281 argsPre.append("cx")
9283 needsUnwrap = False
9284 argsPost = []
9285 runConstructorInCallerCompartment = descriptor.interface.getExtendedAttribute(
9286 "RunConstructorInCallerCompartment"
9288 if isConstructor and not runConstructorInCallerCompartment:
9289 needsUnwrap = True
9290 needsUnwrappedVar = False
9291 unwrappedVar = "obj"
9292 if descriptor.interface.isJSImplemented():
9293 # We need the desired proto in our constructor, because the
9294 # constructor will actually construct our reflector.
9295 argsPost.append("desiredProto")
9296 elif descriptor.interface.isJSImplemented():
9297 if not idlNode.isStatic():
9298 needsUnwrap = True
9299 needsUnwrappedVar = True
9300 argsPost.append(
9301 "(unwrappedObj ? js::GetNonCCWObjectRealm(*unwrappedObj) : js::GetContextRealm(cx))"
9303 elif needScopeObject(
9304 returnType,
9305 arguments,
9306 self.extendedAttributes,
9307 descriptor.wrapperCache,
9308 True,
9309 idlNode.getExtendedAttribute("StoreInSlot"),
9311 # If we ever end up with APIs like this on cross-origin objects,
9312 # figure out how the CheckedUnwrapDynamic bits should work. Chances
9313 # are, just calling it with "cx" is fine... For now, though, just
9314 # assert that it does not matter.
9315 assert not descriptor.isMaybeCrossOriginObject()
9316 # The scope object should always be from the relevant
9317 # global. Make sure to unwrap it as needed.
9318 cgThings.append(
9319 CGGeneric(
9320 dedent(
9322 JS::Rooted<JSObject*> unwrappedObj(cx, js::CheckedUnwrapStatic(obj));
9323 // Caller should have ensured that "obj" can be unwrapped already.
9324 MOZ_DIAGNOSTIC_ASSERT(unwrappedObj);
9329 argsPre.append("unwrappedObj")
9331 if needsUnwrap and needsUnwrappedVar:
9332 # We cannot assign into obj because it's a Handle, not a
9333 # MutableHandle, so we need a separate Rooted.
9334 cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;\n"))
9335 unwrappedVar = "unwrappedObj.ref()"
9337 if idlNode.isMethod() and idlNode.isLegacycaller():
9338 # If we can have legacycaller with identifier, we can't
9339 # just use the idlNode to determine whether we're
9340 # generating code for the legacycaller or not.
9341 assert idlNode.isIdentifierLess()
9342 # Pass in our thisVal
9343 argsPre.append("args.thisv()")
9345 if idlNode.isMethod():
9346 argDescription = "argument %(index)d"
9347 elif setter:
9348 argDescription = "value being assigned"
9349 else:
9350 assert self.argCount == 0
9352 if needsUnwrap:
9353 # It's very important that we construct our unwrappedObj, if we need
9354 # to do it, before we might start setting up Rooted things for our
9355 # arguments, so that we don't violate the stack discipline Rooted
9356 # depends on.
9357 cgThings.append(
9358 CGGeneric("bool objIsXray = xpc::WrapperFactory::IsXrayWrapper(obj);\n")
9360 if needsUnwrappedVar:
9361 cgThings.append(
9362 CGIfWrapper(
9363 CGGeneric("unwrappedObj.emplace(cx, obj);\n"), "objIsXray"
9367 for i in range(argConversionStartsAt, self.argCount):
9368 cgThings.append(
9369 CGArgumentConverter(
9370 arguments[i],
9372 self.descriptor,
9373 argDescription % {"index": i + 1},
9374 idlNode,
9375 invalidEnumValueFatal=not setter,
9376 lenientFloatCode=lenientFloatCode,
9380 # Now that argument processing is done, enforce the LenientFloat stuff
9381 if lenientFloatCode:
9382 if setter:
9383 foundNonFiniteFloatBehavior = "return true;\n"
9384 else:
9385 assert idlNode.isMethod()
9386 foundNonFiniteFloatBehavior = dedent(
9388 args.rval().setUndefined();
9389 return true;
9392 cgThings.append(
9393 CGGeneric(
9394 fill(
9396 if (foundNonFiniteFloat) {
9397 $*{returnSteps}
9399 """,
9400 returnSteps=foundNonFiniteFloatBehavior,
9405 if needsUnwrap:
9406 # Something depends on having the unwrapped object, so unwrap it now.
9407 xraySteps = []
9408 # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is
9409 # not null.
9410 xraySteps.append(
9411 CGGeneric(
9412 fill(
9414 // Since our object is an Xray, we can just CheckedUnwrapStatic:
9415 // we know Xrays have no dynamic unwrap behavior.
9416 ${obj} = js::CheckedUnwrapStatic(${obj});
9417 if (!${obj}) {
9418 return false;
9420 """,
9421 obj=unwrappedVar,
9425 if isConstructor:
9426 # If we're called via an xray, we need to enter the underlying
9427 # object's compartment and then wrap up all of our arguments into
9428 # that compartment as needed. This is all happening after we've
9429 # already done the conversions from JS values to WebIDL (C++)
9430 # values, so we only need to worry about cases where there are 'any'
9431 # or 'object' types, or other things that we represent as actual
9432 # JSAPI types, present. Effectively, we're emulating a
9433 # CrossCompartmentWrapper, but working with the C++ types, not the
9434 # original list of JS::Values.
9435 cgThings.append(CGGeneric("Maybe<JSAutoRealm> ar;\n"))
9436 xraySteps.append(CGGeneric("ar.emplace(cx, obj);\n"))
9437 xraySteps.append(
9438 CGGeneric(
9439 dedent(
9441 if (!JS_WrapObject(cx, &desiredProto)) {
9442 return false;
9448 xraySteps.extend(
9449 wrapArgIntoCurrentCompartment(arg, argname, isMember=False)
9450 for arg, argname in self.getArguments()
9453 cgThings.append(CGIfWrapper(CGList(xraySteps), "objIsXray"))
9455 if idlNode.getExtendedAttribute("CEReactions") is not None and not getter:
9456 cgThings.append(
9457 CGGeneric(
9458 dedent(
9460 Maybe<AutoCEReaction> ceReaction;
9461 DocGroup* docGroup = self->GetDocGroup();
9462 if (docGroup) {
9463 ceReaction.emplace(docGroup->CustomElementReactionsStack(), cx);
9470 # If this is a method that was generated by a maplike/setlike
9471 # interface, use the maplike/setlike generator to fill in the body.
9472 # Otherwise, use CGCallGenerator to call the native method.
9473 if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod():
9474 if (
9475 idlNode.maplikeOrSetlikeOrIterable.isMaplike()
9476 or idlNode.maplikeOrSetlikeOrIterable.isSetlike()
9478 cgThings.append(
9479 CGMaplikeOrSetlikeMethodGenerator(
9480 descriptor,
9481 idlNode.maplikeOrSetlikeOrIterable,
9482 idlNode.identifier.name,
9485 else:
9486 cgThings.append(
9487 CGIterableMethodGenerator(
9488 descriptor,
9489 idlNode.identifier.name,
9490 self.getArgumentNames(),
9493 elif idlNode.isAttr() and idlNode.type.isObservableArray():
9494 assert setter
9495 cgThings.append(CGObservableArraySetterGenerator(descriptor, idlNode))
9496 else:
9497 if errorReportingLabel is None:
9498 context = GetLabelForErrorReporting(descriptor, idlNode, isConstructor)
9499 if getter:
9500 context = context + " getter"
9501 elif setter:
9502 context = context + " setter"
9503 # Callee expects a quoted string for the context if
9504 # there's a context.
9505 context = '"%s"' % context
9506 else:
9507 context = errorReportingLabel
9509 if idlNode.isMethod() and idlNode.getExtendedAttribute("WebExtensionStub"):
9511 nativeMethodName,
9512 argsPre,
9513 args,
9514 ] = self.processWebExtensionStubAttribute(cgThings)
9515 else:
9516 args = self.getArguments()
9518 cgThings.append(
9519 CGCallGenerator(
9520 self.needsErrorResult(),
9521 needsCallerType(idlNode),
9522 isChromeOnly(idlNode),
9523 args,
9524 argsPre + additionalArgsPre,
9525 returnType,
9526 self.extendedAttributes,
9527 descriptor,
9528 nativeMethodName,
9529 static,
9530 # We know our "self" must be being kept alive; otherwise we have
9531 # a serious problem. In common cases it's just an argument and
9532 # we're MOZ_CAN_RUN_SCRIPT, but in some cases it's on the stack
9533 # and being kept alive via references from JS.
9534 object="MOZ_KnownLive(self)",
9535 argsPost=argsPost,
9536 resultVar=resultVar,
9537 context=context,
9541 if useCounterName:
9542 # Generate a telemetry call for when [UseCounter] is used.
9543 windowCode = fill(
9545 SetUseCounter(obj, eUseCounter_${useCounterName});
9546 """,
9547 useCounterName=useCounterName,
9549 workerCode = fill(
9551 SetUseCounter(UseCounterWorker::${useCounterName});
9552 """,
9553 useCounterName=useCounterName,
9555 code = ""
9556 if idlNode.isExposedInWindow() and idlNode.isExposedInAnyWorker():
9557 code += fill(
9559 if (NS_IsMainThread()) {
9560 ${windowCode}
9561 } else {
9562 ${workerCode}
9564 """,
9565 windowCode=windowCode,
9566 workerCode=workerCode,
9568 elif idlNode.isExposedInWindow():
9569 code += windowCode
9570 elif idlNode.isExposedInAnyWorker():
9571 code += workerCode
9573 cgThings.append(CGGeneric(code))
9575 self.cgRoot = CGList(cgThings)
9577 def getArgumentNames(self):
9578 return ["arg" + str(i) for i in range(len(self.arguments))]
9580 def getArguments(self):
9581 return list(zip(self.arguments, self.getArgumentNames()))
9583 def processWebExtensionStubAttribute(self, cgThings):
9584 nativeMethodName = "CallWebExtMethod"
9585 stubNameSuffix = self.idlNode.getExtendedAttribute("WebExtensionStub")
9586 if isinstance(stubNameSuffix, list):
9587 nativeMethodName += stubNameSuffix[0]
9589 argsLength = len(self.getArguments())
9590 singleVariadicArg = argsLength == 1 and self.getArguments()[0][0].variadic
9592 # If the method signature does only include a single variadic arguments,
9593 # then `arg0` is already a Sequence of JS values and we can pass that
9594 # to the WebExtensions Stub method as is.
9595 if singleVariadicArg:
9596 argsPre = [
9597 "cx",
9598 'u"%s"_ns' % self.idlNode.identifier.name,
9599 "Constify(%s)" % "arg0",
9601 args = []
9602 return [nativeMethodName, argsPre, args]
9604 argsPre = [
9605 "cx",
9606 'u"%s"_ns' % self.idlNode.identifier.name,
9607 "Constify(%s)" % "args_sequence",
9609 args = []
9611 # Determine the maximum number of elements of the js values sequence argument,
9612 # skipping the last optional callback argument if any:
9614 # if this WebExtensions API method does expect a last optional callback argument,
9615 # then it is the callback parameter supported for chrome-compatibility
9616 # reasons, and we want it as a separate argument passed to the WebExtension
9617 # stub method and skip it from the js values sequence including all other
9618 # arguments.
9619 maxArgsSequenceLen = argsLength
9620 if argsLength > 0:
9621 lastArg = self.getArguments()[argsLength - 1]
9622 isCallback = lastArg[0].type.tag() == IDLType.Tags.callback
9623 if isCallback and lastArg[0].optional:
9624 argsPre.append(
9625 "MOZ_KnownLive(NonNullHelper(Constify(%s)))" % lastArg[1]
9627 maxArgsSequenceLen = argsLength - 1
9629 cgThings.append(
9630 CGGeneric(
9631 dedent(
9632 fill(
9634 // Collecting all args js values into the single sequence argument
9635 // passed to the webextensions stub method.
9637 // NOTE: The stub method will receive the original non-normalized js values,
9638 // but those arguments will still be normalized on the main thread by the
9639 // WebExtensions API request handler using the same JSONSchema defnition
9640 // used by the non-webIDL webextensions API bindings.
9641 AutoSequence<JS::Value> args_sequence;
9642 SequenceRooter<JS::Value> args_sequence_holder(cx, &args_sequence);
9644 // maximum number of arguments expected by the WebExtensions API method
9645 // excluding the last optional chrome-compatible callback argument (which
9646 // is being passed to the stub method as a separate additional argument).
9647 uint32_t maxArgsSequenceLen = ${maxArgsSequenceLen};
9649 uint32_t sequenceArgsLen = args.length() <= maxArgsSequenceLen ?
9650 args.length() : maxArgsSequenceLen;
9652 if (sequenceArgsLen > 0) {
9653 if (!args_sequence.SetCapacity(sequenceArgsLen, mozilla::fallible)) {
9654 JS_ReportOutOfMemory(cx);
9655 return false;
9657 for (uint32_t argIdx = 0; argIdx < sequenceArgsLen; ++argIdx) {
9658 // OK to do infallible append here, since we ensured capacity already.
9659 JS::Value& slot = *args_sequence.AppendElement();
9660 slot = args[argIdx];
9663 """,
9664 maxArgsSequenceLen=maxArgsSequenceLen,
9670 return [nativeMethodName, argsPre, args]
9672 def needsErrorResult(self):
9673 return "needsErrorResult" in self.extendedAttributes
9675 def wrap_return_value(self):
9676 wrapCode = ""
9678 returnsNewObject = memberReturnsNewObject(self.idlNode)
9679 if returnsNewObject and (
9680 self.returnType.isGeckoInterface() or self.returnType.isPromise()
9682 wrapCode += dedent(
9684 static_assert(!std::is_pointer_v<decltype(result)>,
9685 "NewObject implies that we need to keep the object alive with a strong reference.");
9689 if self.setSlot:
9690 # For attributes in slots, we want to do some
9691 # post-processing once we've wrapped them.
9692 successCode = "break;\n"
9693 else:
9694 successCode = None
9696 resultTemplateValues = {
9697 "jsvalRef": "args.rval()",
9698 "jsvalHandle": "args.rval()",
9699 "returnsNewObject": returnsNewObject,
9700 "isConstructorRetval": self.isConstructor,
9701 "successCode": successCode,
9702 # 'obj' in this dictionary is the thing whose compartment we are
9703 # trying to do the to-JS conversion in. We're going to put that
9704 # thing in a variable named "conversionScope" if setSlot is true.
9705 # Otherwise, just use "obj" for lack of anything better.
9706 "obj": "conversionScope" if self.setSlot else "obj",
9709 wrapCode += wrapForType(self.returnType, self.descriptor, resultTemplateValues)
9711 if self.setSlot:
9712 if self.idlNode.isStatic():
9713 raise TypeError(
9714 "Attribute %s.%s is static, so we don't have a useful slot "
9715 "to cache it in, because we don't have support for that on "
9716 "interface objects. See "
9717 "https://bugzilla.mozilla.org/show_bug.cgi?id=1363870"
9719 self.descriptor.interface.identifier.name,
9720 self.idlNode.identifier.name,
9724 # When using a slot on the Xray expando, we need to make sure that
9725 # our initial conversion to a JS::Value is done in the caller
9726 # compartment. When using a slot on our reflector, we want to do
9727 # the conversion in the compartment of that reflector (that is,
9728 # slotStorage). In both cases we want to make sure that we finally
9729 # set up args.rval() to be in the caller compartment. We also need
9730 # to make sure that the conversion steps happen inside a do/while
9731 # that they can break out of on success.
9733 # Of course we always have to wrap the value into the slotStorage
9734 # compartment before we store it in slotStorage.
9736 # postConversionSteps are the steps that run while we're still in
9737 # the compartment we do our conversion in but after we've finished
9738 # the initial conversion into args.rval().
9739 postConversionSteps = ""
9740 if self.idlNode.getExtendedAttribute("Frozen"):
9741 assert (
9742 self.idlNode.type.isSequence() or self.idlNode.type.isDictionary()
9744 freezeValue = CGGeneric(
9745 "JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());\n"
9746 "if (!JS_FreezeObject(cx, rvalObj)) {\n"
9747 " return false;\n"
9748 "}\n"
9750 if self.idlNode.type.nullable():
9751 freezeValue = CGIfWrapper(freezeValue, "args.rval().isObject()")
9752 postConversionSteps += freezeValue.define()
9754 # slotStorageSteps are steps that run once we have entered the
9755 # slotStorage compartment.
9756 if self.idlNode.getExtendedAttribute(
9757 "ReflectedHTMLAttributeReturningFrozenArray"
9759 storeInSlot = fill(
9761 array[${arrayIndex}] = storedVal;
9762 """,
9763 arrayIndex=reflectedHTMLAttributesArrayIndex(
9764 self.descriptor, self.idlNode
9767 else:
9768 storeInSlot = dedent(
9770 JS::SetReservedSlot(slotStorage, slotIndex, storedVal);
9774 slotStorageSteps = fill(
9776 // Make a copy so that we don't do unnecessary wrapping on args.rval().
9777 JS::Rooted<JS::Value> storedVal(cx, args.rval());
9778 if (!${maybeWrap}(cx, &storedVal)) {
9779 return false;
9781 $*{storeInSlot}
9782 """,
9783 maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type),
9784 storeInSlot=storeInSlot,
9787 checkForXray = mayUseXrayExpandoSlots(self.descriptor, self.idlNode)
9789 # For the case of Cached attributes, go ahead and preserve our
9790 # wrapper if needed. We need to do this because otherwise the
9791 # wrapper could get garbage-collected and the cached value would
9792 # suddenly disappear, but the whole premise of cached values is that
9793 # they never change without explicit action on someone's part. We
9794 # don't do this for StoreInSlot, since those get dealt with during
9795 # wrapper setup, and failure would involve us trying to clear an
9796 # already-preserved wrapper.
9797 if (
9798 self.idlNode.getExtendedAttribute("Cached")
9799 or self.idlNode.getExtendedAttribute(
9800 "ReflectedHTMLAttributeReturningFrozenArray"
9802 ) and self.descriptor.wrapperCache:
9803 preserveWrapper = dedent(
9805 PreserveWrapper(self);
9808 if checkForXray:
9809 preserveWrapper = fill(
9811 if (!isXray) {
9812 // In the Xray case we don't need to do this, because getting the
9813 // expando object already preserved our wrapper.
9814 $*{preserveWrapper}
9816 """,
9817 preserveWrapper=preserveWrapper,
9819 slotStorageSteps += preserveWrapper
9821 if checkForXray:
9822 # In the Xray case we use the current global as conversion
9823 # scope, as explained in the big compartment/conversion comment
9824 # above.
9825 conversionScope = "isXray ? JS::CurrentGlobalOrNull(cx) : slotStorage"
9826 else:
9827 conversionScope = "slotStorage"
9829 wrapCode = fill(
9832 JS::Rooted<JSObject*> conversionScope(cx, ${conversionScope});
9833 JSAutoRealm ar(cx, conversionScope);
9834 do { // block we break out of when done wrapping
9835 $*{wrapCode}
9836 } while (false);
9837 $*{postConversionSteps}
9839 { // And now store things in the realm of our slotStorage.
9840 JSAutoRealm ar(cx, slotStorage);
9841 $*{slotStorageSteps}
9843 // And now make sure args.rval() is in the caller realm.
9844 return ${maybeWrap}(cx, args.rval());
9845 """,
9846 conversionScope=conversionScope,
9847 wrapCode=wrapCode,
9848 postConversionSteps=postConversionSteps,
9849 slotStorageSteps=slotStorageSteps,
9850 maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type),
9852 return wrapCode
9854 def define(self):
9855 return self.cgRoot.define() + self.wrap_return_value()
9858 class CGSwitch(CGList):
9860 A class to generate code for a switch statement.
9862 Takes three constructor arguments: an expression, a list of cases,
9863 and an optional default.
9865 Each case is a CGCase. The default is a CGThing for the body of
9866 the default case, if any.
9869 def __init__(self, expression, cases, default=None):
9870 CGList.__init__(self, [CGIndenter(c) for c in cases])
9871 self.prepend(CGGeneric("switch (" + expression + ") {\n"))
9872 if default is not None:
9873 self.append(
9874 CGIndenter(
9875 CGWrapper(CGIndenter(default), pre="default: {\n", post="}\n")
9879 self.append(CGGeneric("}\n"))
9882 class CGCase(CGList):
9884 A class to generate code for a case statement.
9886 Takes three constructor arguments: an expression, a CGThing for
9887 the body (allowed to be None if there is no body), and an optional
9888 argument for whether add a break, add fallthrough annotation or add nothing
9889 (defaulting to add a break).
9892 ADD_BREAK = 0
9893 ADD_FALLTHROUGH = 1
9894 DONT_ADD_BREAK = 2
9896 def __init__(self, expression, body, breakOrFallthrough=ADD_BREAK):
9897 CGList.__init__(self, [])
9899 assert (
9900 breakOrFallthrough == CGCase.ADD_BREAK
9901 or breakOrFallthrough == CGCase.ADD_FALLTHROUGH
9902 or breakOrFallthrough == CGCase.DONT_ADD_BREAK
9905 self.append(CGGeneric("case " + expression + ": {\n"))
9906 bodyList = CGList([body])
9907 if breakOrFallthrough == CGCase.ADD_FALLTHROUGH:
9908 bodyList.append(CGGeneric("[[fallthrough]];\n"))
9909 elif breakOrFallthrough == CGCase.ADD_BREAK:
9910 bodyList.append(CGGeneric("break;\n"))
9911 self.append(CGIndenter(bodyList))
9912 self.append(CGGeneric("}\n"))
9915 class CGMethodCall(CGThing):
9917 A class to generate selection of a method signature from a set of
9918 signatures and generation of a call to that signature.
9921 def __init__(
9922 self, nativeMethodName, static, descriptor, method, isConstructor=False
9924 CGThing.__init__(self)
9926 methodName = GetLabelForErrorReporting(descriptor, method, isConstructor)
9927 argDesc = "argument %d"
9929 if method.getExtendedAttribute("UseCounter"):
9930 useCounterName = methodName.replace(".", "_").replace(" ", "_")
9931 else:
9932 useCounterName = None
9934 if method.isStatic():
9935 nativeType = descriptor.nativeType
9936 staticTypeOverride = PropertyDefiner.getStringAttr(
9937 method, "StaticClassOverride"
9939 if staticTypeOverride:
9940 nativeType = staticTypeOverride
9941 nativeMethodName = "%s::%s" % (nativeType, nativeMethodName)
9943 def requiredArgCount(signature):
9944 arguments = signature[1]
9945 if len(arguments) == 0:
9946 return 0
9947 requiredArgs = len(arguments)
9948 while requiredArgs and arguments[requiredArgs - 1].optional:
9949 requiredArgs -= 1
9950 return requiredArgs
9952 def getPerSignatureCall(signature, argConversionStartsAt=0):
9953 return CGPerSignatureCall(
9954 signature[0],
9955 signature[1],
9956 nativeMethodName,
9957 static,
9958 descriptor,
9959 method,
9960 argConversionStartsAt=argConversionStartsAt,
9961 isConstructor=isConstructor,
9962 useCounterName=useCounterName,
9965 signatures = method.signatures()
9966 if len(signatures) == 1:
9967 # Special case: we can just do a per-signature method call
9968 # here for our one signature and not worry about switching
9969 # on anything.
9970 signature = signatures[0]
9971 self.cgRoot = CGList([getPerSignatureCall(signature)])
9972 requiredArgs = requiredArgCount(signature)
9974 # Skip required arguments check for maplike/setlike interfaces, as
9975 # they can have arguments which are not passed, and are treated as
9976 # if undefined had been explicitly passed.
9977 if requiredArgs > 0 and not method.isMaplikeOrSetlikeOrIterableMethod():
9978 code = fill(
9980 if (!args.requireAtLeast(cx, "${methodName}", ${requiredArgs})) {
9981 return false;
9983 """,
9984 requiredArgs=requiredArgs,
9985 methodName=methodName,
9987 self.cgRoot.prepend(CGGeneric(code))
9988 return
9990 # Need to find the right overload
9991 maxArgCount = method.maxArgCount
9992 allowedArgCounts = method.allowedArgCounts
9994 argCountCases = []
9995 for argCountIdx, argCount in enumerate(allowedArgCounts):
9996 possibleSignatures = method.signaturesForArgCount(argCount)
9998 # Try to optimize away cases when the next argCount in the list
9999 # will have the same code as us; if it does, we can fall through to
10000 # that case.
10001 if argCountIdx + 1 < len(allowedArgCounts):
10002 nextPossibleSignatures = method.signaturesForArgCount(
10003 allowedArgCounts[argCountIdx + 1]
10005 else:
10006 nextPossibleSignatures = None
10007 if possibleSignatures == nextPossibleSignatures:
10008 # Same set of signatures means we better have the same
10009 # distinguishing index. So we can in fact just fall through to
10010 # the next case here.
10011 assert len(possibleSignatures) == 1 or (
10012 method.distinguishingIndexForArgCount(argCount)
10013 == method.distinguishingIndexForArgCount(
10014 allowedArgCounts[argCountIdx + 1]
10017 argCountCases.append(
10018 CGCase(str(argCount), None, CGCase.ADD_FALLTHROUGH)
10020 continue
10022 if len(possibleSignatures) == 1:
10023 # easy case!
10024 signature = possibleSignatures[0]
10025 argCountCases.append(
10026 CGCase(str(argCount), getPerSignatureCall(signature))
10028 continue
10030 distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
10032 def distinguishingArgument(signature):
10033 args = signature[1]
10034 if distinguishingIndex < len(args):
10035 return args[distinguishingIndex]
10036 assert args[-1].variadic
10037 return args[-1]
10039 def distinguishingType(signature):
10040 return distinguishingArgument(signature).type
10042 for sig in possibleSignatures:
10043 # We should not have "any" args at distinguishingIndex,
10044 # since we have multiple possible signatures remaining,
10045 # but "any" is never distinguishable from anything else.
10046 assert not distinguishingType(sig).isAny()
10047 # We can't handle unions at the distinguishing index.
10048 if distinguishingType(sig).isUnion():
10049 raise TypeError(
10050 "No support for unions as distinguishing "
10051 "arguments yet: %s" % distinguishingArgument(sig).location
10053 # We don't support variadics as the distinguishingArgument yet.
10054 # If you want to add support, consider this case:
10056 # undefined(long... foo);
10057 # undefined(long bar, Int32Array baz);
10059 # in which we have to convert argument 0 to long before picking
10060 # an overload... but all the variadic stuff needs to go into a
10061 # single array in case we pick that overload, so we have to have
10062 # machinery for converting argument 0 to long and then either
10063 # placing it in the variadic bit or not. Or something. We may
10064 # be able to loosen this restriction if the variadic arg is in
10065 # fact at distinguishingIndex, perhaps. Would need to
10066 # double-check.
10067 if distinguishingArgument(sig).variadic:
10068 raise TypeError(
10069 "No support for variadics as distinguishing "
10070 "arguments yet: %s" % distinguishingArgument(sig).location
10073 # Convert all our arguments up to the distinguishing index.
10074 # Doesn't matter which of the possible signatures we use, since
10075 # they all have the same types up to that point; just use
10076 # possibleSignatures[0]
10077 caseBody = [
10078 CGArgumentConverter(
10079 possibleSignatures[0][1][i],
10081 descriptor,
10082 argDesc % (i + 1),
10083 method,
10085 for i in range(0, distinguishingIndex)
10088 # Select the right overload from our set.
10089 distinguishingArg = "args[%d]" % distinguishingIndex
10091 def tryCall(
10092 signature, indent, isDefinitelyObject=False, isNullOrUndefined=False
10094 assert not isDefinitelyObject or not isNullOrUndefined
10095 assert isDefinitelyObject or isNullOrUndefined
10096 if isDefinitelyObject:
10097 failureCode = "break;\n"
10098 else:
10099 failureCode = None
10100 type = distinguishingType(signature)
10101 # The argument at index distinguishingIndex can't possibly be
10102 # unset here, because we've already checked that argc is large
10103 # enough that we can examine this argument. But note that we
10104 # still want to claim that optional arguments are optional, in
10105 # case undefined was passed in.
10106 argIsOptional = distinguishingArgument(signature).canHaveMissingValue()
10107 testCode = instantiateJSToNativeConversion(
10108 getJSToNativeConversionInfo(
10109 type,
10110 descriptor,
10111 failureCode=failureCode,
10112 isDefinitelyObject=isDefinitelyObject,
10113 isNullOrUndefined=isNullOrUndefined,
10114 isOptional=argIsOptional,
10115 sourceDescription=(argDesc % (distinguishingIndex + 1)),
10118 "declName": "arg%d" % distinguishingIndex,
10119 "holderName": ("arg%d" % distinguishingIndex) + "_holder",
10120 "val": distinguishingArg,
10121 "obj": "obj",
10122 "haveValue": "args.hasDefined(%d)" % distinguishingIndex,
10123 "passedToJSImpl": toStringBool(
10124 isJSImplementedDescriptor(descriptor)
10127 checkForValue=argIsOptional,
10129 caseBody.append(CGIndenter(testCode, indent))
10131 # If we got this far, we know we unwrapped to the right
10132 # C++ type, so just do the call. Start conversion with
10133 # distinguishingIndex + 1, since we already converted
10134 # distinguishingIndex.
10135 caseBody.append(
10136 CGIndenter(
10137 getPerSignatureCall(signature, distinguishingIndex + 1), indent
10141 def hasConditionalConversion(type):
10143 Return whether the argument conversion for this type will be
10144 conditional on the type of incoming JS value. For example, for
10145 interface types the conversion is conditional on the incoming
10146 value being isObject().
10148 For the types for which this returns false, we do not have to
10149 output extra isUndefined() or isNullOrUndefined() cases, because
10150 null/undefined values will just fall through into our
10151 unconditional conversion.
10153 if type.isString() or type.isEnum():
10154 return False
10155 if type.isBoolean():
10156 distinguishingTypes = (
10157 distinguishingType(s) for s in possibleSignatures
10159 return any(
10160 t.isString() or t.isEnum() or t.isNumeric()
10161 for t in distinguishingTypes
10163 if type.isNumeric():
10164 distinguishingTypes = (
10165 distinguishingType(s) for s in possibleSignatures
10167 return any(t.isString() or t.isEnum() for t in distinguishingTypes)
10168 return True
10170 def needsNullOrUndefinedCase(type):
10172 Return true if the type needs a special isNullOrUndefined() case
10174 return (
10175 type.nullable() and hasConditionalConversion(type)
10176 ) or type.isDictionary()
10178 # First check for undefined and optional distinguishing arguments
10179 # and output a special branch for that case. Note that we don't
10180 # use distinguishingArgument here because we actualy want to
10181 # exclude variadic arguments. Also note that we skip this check if
10182 # we plan to output a isNullOrUndefined() special case for this
10183 # argument anyway, since that will subsume our isUndefined() check.
10184 # This is safe, because there can be at most one nullable
10185 # distinguishing argument, so if we're it we'll definitely get
10186 # picked up by the nullable handling. Also, we can skip this check
10187 # if the argument has an unconditional conversion later on.
10188 undefSigs = [
10190 for s in possibleSignatures
10191 if distinguishingIndex < len(s[1])
10192 and s[1][distinguishingIndex].optional
10193 and hasConditionalConversion(s[1][distinguishingIndex].type)
10194 and not needsNullOrUndefinedCase(s[1][distinguishingIndex].type)
10196 # Can't have multiple signatures with an optional argument at the
10197 # same index.
10198 assert len(undefSigs) < 2
10199 if len(undefSigs) > 0:
10200 caseBody.append(
10201 CGGeneric("if (%s.isUndefined()) {\n" % distinguishingArg)
10203 tryCall(undefSigs[0], 2, isNullOrUndefined=True)
10204 caseBody.append(CGGeneric("}\n"))
10206 # Next, check for null or undefined. That means looking for
10207 # nullable arguments at the distinguishing index and outputting a
10208 # separate branch for them. But if the nullable argument has an
10209 # unconditional conversion, we don't need to do that. The reason
10210 # for that is that at most one argument at the distinguishing index
10211 # is nullable (since two nullable arguments are not
10212 # distinguishable), and null/undefined values will always fall
10213 # through to the unconditional conversion we have, if any, since
10214 # they will fail whatever the conditions on the input value are for
10215 # our other conversions.
10216 nullOrUndefSigs = [
10218 for s in possibleSignatures
10219 if needsNullOrUndefinedCase(distinguishingType(s))
10221 # Can't have multiple nullable types here
10222 assert len(nullOrUndefSigs) < 2
10223 if len(nullOrUndefSigs) > 0:
10224 caseBody.append(
10225 CGGeneric("if (%s.isNullOrUndefined()) {\n" % distinguishingArg)
10227 tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True)
10228 caseBody.append(CGGeneric("}\n"))
10230 # Now check for distinguishingArg being various kinds of objects.
10231 # The spec says to check for the following things in order:
10232 # 1) A platform object that's not a platform array object, being
10233 # passed to an interface or "object" arg.
10234 # 2) A callable object being passed to a callback or "object" arg.
10235 # 3) An iterable object being passed to a sequence arg.
10236 # 4) Any object being passed to a array or callback interface or
10237 # dictionary or "object" arg.
10239 # First grab all the overloads that have a non-callback interface
10240 # (which includes SpiderMonkey interfaces) at the distinguishing
10241 # index. We can also include the ones that have an "object" here,
10242 # since if those are present no other object-typed argument will
10243 # be.
10244 objectSigs = [
10246 for s in possibleSignatures
10247 if (
10248 distinguishingType(s).isObject()
10249 or distinguishingType(s).isNonCallbackInterface()
10253 # And all the overloads that take callbacks
10254 objectSigs.extend(
10255 s for s in possibleSignatures if distinguishingType(s).isCallback()
10258 # And all the overloads that take sequences
10259 objectSigs.extend(
10260 s for s in possibleSignatures if distinguishingType(s).isSequence()
10263 # Now append all the overloads that take a dictionary or callback
10264 # interface or record. There should be only one of these!
10265 genericObjectSigs = [
10267 for s in possibleSignatures
10268 if (
10269 distinguishingType(s).isDictionary()
10270 or distinguishingType(s).isRecord()
10271 or distinguishingType(s).isCallbackInterface()
10274 assert len(genericObjectSigs) <= 1
10275 objectSigs.extend(genericObjectSigs)
10277 # There might be more than one thing in objectSigs; we need to check
10278 # which ones we unwrap to.
10279 if len(objectSigs) > 0:
10280 # Here it's enough to guard on our argument being an object.
10281 # The code for unwrapping non-callback interfaces, spiderMonkey
10282 # interfaces, and sequences will just bail out and move
10283 # on to the next overload if the object fails to unwrap
10284 # correctly, while "object" accepts any object anyway. We
10285 # could even not do the isObject() check up front here, but in
10286 # cases where we have multiple object overloads it makes sense
10287 # to do it only once instead of for each overload. That will
10288 # also allow the unwrapping test to skip having to do codegen
10289 # for the null-or-undefined case, which we already handled
10290 # above.
10291 caseBody.append(CGGeneric("if (%s.isObject()) {\n" % distinguishingArg))
10292 for sig in objectSigs:
10293 caseBody.append(CGIndenter(CGGeneric("do {\n")))
10294 # Indent by 4, since we need to indent further
10295 # than our "do" statement
10296 tryCall(sig, 4, isDefinitelyObject=True)
10297 caseBody.append(CGIndenter(CGGeneric("} while (false);\n")))
10299 caseBody.append(CGGeneric("}\n"))
10301 # Now we only have to consider booleans, numerics, and strings. If
10302 # we only have one of them, then we can just output it. But if not,
10303 # then we need to output some of the cases conditionally: if we have
10304 # a string overload, then boolean and numeric are conditional, and
10305 # if not then boolean is conditional if we have a numeric overload.
10306 def findUniqueSignature(filterLambda):
10307 sigs = [s for s in possibleSignatures if filterLambda(s)]
10308 assert len(sigs) < 2
10309 if len(sigs) > 0:
10310 return sigs[0]
10311 return None
10313 stringSignature = findUniqueSignature(
10314 lambda s: (
10315 distinguishingType(s).isString() or distinguishingType(s).isEnum()
10318 numericSignature = findUniqueSignature(
10319 lambda s: distinguishingType(s).isNumeric()
10321 booleanSignature = findUniqueSignature(
10322 lambda s: distinguishingType(s).isBoolean()
10325 if stringSignature or numericSignature:
10326 booleanCondition = "%s.isBoolean()"
10327 else:
10328 booleanCondition = None
10330 if stringSignature:
10331 numericCondition = "%s.isNumber()"
10332 else:
10333 numericCondition = None
10335 def addCase(sig, condition):
10336 sigCode = getPerSignatureCall(sig, distinguishingIndex)
10337 if condition:
10338 sigCode = CGIfWrapper(sigCode, condition % distinguishingArg)
10339 caseBody.append(sigCode)
10341 if booleanSignature:
10342 addCase(booleanSignature, booleanCondition)
10343 if numericSignature:
10344 addCase(numericSignature, numericCondition)
10345 if stringSignature:
10346 addCase(stringSignature, None)
10348 if not booleanSignature and not numericSignature and not stringSignature:
10349 # Just throw; we have no idea what we're supposed to
10350 # do with this.
10351 caseBody.append(
10352 CGGeneric(
10353 'return cx.ThrowErrorMessage<MSG_OVERLOAD_RESOLUTION_FAILED>("%d", "%d");\n'
10354 % (distinguishingIndex + 1, argCount)
10358 argCountCases.append(CGCase(str(argCount), CGList(caseBody)))
10360 overloadCGThings = []
10361 overloadCGThings.append(
10362 CGGeneric(
10363 "unsigned argcount = std::min(args.length(), %du);\n" % maxArgCount
10366 overloadCGThings.append(
10367 CGSwitch(
10368 "argcount",
10369 argCountCases,
10370 CGGeneric(
10371 dedent(
10373 // Using nsPrintfCString here would require including that
10374 // header. Let's not worry about it.
10375 nsAutoCString argCountStr;
10376 argCountStr.AppendPrintf("%u", args.length());
10377 return cx.ThrowErrorMessage<MSG_INVALID_OVERLOAD_ARGCOUNT>(argCountStr.get());
10383 overloadCGThings.append(
10384 CGGeneric(
10385 'MOZ_CRASH("We have an always-returning default case");\n'
10386 "return false;\n"
10389 self.cgRoot = CGList(overloadCGThings)
10391 def define(self):
10392 return self.cgRoot.define()
10395 class CGGetterCall(CGPerSignatureCall):
10397 A class to generate a native object getter call for a particular IDL
10398 getter.
10401 def __init__(
10402 self,
10403 returnType,
10404 nativeMethodName,
10405 descriptor,
10406 attr,
10407 errorReportingLabel=None,
10408 argsPre=[],
10409 dontSetSlot=False,
10410 extendedAttributes=None,
10411 preConversionCode=None,
10413 self.preConversionCode = preConversionCode
10414 if attr.getExtendedAttribute("UseCounter"):
10415 useCounterName = "%s_%s_getter" % (
10416 descriptor.interface.identifier.name,
10417 attr.identifier.name,
10419 else:
10420 useCounterName = None
10421 if attr.isStatic():
10422 nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName)
10423 CGPerSignatureCall.__init__(
10424 self,
10425 returnType,
10427 nativeMethodName,
10428 attr.isStatic(),
10429 descriptor,
10430 attr,
10431 getter=True,
10432 useCounterName=useCounterName,
10433 dontSetSlot=dontSetSlot,
10434 extendedAttributes=extendedAttributes,
10435 errorReportingLabel=errorReportingLabel,
10436 additionalArgsPre=argsPre,
10439 def wrap_return_value(self):
10440 wrap = CGPerSignatureCall.wrap_return_value(self)
10441 if self.preConversionCode is not None:
10442 wrap = self.preConversionCode + wrap
10443 return wrap
10446 class FakeIdentifier:
10447 def __init__(self, name):
10448 self.name = name
10451 class FakeArgument:
10453 A class that quacks like an IDLArgument. This is used to make
10454 setters look like method calls or for special operations.
10457 def __init__(self, type, name="arg", allowTreatNonCallableAsNull=False):
10458 self.type = type
10459 self.optional = False
10460 self.variadic = False
10461 self.defaultValue = None
10462 self._allowTreatNonCallableAsNull = allowTreatNonCallableAsNull
10464 self.identifier = FakeIdentifier(name)
10466 def allowTreatNonCallableAsNull(self):
10467 return self._allowTreatNonCallableAsNull
10469 def canHaveMissingValue(self):
10470 return False
10473 class CGSetterCall(CGPerSignatureCall):
10475 A class to generate a native object setter call for a particular IDL
10476 setter.
10479 def __init__(
10480 self,
10481 argType,
10482 nativeMethodName,
10483 descriptor,
10484 attr,
10485 errorReportingLabel=None,
10486 argsPre=[],
10488 if attr.getExtendedAttribute("UseCounter"):
10489 useCounterName = "%s_%s_setter" % (
10490 descriptor.interface.identifier.name,
10491 attr.identifier.name,
10493 else:
10494 useCounterName = None
10495 if attr.isStatic():
10496 nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName)
10497 CGPerSignatureCall.__init__(
10498 self,
10499 None,
10500 [FakeArgument(argType, allowTreatNonCallableAsNull=True)],
10501 nativeMethodName,
10502 attr.isStatic(),
10503 descriptor,
10504 attr,
10505 setter=True,
10506 useCounterName=useCounterName,
10507 errorReportingLabel=errorReportingLabel,
10508 additionalArgsPre=argsPre,
10511 def wrap_return_value(self):
10512 attr = self.idlNode
10513 clearSlot = ""
10514 if self.descriptor.wrapperCache and attr.slotIndices is not None:
10515 if attr.getExtendedAttribute("StoreInSlot"):
10516 clearSlot = "%s(cx, self);\n" % MakeClearCachedValueNativeName(
10517 self.idlNode
10519 elif attr.getExtendedAttribute("Cached"):
10520 clearSlot = "%s(self);\n" % MakeClearCachedValueNativeName(self.idlNode)
10522 # We have no return value
10523 return "\n" "%s" "return true;\n" % clearSlot
10526 class CGAbstractBindingMethod(CGAbstractStaticMethod):
10528 Common class to generate some of our class hooks. This will generate the
10529 function declaration, get a reference to the JS object for our binding
10530 object (which might be an argument of the class hook or something we get
10531 from a JS::CallArgs), and unwrap into the right C++ type. Subclasses are
10532 expected to override the generate_code function to do the rest of the work.
10533 This function should return a CGThing which is already properly indented.
10535 getThisObj should be code for getting a JSObject* for the binding
10536 object. "" can be passed in if the binding object is already stored in
10537 'obj'.
10539 callArgs should be code for getting a JS::CallArgs into a variable
10540 called 'args'. This can be "" if there is already such a variable
10541 around or if the body does not need a JS::CallArgs.
10545 def __init__(
10546 self,
10547 descriptor,
10548 name,
10549 args,
10550 getThisObj,
10551 callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n",
10553 CGAbstractStaticMethod.__init__(
10554 self, descriptor, name, "bool", args, canRunScript=True
10557 # This can't ever happen, because we only use this for class hooks.
10558 self.unwrapFailureCode = fill(
10560 MOZ_CRASH("Unexpected object in '${name}' hook");
10561 return false;
10562 """,
10563 name=name,
10566 if getThisObj == "":
10567 self.getThisObj = None
10568 else:
10569 self.getThisObj = CGGeneric(
10570 "JS::Rooted<JSObject*> obj(cx, %s);\n" % getThisObj
10572 self.callArgs = callArgs
10574 def definition_body(self):
10575 body = self.callArgs
10576 if self.getThisObj is not None:
10577 body += self.getThisObj.define() + "\n"
10578 body += "%s* self;\n" % self.descriptor.nativeType
10579 body += dedent(
10581 JS::Rooted<JS::Value> rootSelf(cx, JS::ObjectValue(*obj));
10585 body += str(
10586 CastableObjectUnwrapper(
10587 self.descriptor, "rootSelf", "&rootSelf", "self", self.unwrapFailureCode
10591 return body + self.generate_code().define()
10593 def generate_code(self):
10594 assert False # Override me
10597 class CGAbstractStaticBindingMethod(CGAbstractStaticMethod):
10599 Common class to generate the JSNatives for all our static methods, getters
10600 and setters. This will generate the function declaration and unwrap the
10601 global object. Subclasses are expected to override the generate_code
10602 function to do the rest of the work. This function should return a
10603 CGThing which is already properly indented.
10606 def __init__(self, descriptor, name):
10607 CGAbstractStaticMethod.__init__(
10608 self, descriptor, name, "bool", JSNativeArguments(), canRunScript=True
10611 def definition_body(self):
10612 # Make sure that "obj" is in the same compartment as "cx", since we'll
10613 # later use it to wrap return values.
10614 unwrap = dedent(
10616 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
10617 JS::Rooted<JSObject*> obj(cx, &args.callee());
10621 return unwrap + self.generate_code().define()
10623 def generate_code(self):
10624 assert False # Override me
10627 def MakeNativeName(name):
10628 return name[0].upper() + IDLToCIdentifier(name[1:])
10631 def GetWebExposedName(idlObject, descriptor):
10632 if idlObject == descriptor.operations["Stringifier"]:
10633 return "toString"
10634 name = idlObject.identifier.name
10635 if name == "__namedsetter":
10636 return "named setter"
10637 if name == "__namedgetter":
10638 return "named getter"
10639 if name == "__indexedsetter":
10640 return "indexed setter"
10641 if name == "__indexedgetter":
10642 return "indexed getter"
10643 if name == "__legacycaller":
10644 return "legacy caller"
10645 return name
10648 def GetConstructorNameForReporting(descriptor, ctor):
10649 # Figure out the name of our constructor for reporting purposes.
10650 # For unnamed webidl constructors, identifier.name is "constructor" but
10651 # the name JS sees is the interface name; for legacy factory functions
10652 # identifier.name is the actual name.
10653 ctorName = ctor.identifier.name
10654 if ctorName == "constructor":
10655 return descriptor.interface.identifier.name
10656 return ctorName
10659 def GetLabelForErrorReporting(descriptor, idlObject, isConstructor):
10661 descriptor is the descriptor for the interface involved
10663 idlObject is the method (regular or static), attribute (regular or
10664 static), or constructor (named or not) involved.
10666 isConstructor is true if idlObject is a constructor and false otherwise.
10668 if isConstructor:
10669 return "%s constructor" % GetConstructorNameForReporting(descriptor, idlObject)
10671 namePrefix = descriptor.interface.identifier.name
10672 name = GetWebExposedName(idlObject, descriptor)
10673 if " " in name:
10674 # It's got a space already, so just space-separate.
10675 return "%s %s" % (namePrefix, name)
10677 return "%s.%s" % (namePrefix, name)
10680 class CGSpecializedMethod(CGAbstractStaticMethod):
10682 A class for generating the C++ code for a specialized method that the JIT
10683 can call with lower overhead.
10686 def __init__(self, descriptor, method):
10687 self.method = method
10688 name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name))
10689 args = [
10690 Argument("JSContext*", "cx"),
10691 Argument("JS::Handle<JSObject*>", "obj"),
10692 Argument("void*", "void_self"),
10693 Argument("const JSJitMethodCallArgs&", "args"),
10695 CGAbstractStaticMethod.__init__(
10696 self, descriptor, name, "bool", args, canRunScript=True
10699 def definition_body(self):
10700 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method)
10701 call = CGMethodCall(
10702 nativeName, self.method.isStatic(), self.descriptor, self.method
10703 ).define()
10704 prefix = ""
10705 if self.method.getExtendedAttribute("CrossOriginCallable"):
10706 for signature in self.method.signatures():
10707 # non-undefined signatures would require us to deal with remote proxies for the
10708 # return value here.
10709 if not signature[0].isUndefined():
10710 raise TypeError(
10711 "We don't support a method marked as CrossOriginCallable "
10712 "with non-undefined return type"
10714 prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
10715 prefix = fill(
10717 // CrossOriginThisPolicy::UnwrapThisObject stores a ${nativeType}::RemoteProxy in void_self
10718 // if obj is a proxy with a RemoteObjectProxy handler for the right type, or else it stores
10719 // a ${nativeType}. If we get here from the JIT (without going through UnwrapThisObject) we
10720 // know void_self contains a ${nativeType}; we don't have special cases in the JIT to deal
10721 // with remote object proxies.
10722 if (IsRemoteObjectProxy(obj, ${prototypeID})) {
10723 auto* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
10724 $*{call}
10726 """,
10727 prototypeID=prototypeID,
10728 nativeType=self.descriptor.nativeType,
10729 call=call,
10731 return prefix + fill(
10733 auto* self = static_cast<${nativeType}*>(void_self);
10734 $*{call}
10735 """,
10736 nativeType=self.descriptor.nativeType,
10737 call=call,
10740 def auto_profiler_label(self):
10741 interface_name = self.descriptor.interface.identifier.name
10742 method_name = self.method.identifier.name
10743 return fill(
10745 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
10746 "${interface_name}", "${method_name}", DOM, cx,
10747 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_METHOD) |
10748 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
10749 """,
10750 interface_name=interface_name,
10751 method_name=method_name,
10754 @staticmethod
10755 def should_have_method_description(descriptor, idlMethod):
10757 Returns whether the given IDL method (static, non-static, constructor)
10758 should have a method description declaration, for use in error
10759 reporting.
10761 # If a method has overloads, it needs a method description, because it
10762 # can throw MSG_INVALID_OVERLOAD_ARGCOUNT at the very least.
10763 if len(idlMethod.signatures()) != 1:
10764 return True
10766 # Methods with only one signature need a method description if one of
10767 # their args needs it.
10768 sig = idlMethod.signatures()[0]
10769 args = sig[1]
10770 return any(
10771 idlTypeNeedsCallContext(
10772 arg.type,
10773 descriptor,
10774 allowTreatNonCallableAsNull=arg.allowTreatNonCallableAsNull(),
10776 for arg in args
10779 @staticmethod
10780 def error_reporting_label_helper(descriptor, idlMethod, isConstructor):
10782 Returns the method description to use for error reporting for the given
10783 IDL method. Used to implement common error_reporting_label() functions
10784 across different classes.
10786 if not CGSpecializedMethod.should_have_method_description(
10787 descriptor, idlMethod
10789 return None
10790 return '"%s"' % GetLabelForErrorReporting(descriptor, idlMethod, isConstructor)
10792 def error_reporting_label(self):
10793 return CGSpecializedMethod.error_reporting_label_helper(
10794 self.descriptor, self.method, isConstructor=False
10797 @staticmethod
10798 def makeNativeName(descriptor, method):
10799 if method.underlyingAttr:
10800 return CGSpecializedGetterCommon.makeNativeName(
10801 descriptor, method.underlyingAttr
10803 name = method.identifier.name
10804 return MakeNativeName(descriptor.binaryNameFor(name, method.isStatic()))
10807 class CGMethodPromiseWrapper(CGAbstractStaticMethod):
10809 A class for generating a wrapper around another method that will
10810 convert exceptions to promises.
10813 def __init__(self, descriptor, methodToWrap):
10814 self.method = methodToWrap
10815 name = self.makeName(methodToWrap.name)
10816 args = list(methodToWrap.args)
10817 CGAbstractStaticMethod.__init__(
10818 self, descriptor, name, "bool", args, canRunScript=True
10821 def definition_body(self):
10822 return fill(
10824 bool ok = ${methodName}(${args});
10825 if (ok) {
10826 return true;
10828 return ConvertExceptionToPromise(cx, args.rval());
10829 """,
10830 methodName=self.method.name,
10831 args=", ".join(arg.name for arg in self.args),
10834 @staticmethod
10835 def makeName(methodName):
10836 return methodName + "_promiseWrapper"
10839 class CGDefaultToJSONMethod(CGSpecializedMethod):
10840 def __init__(self, descriptor, method):
10841 assert method.isDefaultToJSON()
10842 CGSpecializedMethod.__init__(self, descriptor, method)
10844 def definition_body(self):
10845 ret = fill(
10847 auto* self = static_cast<${nativeType}*>(void_self);
10848 JS::Rooted<JSObject*> result(cx, JS_NewPlainObject(cx));
10849 if (!result) {
10850 return false;
10852 """,
10853 nativeType=self.descriptor.nativeType,
10856 jsonDescriptors = [self.descriptor]
10857 interface = self.descriptor.interface.parent
10858 while interface:
10859 descriptor = self.descriptor.getDescriptor(interface.identifier.name)
10860 if descriptor.hasDefaultToJSON:
10861 jsonDescriptors.append(descriptor)
10862 interface = interface.parent
10864 # Iterate the array in reverse: oldest ancestor first
10865 for descriptor in jsonDescriptors[::-1]:
10866 ret += fill(
10868 if (!${parentclass}::CollectJSONAttributes(cx, obj, MOZ_KnownLive(self), result)) {
10869 return false;
10871 """,
10872 parentclass=toBindingNamespace(descriptor.name),
10874 ret += "args.rval().setObject(*result);\n" "return true;\n"
10875 return ret
10878 class CGLegacyCallHook(CGAbstractBindingMethod):
10880 Call hook for our object
10883 def __init__(self, descriptor):
10884 self._legacycaller = descriptor.operations["LegacyCaller"]
10885 # Our "self" is actually the callee in this case, not the thisval.
10886 CGAbstractBindingMethod.__init__(
10887 self,
10888 descriptor,
10889 LEGACYCALLER_HOOK_NAME,
10890 JSNativeArguments(),
10891 getThisObj="&args.callee()",
10894 def define(self):
10895 if not self._legacycaller:
10896 return ""
10897 return CGAbstractBindingMethod.define(self)
10899 def generate_code(self):
10900 name = self._legacycaller.identifier.name
10901 nativeName = MakeNativeName(self.descriptor.binaryNameFor(name, False))
10902 return CGMethodCall(nativeName, False, self.descriptor, self._legacycaller)
10904 def error_reporting_label(self):
10905 # Should act like methods.
10906 return CGSpecializedMethod.error_reporting_label_helper(
10907 self.descriptor, self._legacycaller, isConstructor=False
10911 class CGResolveHook(CGAbstractClassHook):
10913 Resolve hook for objects that have the NeedResolve extended attribute.
10916 def __init__(self, descriptor):
10917 assert descriptor.interface.getExtendedAttribute("NeedResolve")
10919 args = [
10920 Argument("JSContext*", "cx"),
10921 Argument("JS::Handle<JSObject*>", "obj"),
10922 Argument("JS::Handle<jsid>", "id"),
10923 Argument("bool*", "resolvedp"),
10925 CGAbstractClassHook.__init__(self, descriptor, RESOLVE_HOOK_NAME, "bool", args)
10927 def generate_code(self):
10928 return dedent(
10930 JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> desc(cx);
10931 if (!self->DoResolve(cx, obj, id, &desc)) {
10932 return false;
10934 if (desc.isNothing()) {
10935 return true;
10937 // If desc.value() is undefined, then the DoResolve call
10938 // has already defined it on the object. Don't try to also
10939 // define it.
10940 MOZ_ASSERT(desc->isDataDescriptor());
10941 if (!desc->value().isUndefined()) {
10942 JS::Rooted<JS::PropertyDescriptor> defineDesc(cx, *desc);
10943 defineDesc.setResolving(true);
10944 if (!JS_DefinePropertyById(cx, obj, id, defineDesc)) {
10945 return false;
10948 *resolvedp = true;
10949 return true;
10953 def definition_body(self):
10954 if self.descriptor.isGlobal():
10955 # Resolve standard classes
10956 prefix = dedent(
10958 if (!ResolveGlobal(cx, obj, id, resolvedp)) {
10959 return false;
10961 if (*resolvedp) {
10962 return true;
10967 else:
10968 prefix = ""
10969 return prefix + CGAbstractClassHook.definition_body(self)
10972 class CGMayResolveHook(CGAbstractStaticMethod):
10974 Resolve hook for objects that have the NeedResolve extended attribute.
10977 def __init__(self, descriptor):
10978 assert descriptor.interface.getExtendedAttribute("NeedResolve")
10980 args = [
10981 Argument("const JSAtomState&", "names"),
10982 Argument("jsid", "id"),
10983 Argument("JSObject*", "maybeObj"),
10985 CGAbstractStaticMethod.__init__(
10986 self, descriptor, MAY_RESOLVE_HOOK_NAME, "bool", args
10989 def definition_body(self):
10990 if self.descriptor.isGlobal():
10991 # Check whether this would resolve as a standard class.
10992 prefix = dedent(
10994 if (MayResolveGlobal(names, id, maybeObj)) {
10995 return true;
11000 else:
11001 prefix = ""
11002 return prefix + "return %s::MayResolve(id);\n" % self.descriptor.nativeType
11005 class CGEnumerateHook(CGAbstractBindingMethod):
11007 Enumerate hook for objects with custom hooks.
11010 def __init__(self, descriptor):
11011 assert descriptor.interface.getExtendedAttribute("NeedResolve")
11013 args = [
11014 Argument("JSContext*", "cx"),
11015 Argument("JS::Handle<JSObject*>", "obj"),
11016 Argument("JS::MutableHandleVector<jsid>", "properties"),
11017 Argument("bool", "enumerableOnly"),
11019 # Our "self" is actually the "obj" argument in this case, not the thisval.
11020 CGAbstractBindingMethod.__init__(
11021 self, descriptor, NEW_ENUMERATE_HOOK_NAME, args, getThisObj="", callArgs=""
11024 def generate_code(self):
11025 return CGGeneric(
11026 dedent(
11028 FastErrorResult rv;
11029 self->GetOwnPropertyNames(cx, properties, enumerableOnly, rv);
11030 if (rv.MaybeSetPendingException(cx)) {
11031 return false;
11033 return true;
11038 def definition_body(self):
11039 if self.descriptor.isGlobal():
11040 # Enumerate standard classes
11041 prefix = dedent(
11043 if (!EnumerateGlobal(cx, obj, properties, enumerableOnly)) {
11044 return false;
11049 else:
11050 prefix = ""
11051 return prefix + CGAbstractBindingMethod.definition_body(self)
11054 class CppKeywords:
11056 A class for checking if method names declared in webidl
11057 are not in conflict with C++ keywords.
11060 keywords = frozenset(
11062 "alignas",
11063 "alignof",
11064 "and",
11065 "and_eq",
11066 "asm",
11067 "assert",
11068 "auto",
11069 "bitand",
11070 "bitor",
11071 "bool",
11072 "break",
11073 "case",
11074 "catch",
11075 "char",
11076 "char16_t",
11077 "char32_t",
11078 "class",
11079 "compl",
11080 "const",
11081 "constexpr",
11082 "const_cast",
11083 "continue",
11084 "decltype",
11085 "default",
11086 "delete",
11087 "do",
11088 "double",
11089 "dynamic_cast",
11090 "else",
11091 "enum",
11092 "explicit",
11093 "export",
11094 "extern",
11095 "false",
11096 "final",
11097 "float",
11098 "for",
11099 "friend",
11100 "goto",
11101 "if",
11102 "inline",
11103 "int",
11104 "long",
11105 "mutable",
11106 "namespace",
11107 "new",
11108 "noexcept",
11109 "not",
11110 "not_eq",
11111 "nullptr",
11112 "operator",
11113 "or",
11114 "or_eq",
11115 "override",
11116 "private",
11117 "protected",
11118 "public",
11119 "register",
11120 "reinterpret_cast",
11121 "return",
11122 "short",
11123 "signed",
11124 "sizeof",
11125 "static",
11126 "static_assert",
11127 "static_cast",
11128 "struct",
11129 "switch",
11130 "template",
11131 "this",
11132 "thread_local",
11133 "throw",
11134 "true",
11135 "try",
11136 "typedef",
11137 "typeid",
11138 "typename",
11139 "union",
11140 "unsigned",
11141 "using",
11142 "virtual",
11143 "void",
11144 "volatile",
11145 "wchar_t",
11146 "while",
11147 "xor",
11148 "xor_eq",
11152 @staticmethod
11153 def checkMethodName(name):
11154 # Double '_' because 'assert' and '_assert' cannot be used in MS2013 compiler.
11155 # Bug 964892 and bug 963560.
11156 if name in CppKeywords.keywords:
11157 name = "_" + name + "_"
11158 return name
11161 class CGStaticMethod(CGAbstractStaticBindingMethod):
11163 A class for generating the C++ code for an IDL static method.
11166 def __init__(self, descriptor, method):
11167 self.method = method
11168 name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name))
11169 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
11171 def generate_code(self):
11172 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method)
11173 return CGMethodCall(nativeName, True, self.descriptor, self.method)
11175 def auto_profiler_label(self):
11176 interface_name = self.descriptor.interface.identifier.name
11177 method_name = self.method.identifier.name
11178 return fill(
11180 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11181 "${interface_name}", "${method_name}", DOM, cx,
11182 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_METHOD) |
11183 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11184 """,
11185 interface_name=interface_name,
11186 method_name=method_name,
11189 def error_reporting_label(self):
11190 return CGSpecializedMethod.error_reporting_label_helper(
11191 self.descriptor, self.method, isConstructor=False
11195 class CGSpecializedGetterCommon(CGAbstractStaticMethod):
11197 A class for generating the code for a specialized attribute getter
11198 that the JIT can call with lower overhead.
11201 def __init__(
11202 self,
11203 descriptor,
11204 name,
11205 nativeName,
11206 attr,
11207 args,
11208 errorReportingLabel=None,
11209 additionalArg=None,
11211 self.nativeName = nativeName
11212 self.errorReportingLabel = errorReportingLabel
11213 self.additionalArgs = [] if additionalArg is None else [additionalArg]
11214 # StoreInSlot attributes have their getters called from Wrap(). We
11215 # really hope they can't run script, and don't want to annotate Wrap()
11216 # methods as doing that anyway, so let's not annotate them as
11217 # MOZ_CAN_RUN_SCRIPT.
11218 CGAbstractStaticMethod.__init__(
11219 self,
11220 descriptor,
11221 name,
11222 "bool",
11223 args + self.additionalArgs,
11224 canRunScript=not attr.getExtendedAttribute("StoreInSlot"),
11227 def definition_body(self):
11228 prefix = fill(
11230 auto* self = static_cast<${nativeType}*>(void_self);
11231 """,
11232 nativeType=self.descriptor.nativeType,
11235 if self.attr.isMaplikeOrSetlikeAttr():
11236 assert not self.attr.getExtendedAttribute("CrossOriginReadable")
11237 # If the interface is maplike/setlike, there will be one getter
11238 # method for the size property of the backing object. Due to having
11239 # to unpack the backing object from the slot, this requires its own
11240 # generator.
11241 return prefix + getMaplikeOrSetlikeSizeGetterBody(
11242 self.descriptor, self.attr
11245 if self.attr.type.isObservableArray():
11246 assert not self.attr.getExtendedAttribute("CrossOriginReadable")
11247 # If the attribute is observableArray, due to having to unpack the
11248 # backing object from the slot, this requires its own generator.
11249 return prefix + getObservableArrayGetterBody(self.descriptor, self.attr)
11251 if self.nativeName is None:
11252 nativeName = CGSpecializedGetterCommon.makeNativeName(
11253 self.descriptor, self.attr
11255 else:
11256 nativeName = self.nativeName
11258 type = self.attr.type
11259 if self.attr.getExtendedAttribute("CrossOriginReadable"):
11260 remoteType = type
11261 extendedAttributes = self.descriptor.getExtendedAttributes(
11262 self.attr, getter=True
11264 if (
11265 remoteType.isGeckoInterface()
11266 and not remoteType.unroll().inner.isExternal()
11267 and remoteType.unroll().inner.getExtendedAttribute("ChromeOnly") is None
11269 # We'll use a JSObject. It might make more sense to use remoteType's
11270 # RemoteProxy, but it's not easy to construct a type for that from here.
11271 remoteType = BuiltinTypes[IDLBuiltinType.Types.object]
11272 if "needsErrorResult" not in extendedAttributes:
11273 extendedAttributes.append("needsErrorResult")
11274 prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
11275 prefix = (
11276 fill(
11278 if (IsRemoteObjectProxy(obj, ${prototypeID})) {
11279 ${nativeType}::RemoteProxy* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
11280 $*{call}
11282 """,
11283 prototypeID=prototypeID,
11284 nativeType=self.descriptor.nativeType,
11285 call=CGGetterCall(
11286 remoteType,
11287 nativeName,
11288 self.descriptor,
11289 self.attr,
11290 self.errorReportingLabel,
11291 argsPre=[a.name for a in self.additionalArgs],
11292 dontSetSlot=True,
11293 extendedAttributes=extendedAttributes,
11294 ).define(),
11296 + prefix
11299 argsPre = [a.name for a in self.additionalArgs]
11300 maybeReturnCachedVal = None
11301 if self.attr.slotIndices is not None:
11302 # We're going to store this return value in a slot on some object,
11303 # to cache it. The question is, which object? For dictionary and
11304 # sequence return values, we want to use a slot on the Xray expando
11305 # if we're called via Xrays, and a slot on our reflector otherwise.
11306 # On the other hand, when dealing with some interface types
11307 # (e.g. window.document) we want to avoid calling the getter more
11308 # than once. In the case of window.document, it's because the
11309 # getter can start returning null, which would get hidden in the
11310 # non-Xray case by the fact that it's [StoreOnSlot], so the cached
11311 # version is always around.
11313 # The upshot is that we use the reflector slot for any getter whose
11314 # type is a gecko interface, whether we're called via Xrays or not.
11315 # Since [Cached] and [StoreInSlot] cannot be used with "NewObject",
11316 # we know that in the interface type case the returned object is
11317 # wrappercached. So creating Xrays to it is reasonable.
11318 if mayUseXrayExpandoSlots(self.descriptor, self.attr):
11319 prefix += dedent(
11321 // Have to either root across the getter call or reget after.
11322 bool isXray;
11323 JS::Rooted<JSObject*> slotStorage(cx, GetCachedSlotStorageObject(cx, obj, &isXray));
11324 if (!slotStorage) {
11325 return false;
11329 else:
11330 prefix += dedent(
11332 // Have to either root across the getter call or reget after.
11333 JS::Rooted<JSObject*> slotStorage(cx, js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false));
11334 MOZ_ASSERT(IsDOMObject(slotStorage));
11338 if self.attr.getExtendedAttribute(
11339 "ReflectedHTMLAttributeReturningFrozenArray"
11341 argsPre.append("hasCachedValue ? &useCachedValue : nullptr")
11342 isXray = (
11343 "isXray"
11344 if mayUseXrayExpandoSlots(self.descriptor, self.attr)
11345 else "false"
11347 prefix += fill(
11349 auto& array = ReflectedHTMLAttributeSlots::GetOrCreate(slotStorage, ${isXray});
11350 JS::Rooted<JS::Value> cachedVal(cx, array[${arrayIndex}]);
11351 bool hasCachedValue = !cachedVal.isUndefined();
11352 bool useCachedValue = false;
11353 """,
11354 isXray=isXray,
11355 arrayIndex=reflectedHTMLAttributesArrayIndex(
11356 self.descriptor, self.attr
11359 maybeReturnCachedVal = fill(
11361 MOZ_ASSERT_IF(useCachedValue, hasCachedValue);
11362 if (hasCachedValue && useCachedValue) {
11363 args.rval().set(cachedVal);
11364 // The cached value is in the compartment of slotStorage,
11365 // so wrap into the caller compartment as needed.
11366 return ${maybeWrap}(cx, args.rval());
11369 ${clearCachedValue}(self);
11371 """,
11372 maybeWrap=getMaybeWrapValueFuncForType(self.attr.type),
11373 clearCachedValue=MakeClearCachedValueNativeName(self.attr),
11375 else:
11376 if mayUseXrayExpandoSlots(self.descriptor, self.attr):
11377 prefix += fill(
11379 const size_t slotIndex = isXray ? ${xraySlotIndex} : ${slotIndex};
11380 """,
11381 xraySlotIndex=memberXrayExpandoReservedSlot(
11382 self.attr, self.descriptor
11384 slotIndex=memberReservedSlot(self.attr, self.descriptor),
11386 else:
11387 prefix += fill(
11389 const size_t slotIndex = ${slotIndex};
11390 """,
11391 slotIndex=memberReservedSlot(self.attr, self.descriptor),
11393 prefix += fill(
11395 MOZ_ASSERT(slotIndex < JSCLASS_RESERVED_SLOTS(JS::GetClass(slotStorage)));
11397 // Scope for cachedVal
11398 JS::Value cachedVal = JS::GetReservedSlot(slotStorage, slotIndex);
11399 if (!cachedVal.isUndefined()) {
11400 args.rval().set(cachedVal);
11401 // The cached value is in the compartment of slotStorage,
11402 // so wrap into the caller compartment as needed.
11403 return ${maybeWrap}(cx, args.rval());
11407 """,
11408 maybeWrap=getMaybeWrapValueFuncForType(self.attr.type),
11411 return (
11412 prefix
11413 + CGGetterCall(
11414 type,
11415 nativeName,
11416 self.descriptor,
11417 self.attr,
11418 self.errorReportingLabel,
11419 argsPre=argsPre,
11420 preConversionCode=maybeReturnCachedVal,
11421 ).define()
11424 def auto_profiler_label(self, profilerLabel=None):
11425 if profilerLabel is None:
11426 profilerLabel = '"' + self.attr.identifier.name + '"'
11427 interface_name = self.descriptor.interface.identifier.name
11428 return fill(
11430 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11431 "${interface_name}", ${attr_name}, DOM, cx,
11432 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER) |
11433 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11434 """,
11435 interface_name=interface_name,
11436 attr_name=profilerLabel,
11439 def error_reporting_label(self):
11440 # Getters never need a BindingCallContext.
11441 return None
11443 @staticmethod
11444 def makeNativeName(descriptor, attr):
11445 name = attr.identifier.name
11446 nativeName = MakeNativeName(descriptor.binaryNameFor(name, attr.isStatic()))
11447 _, resultOutParam, _, _, _ = getRetvalDeclarationForType(attr.type, descriptor)
11448 extendedAttrs = descriptor.getExtendedAttributes(attr, getter=True)
11449 canFail = "needsErrorResult" in extendedAttrs or "canOOM" in extendedAttrs
11450 if resultOutParam or attr.type.nullable() or canFail:
11451 nativeName = "Get" + nativeName
11452 return nativeName
11455 class CGSpecializedGetter(CGSpecializedGetterCommon):
11457 A class for generating the code for a specialized attribute getter
11458 that the JIT can call with lower overhead.
11461 def __init__(self, descriptor, attr):
11462 self.attr = attr
11463 name = "get_" + IDLToCIdentifier(attr.identifier.name)
11464 args = [
11465 Argument("JSContext*", "cx"),
11466 Argument("JS::Handle<JSObject*>", "obj"),
11467 Argument("void*", "void_self"),
11468 Argument("JSJitGetterCallArgs", "args"),
11470 CGSpecializedGetterCommon.__init__(self, descriptor, name, None, attr, args)
11473 class CGTemplateForSpecializedGetter(CGSpecializedGetterCommon):
11475 A class for generating the code for a specialized attribute getter
11476 that can be used as the common getter that templated attribute
11477 getters can forward to.
11480 def __init__(self, descriptor, template):
11481 self.attr = template.attr
11482 self.attrNameString = template.attrNameString
11483 args = [
11484 Argument("JSContext*", "cx"),
11485 Argument("JS::Handle<JSObject*>", "obj"),
11486 Argument("void*", "void_self"),
11487 Argument("JSJitGetterCallArgs", "args"),
11489 errorDescription = (
11490 'ErrorDescriptionFor<ErrorFor::getter>{ "%s", attrName }'
11491 % descriptor.interface.identifier.name
11493 CGSpecializedGetterCommon.__init__(
11494 self,
11495 descriptor,
11496 template.getter,
11497 template.getter,
11498 self.attr,
11499 args,
11500 errorReportingLabel=errorDescription,
11501 additionalArg=Argument(template.argument.type, template.argument.name),
11504 def auto_profiler_label(self):
11505 return (
11506 fill(
11508 const char* attrName = ${attrNameString};
11509 """,
11510 attrNameString=self.attrNameString,
11512 + CGSpecializedGetterCommon.auto_profiler_label(self, "attrName")
11516 class CGSpecializedTemplatedGetter(CGAbstractStaticMethod):
11518 A class for generating the code for a specialized templated attribute
11519 getter that forwards to a common template getter.
11522 def __init__(self, descriptor, attr, template, additionalArg):
11523 self.attr = attr
11524 self.template = template
11525 self.additionalArg = additionalArg
11526 name = "get_" + IDLToCIdentifier(attr.identifier.name)
11527 args = [
11528 Argument("JSContext*", "cx"),
11529 Argument("JS::Handle<JSObject*>", "obj"),
11530 Argument("void*", "void_self"),
11531 Argument("JSJitGetterCallArgs", "args"),
11533 assert not attr.getExtendedAttribute("StoreInSlot")
11534 CGAbstractStaticMethod.__init__(
11535 self,
11536 descriptor,
11537 name,
11538 "bool",
11539 args,
11540 canRunScript=True,
11543 def definition_body(self):
11544 if self.additionalArg is None:
11545 additionalArg = self.attr.identifier.name
11546 else:
11547 additionalArg = self.additionalArg
11549 return fill(
11551 return ${namespace}::${getter}(cx, obj, void_self, args, ${additionalArg});
11552 """,
11553 namespace=toBindingNamespace(
11554 self.template.descriptor.interface.identifier.name
11556 getter=self.template.getter,
11557 additionalArg=additionalArg,
11561 class CGGetterPromiseWrapper(CGAbstractStaticMethod):
11563 A class for generating a wrapper around another getter that will
11564 convert exceptions to promises.
11567 def __init__(self, descriptor, getterToWrap):
11568 self.getter = getterToWrap
11569 name = self.makeName(getterToWrap.name)
11570 args = list(getterToWrap.args)
11571 CGAbstractStaticMethod.__init__(
11572 self, descriptor, name, "bool", args, canRunScript=True
11575 def definition_body(self):
11576 return fill(
11578 bool ok = ${getterName}(${args});
11579 if (ok) {
11580 return true;
11582 return ConvertExceptionToPromise(cx, args.rval());
11583 """,
11584 getterName=self.getter.name,
11585 args=", ".join(arg.name for arg in self.args),
11588 @staticmethod
11589 def makeName(getterName):
11590 return getterName + "_promiseWrapper"
11593 class CGStaticGetter(CGAbstractStaticBindingMethod):
11595 A class for generating the C++ code for an IDL static attribute getter.
11598 def __init__(self, descriptor, attr):
11599 self.attr = attr
11600 name = "get_" + IDLToCIdentifier(attr.identifier.name)
11601 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
11603 def generate_code(self):
11604 nativeName = CGSpecializedGetterCommon.makeNativeName(
11605 self.descriptor, self.attr
11607 return CGGetterCall(self.attr.type, nativeName, self.descriptor, self.attr)
11609 def auto_profiler_label(self):
11610 interface_name = self.descriptor.interface.identifier.name
11611 attr_name = self.attr.identifier.name
11612 return fill(
11614 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11615 "${interface_name}", "${attr_name}", DOM, cx,
11616 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER) |
11617 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11618 """,
11619 interface_name=interface_name,
11620 attr_name=attr_name,
11623 def error_reporting_label(self):
11624 # Getters never need a BindingCallContext.
11625 return None
11628 class CGSpecializedSetterCommon(CGAbstractStaticMethod):
11630 A class for generating the code for a specialized attribute setter
11631 that the JIT can call with lower overhead.
11634 def __init__(
11635 self,
11636 descriptor,
11637 name,
11638 nativeName,
11639 attr,
11640 args,
11641 errorReportingLabel=None,
11642 additionalArg=None,
11644 self.nativeName = nativeName
11645 self.errorReportingLabel = errorReportingLabel
11646 self.additionalArgs = [] if additionalArg is None else [additionalArg]
11647 CGAbstractStaticMethod.__init__(
11648 self,
11649 descriptor,
11650 name,
11651 "bool",
11652 args + self.additionalArgs,
11653 canRunScript=True,
11656 def definition_body(self):
11657 type = self.attr.type
11658 call = CGSetterCall(
11659 type,
11660 self.nativeName,
11661 self.descriptor,
11662 self.attr,
11663 self.errorReportingLabel,
11664 [a.name for a in self.additionalArgs],
11665 ).define()
11666 prefix = ""
11667 if self.attr.getExtendedAttribute("CrossOriginWritable"):
11668 if type.isGeckoInterface() and not type.unroll().inner.isExternal():
11669 # a setter taking a Gecko interface would require us to deal with remote
11670 # proxies for the value here.
11671 raise TypeError(
11672 "We don't support the setter of %s marked as "
11673 "CrossOriginWritable because it takes a Gecko interface "
11674 "as the value",
11675 self.attr.identifier.name,
11677 prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
11678 prefix = fill(
11680 if (IsRemoteObjectProxy(obj, ${prototypeID})) {
11681 auto* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
11682 $*{call}
11684 """,
11685 prototypeID=prototypeID,
11686 nativeType=self.descriptor.nativeType,
11687 call=call,
11690 return prefix + fill(
11692 auto* self = static_cast<${nativeType}*>(void_self);
11693 $*{call}
11694 """,
11695 nativeType=self.descriptor.nativeType,
11696 call=call,
11699 def auto_profiler_label(self, profilerLabel=None):
11700 interface_name = self.descriptor.interface.identifier.name
11701 if profilerLabel is None:
11702 profilerLabel = '"' + self.attr.identifier.name + '"'
11703 return fill(
11705 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11706 "${interface_name}", ${attr_name}, DOM, cx,
11707 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER) |
11708 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11709 """,
11710 interface_name=interface_name,
11711 attr_name=profilerLabel,
11714 @staticmethod
11715 def error_reporting_label_helper(descriptor, attr):
11716 # Setters need a BindingCallContext if the type of the attribute needs
11717 # one.
11718 if not idlTypeNeedsCallContext(
11719 attr.type, descriptor, allowTreatNonCallableAsNull=True
11721 return None
11722 return '"%s"' % (
11723 GetLabelForErrorReporting(descriptor, attr, isConstructor=False) + " setter"
11726 def error_reporting_label(self):
11727 errorReportingLabel = CGSpecializedSetterCommon.error_reporting_label_helper(
11728 self.descriptor, self.attr
11730 if errorReportingLabel is None:
11731 return None
11732 if self.errorReportingLabel:
11733 return self.errorReportingLabel
11734 return errorReportingLabel
11736 @staticmethod
11737 def makeNativeName(descriptor, attr):
11738 name = attr.identifier.name
11739 return "Set" + MakeNativeName(descriptor.binaryNameFor(name, attr.isStatic()))
11742 class CGSpecializedSetter(CGSpecializedSetterCommon):
11744 A class for generating the code for a specialized attribute setter
11745 that the JIT can call with lower overhead.
11748 def __init__(self, descriptor, attr):
11749 self.attr = attr
11750 name = "set_" + IDLToCIdentifier(attr.identifier.name)
11751 args = [
11752 Argument("JSContext*", "cx"),
11753 Argument("JS::Handle<JSObject*>", "obj"),
11754 Argument("void*", "void_self"),
11755 Argument("JSJitSetterCallArgs", "args"),
11757 CGSpecializedSetterCommon.__init__(
11758 self,
11759 descriptor,
11760 name,
11761 CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
11762 attr,
11763 args,
11767 class CGTemplateForSpecializedSetter(CGSpecializedSetterCommon):
11769 A class for generating the code for a specialized attribute setter
11770 that can be used as the common setter that templated attribute
11771 setters can forward to.
11774 def __init__(self, descriptor, template):
11775 self.attr = template.attr
11776 self.attrNameString = template.attrNameString
11777 args = [
11778 Argument("JSContext*", "cx"),
11779 Argument("JS::Handle<JSObject*>", "obj"),
11780 Argument("void*", "void_self"),
11781 Argument("JSJitSetterCallArgs", "args"),
11783 errorDescription = (
11784 'ErrorDescriptionFor<ErrorFor::setter>{ "%s", attrName }'
11785 % descriptor.interface.identifier.name
11787 CGSpecializedSetterCommon.__init__(
11788 self,
11789 descriptor,
11790 template.setter,
11791 template.setter,
11792 self.attr,
11793 args,
11794 errorReportingLabel=errorDescription,
11795 additionalArg=Argument(template.argument.type, template.argument.name),
11798 def auto_profiler_label(self):
11799 return (
11800 fill(
11802 const char* attrName = ${attrNameString};
11803 """,
11804 attrNameString=self.attrNameString,
11806 + CGSpecializedSetterCommon.auto_profiler_label(self, "attrName")
11810 class CGSpecializedTemplatedSetter(CGAbstractStaticMethod):
11812 A class for generating the code for a specialized templated attribute
11813 setter that forwards to a common template setter.
11816 def __init__(self, descriptor, attr, template, additionalArg):
11817 self.attr = attr
11818 self.template = template
11819 self.additionalArg = additionalArg
11820 name = "set_" + IDLToCIdentifier(attr.identifier.name)
11821 args = [
11822 Argument("JSContext*", "cx"),
11823 Argument("JS::Handle<JSObject*>", "obj"),
11824 Argument("void*", "void_self"),
11825 Argument("JSJitSetterCallArgs", "args"),
11827 CGAbstractStaticMethod.__init__(
11828 self, descriptor, name, "bool", args, canRunScript=True
11831 def definition_body(self):
11832 additionalArgs = []
11833 if self.additionalArg is None:
11834 additionalArgs.append(self.attr.identifier.name)
11835 else:
11836 additionalArgs.append(self.additionalArg)
11838 return fill(
11840 return ${namespace}::${setter}(cx, obj, void_self, args, ${additionalArgs});
11841 """,
11842 namespace=toBindingNamespace(
11843 self.template.descriptor.interface.identifier.name
11845 setter=self.template.setter,
11846 additionalArgs=", ".join(additionalArgs),
11850 class CGStaticSetter(CGAbstractStaticBindingMethod):
11852 A class for generating the C++ code for an IDL static attribute setter.
11855 def __init__(self, descriptor, attr):
11856 self.attr = attr
11857 name = "set_" + IDLToCIdentifier(attr.identifier.name)
11858 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
11860 def generate_code(self):
11861 nativeName = CGSpecializedSetterCommon.makeNativeName(
11862 self.descriptor, self.attr
11864 checkForArg = CGGeneric(
11865 fill(
11867 if (!args.requireAtLeast(cx, "${name} setter", 1)) {
11868 return false;
11870 """,
11871 name=self.attr.identifier.name,
11874 call = CGSetterCall(self.attr.type, nativeName, self.descriptor, self.attr)
11875 return CGList([checkForArg, call])
11877 def auto_profiler_label(self):
11878 interface_name = self.descriptor.interface.identifier.name
11879 attr_name = self.attr.identifier.name
11880 return fill(
11882 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11883 "${interface_name}", "${attr_name}", DOM, cx,
11884 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER) |
11885 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11886 """,
11887 interface_name=interface_name,
11888 attr_name=attr_name,
11891 def error_reporting_label(self):
11892 return CGSpecializedSetterCommon.error_reporting_label_helper(
11893 self.descriptor, self.attr
11897 class CGSpecializedForwardingSetter(CGSpecializedSetter):
11899 A class for generating the code for a specialized attribute setter with
11900 PutForwards that the JIT can call with lower overhead.
11903 def __init__(self, descriptor, attr):
11904 CGSpecializedSetter.__init__(self, descriptor, attr)
11906 def definition_body(self):
11907 attrName = self.attr.identifier.name
11908 forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0]
11909 # JS_GetProperty and JS_SetProperty can only deal with ASCII
11910 assert all(ord(c) < 128 for c in attrName)
11911 assert all(ord(c) < 128 for c in forwardToAttrName)
11912 return fill(
11914 JS::Rooted<JS::Value> v(cx);
11915 if (!JS_GetProperty(cx, obj, "${attr}", &v)) {
11916 return false;
11919 if (!v.isObject()) {
11920 return cx.ThrowErrorMessage<MSG_NOT_OBJECT>("${interface}.${attr}");
11923 JS::Rooted<JSObject*> targetObj(cx, &v.toObject());
11924 return JS_SetProperty(cx, targetObj, "${forwardToAttrName}", args[0]);
11925 """,
11926 attr=attrName,
11927 interface=self.descriptor.interface.identifier.name,
11928 forwardToAttrName=forwardToAttrName,
11931 def error_reporting_label(self):
11932 # We always need to be able to throw.
11933 return '"%s"' % (
11934 GetLabelForErrorReporting(self.descriptor, self.attr, isConstructor=False)
11935 + " setter"
11939 class CGSpecializedReplaceableSetter(CGSpecializedSetter):
11941 A class for generating the code for a specialized attribute setter with
11942 Replaceable that the JIT can call with lower overhead.
11945 def __init__(self, descriptor, attr):
11946 CGSpecializedSetter.__init__(self, descriptor, attr)
11948 def definition_body(self):
11949 attrName = self.attr.identifier.name
11950 # JS_DefineProperty can only deal with ASCII
11951 assert all(ord(c) < 128 for c in attrName)
11952 return (
11953 'return JS_DefineProperty(cx, obj, "%s", args[0], JSPROP_ENUMERATE);\n'
11954 % attrName
11957 def error_reporting_label(self):
11958 # We never throw directly.
11959 return None
11962 class CGSpecializedLenientSetter(CGSpecializedSetter):
11964 A class for generating the code for a specialized attribute setter with
11965 LenientSetter that the JIT can call with lower overhead.
11968 def __init__(self, descriptor, attr):
11969 CGSpecializedSetter.__init__(self, descriptor, attr)
11971 def definition_body(self):
11972 attrName = self.attr.identifier.name
11973 # JS_DefineProperty can only deal with ASCII
11974 assert all(ord(c) < 128 for c in attrName)
11975 return dedent(
11977 DeprecationWarning(cx, obj, DeprecatedOperations::eLenientSetter);
11978 return true;
11982 def error_reporting_label(self):
11983 # We never throw; that's the whole point.
11984 return None
11987 def memberReturnsNewObject(member):
11988 return member.getExtendedAttribute("NewObject") is not None
11991 class CGMemberJITInfo(CGThing):
11993 A class for generating the JITInfo for a property that points to
11994 our specialized getter and setter.
11997 def __init__(self, descriptor, member):
11998 self.member = member
11999 self.descriptor = descriptor
12001 def declare(self):
12002 return ""
12004 def defineJitInfo(
12005 self,
12006 infoName,
12007 opName,
12008 opType,
12009 infallible,
12010 movable,
12011 eliminatable,
12012 aliasSet,
12013 alwaysInSlot,
12014 lazilyInSlot,
12015 slotIndex,
12016 returnTypes,
12017 args,
12020 aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit.
12022 args is None if we don't want to output argTypes for some
12023 reason (e.g. we have overloads or we're not a method) and
12024 otherwise an iterable of the arguments for this method.
12026 assert (
12027 not movable or aliasSet != "AliasEverything"
12028 ) # Can't move write-aliasing things
12029 assert (
12030 not alwaysInSlot or movable
12031 ) # Things always in slots had better be movable
12032 assert (
12033 not eliminatable or aliasSet != "AliasEverything"
12034 ) # Can't eliminate write-aliasing things
12035 assert (
12036 not alwaysInSlot or eliminatable
12037 ) # Things always in slots had better be eliminatable
12039 def jitInfoInitializer(isTypedMethod):
12040 initializer = fill(
12043 { ${opName} },
12044 { prototypes::id::${name} },
12045 { PrototypeTraits<prototypes::id::${name}>::Depth },
12046 JSJitInfo::${opType},
12047 JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */
12048 ${returnType}, /* returnType. Not relevant for setters. */
12049 ${isInfallible}, /* isInfallible. False in setters. */
12050 ${isMovable}, /* isMovable. Not relevant for setters. */
12051 ${isEliminatable}, /* isEliminatable. Not relevant for setters. */
12052 ${isAlwaysInSlot}, /* isAlwaysInSlot. Only relevant for getters. */
12053 ${isLazilyCachedInSlot}, /* isLazilyCachedInSlot. Only relevant for getters. */
12054 ${isTypedMethod}, /* isTypedMethod. Only relevant for methods. */
12055 ${slotIndex} /* Reserved slot index, if we're stored in a slot, else 0. */
12057 """,
12058 opName=opName,
12059 name=self.descriptor.name,
12060 opType=opType,
12061 aliasSet=aliasSet,
12062 returnType=functools.reduce(
12063 CGMemberJITInfo.getSingleReturnType, returnTypes, ""
12065 isInfallible=toStringBool(infallible),
12066 isMovable=toStringBool(movable),
12067 isEliminatable=toStringBool(eliminatable),
12068 isAlwaysInSlot=toStringBool(alwaysInSlot),
12069 isLazilyCachedInSlot=toStringBool(lazilyInSlot),
12070 isTypedMethod=toStringBool(isTypedMethod),
12071 slotIndex="0" if slotIndex is None else slotIndex,
12073 return initializer.rstrip()
12075 if slotIndex is not None:
12076 slotAssert = fill(
12078 static_assert(${slotIndex} <= JSJitInfo::maxSlotIndex, "We won't fit");
12079 static_assert(${slotIndex} < ${classReservedSlots}, "There is no slot for us");
12080 """,
12081 slotIndex=slotIndex,
12082 classReservedSlots=INSTANCE_RESERVED_SLOTS
12083 + self.descriptor.interface.totalMembersInSlots,
12085 else:
12086 slotAssert = ""
12087 if args is not None:
12088 argTypes = "%s_argTypes" % infoName
12089 args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args]
12090 args.append("JSJitInfo::ArgTypeListEnd")
12091 argTypesDecl = "static const JSJitInfo::ArgType %s[] = { %s };\n" % (
12092 argTypes,
12093 ", ".join(args),
12095 return fill(
12097 $*{argTypesDecl}
12098 static const JSTypedMethodJitInfo ${infoName} = {
12099 ${jitInfo},
12100 ${argTypes}
12102 $*{slotAssert}
12103 """,
12104 argTypesDecl=argTypesDecl,
12105 infoName=infoName,
12106 jitInfo=indent(jitInfoInitializer(True)),
12107 argTypes=argTypes,
12108 slotAssert=slotAssert,
12111 # Unexposed things are meant to be used from C++ directly, so we make
12112 # their jitinfo non-static. That way C++ can get at it.
12113 if self.member.getExtendedAttribute("Unexposed"):
12114 storageClass = "extern"
12115 else:
12116 storageClass = "static"
12118 return fill(
12120 ${storageClass} const JSJitInfo ${infoName} = ${jitInfo};
12121 $*{slotAssert}
12122 """,
12123 storageClass=storageClass,
12124 infoName=infoName,
12125 jitInfo=jitInfoInitializer(False),
12126 slotAssert=slotAssert,
12129 def define(self):
12130 if self.member.isAttr():
12131 getterinfo = "%s_getterinfo" % IDLToCIdentifier(self.member.identifier.name)
12132 name = IDLToCIdentifier(self.member.identifier.name)
12133 if self.member.type.isPromise():
12134 name = CGGetterPromiseWrapper.makeName(name)
12135 getter = "get_%s" % name
12136 extendedAttrs = self.descriptor.getExtendedAttributes(
12137 self.member, getter=True
12139 getterinfal = "needsErrorResult" not in extendedAttrs
12141 # At this point getterinfal is true if our getter either can't throw
12142 # at all, or can only throw OOM. In both cases, it's safe to move,
12143 # or dead-code-eliminate, the getter, because throwing OOM is not
12144 # semantically meaningful, so code can't rely on it happening. Note
12145 # that this makes the behavior consistent for OOM thrown from the
12146 # getter itself and OOM thrown from the to-JS conversion of the
12147 # return value (see the "canOOM" and "infallibleForMember" checks
12148 # below).
12149 movable = self.mayBeMovable() and getterinfal
12150 eliminatable = self.mayBeEliminatable() and getterinfal
12151 aliasSet = self.aliasSet()
12153 # Now we have to set getterinfal to whether we can _really_ ever
12154 # throw, from the point of view of the JS engine.
12155 getterinfal = (
12156 getterinfal
12157 and "canOOM" not in extendedAttrs
12158 and infallibleForMember(self.member, self.member.type, self.descriptor)
12160 isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot")
12162 if self.member.slotIndices is not None:
12163 assert (
12164 isAlwaysInSlot
12165 or self.member.getExtendedAttribute("Cached")
12166 or self.member.getExtendedAttribute(
12167 "ReflectedHTMLAttributeReturningFrozenArray"
12169 or self.member.type.isObservableArray()
12171 isLazilyCachedInSlot = (
12172 not isAlwaysInSlot
12173 and not self.member.getExtendedAttribute(
12174 "ReflectedHTMLAttributeReturningFrozenArray"
12177 slotIndex = memberReservedSlot(self.member, self.descriptor)
12178 # We'll statically assert that this is not too big in
12179 # CGUpdateMemberSlotsMethod, in the case when
12180 # isAlwaysInSlot is true.
12181 else:
12182 isLazilyCachedInSlot = False
12183 slotIndex = None
12185 result = self.defineJitInfo(
12186 getterinfo,
12187 getter,
12188 "Getter",
12189 getterinfal,
12190 movable,
12191 eliminatable,
12192 aliasSet,
12193 isAlwaysInSlot,
12194 isLazilyCachedInSlot,
12195 slotIndex,
12196 [self.member.type],
12197 None,
12199 if (
12200 not self.member.readonly
12201 or self.member.getExtendedAttribute("PutForwards") is not None
12202 or self.member.getExtendedAttribute("Replaceable") is not None
12203 or self.member.getExtendedAttribute("LegacyLenientSetter") is not None
12205 setterinfo = "%s_setterinfo" % IDLToCIdentifier(
12206 self.member.identifier.name
12208 # Actually a JSJitSetterOp, but JSJitGetterOp is first in the
12209 # union.
12210 setter = "(JSJitGetterOp)set_%s" % IDLToCIdentifier(
12211 self.member.identifier.name
12213 # Setters are always fallible, since they have to do a typed unwrap.
12214 result += self.defineJitInfo(
12215 setterinfo,
12216 setter,
12217 "Setter",
12218 False,
12219 False,
12220 False,
12221 "AliasEverything",
12222 False,
12223 False,
12224 None,
12225 [BuiltinTypes[IDLBuiltinType.Types.undefined]],
12226 None,
12228 return result
12229 if self.member.isMethod():
12230 methodinfo = "%s_methodinfo" % IDLToCIdentifier(self.member.identifier.name)
12231 name = CppKeywords.checkMethodName(
12232 IDLToCIdentifier(self.member.identifier.name)
12234 if self.member.returnsPromise():
12235 name = CGMethodPromiseWrapper.makeName(name)
12236 # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union.
12237 method = "(JSJitGetterOp)%s" % name
12239 # Methods are infallible if they are infallible, have no arguments
12240 # to unwrap, and have a return type that's infallible to wrap up for
12241 # return.
12242 sigs = self.member.signatures()
12243 if len(sigs) != 1:
12244 # Don't handle overloading. If there's more than one signature,
12245 # one of them must take arguments.
12246 methodInfal = False
12247 args = None
12248 movable = False
12249 eliminatable = False
12250 else:
12251 sig = sigs[0]
12252 # For methods that affect nothing, it's OK to set movable to our
12253 # notion of infallible on the C++ side, without considering
12254 # argument conversions, since argument conversions that can
12255 # reliably throw would be effectful anyway and the jit doesn't
12256 # move effectful things.
12257 extendedAttrs = self.descriptor.getExtendedAttributes(self.member)
12258 hasInfallibleImpl = "needsErrorResult" not in extendedAttrs
12259 # At this point hasInfallibleImpl is true if our method either
12260 # can't throw at all, or can only throw OOM. In both cases, it
12261 # may be safe to move, or dead-code-eliminate, the method,
12262 # because throwing OOM is not semantically meaningful, so code
12263 # can't rely on it happening. Note that this makes the behavior
12264 # consistent for OOM thrown from the method itself and OOM
12265 # thrown from the to-JS conversion of the return value (see the
12266 # "canOOM" and "infallibleForMember" checks below).
12267 movable = self.mayBeMovable() and hasInfallibleImpl
12268 eliminatable = self.mayBeEliminatable() and hasInfallibleImpl
12269 # XXXbz can we move the smarts about fallibility due to arg
12270 # conversions into the JIT, using our new args stuff?
12271 if len(sig[1]) != 0 or not infallibleForMember(
12272 self.member, sig[0], self.descriptor
12274 # We have arguments or our return-value boxing can fail
12275 methodInfal = False
12276 else:
12277 methodInfal = hasInfallibleImpl and "canOOM" not in extendedAttrs
12278 # For now, only bother to output args if we're side-effect-free.
12279 if self.member.affects == "Nothing":
12280 args = sig[1]
12281 else:
12282 args = None
12284 aliasSet = self.aliasSet()
12285 result = self.defineJitInfo(
12286 methodinfo,
12287 method,
12288 "Method",
12289 methodInfal,
12290 movable,
12291 eliminatable,
12292 aliasSet,
12293 False,
12294 False,
12295 None,
12296 [s[0] for s in sigs],
12297 args,
12299 return result
12300 raise TypeError("Illegal member type to CGPropertyJITInfo")
12302 def mayBeMovable(self):
12304 Returns whether this attribute or method may be movable, just
12305 based on Affects/DependsOn annotations.
12307 affects = self.member.affects
12308 dependsOn = self.member.dependsOn
12309 assert affects in IDLInterfaceMember.AffectsValues
12310 assert dependsOn in IDLInterfaceMember.DependsOnValues
12311 # Things that are DependsOn=DeviceState are not movable, because we
12312 # don't want them coalesced with each other or loop-hoisted, since
12313 # their return value can change even if nothing is going on from our
12314 # point of view.
12315 return affects == "Nothing" and (
12316 dependsOn != "Everything" and dependsOn != "DeviceState"
12319 def mayBeEliminatable(self):
12321 Returns whether this attribute or method may be eliminatable, just
12322 based on Affects/DependsOn annotations.
12324 # dependsOn shouldn't affect this decision at all, except in jitinfo we
12325 # have no way to express "Depends on everything, affects nothing",
12326 # because we only have three alias set values: AliasNone ("depends on
12327 # nothing, affects nothing"), AliasDOMSets ("depends on DOM sets,
12328 # affects nothing"), AliasEverything ("depends on everything, affects
12329 # everything"). So the [Affects=Nothing, DependsOn=Everything] case
12330 # gets encoded as AliasEverything and defineJitInfo asserts that if our
12331 # alias state is AliasEverything then we're not eliminatable (because it
12332 # thinks we might have side-effects at that point). Bug 1155796 is
12333 # tracking possible solutions for this.
12334 affects = self.member.affects
12335 dependsOn = self.member.dependsOn
12336 assert affects in IDLInterfaceMember.AffectsValues
12337 assert dependsOn in IDLInterfaceMember.DependsOnValues
12338 return affects == "Nothing" and dependsOn != "Everything"
12340 def aliasSet(self):
12342 Returns the alias set to store in the jitinfo. This may not be the
12343 effective alias set the JIT uses, depending on whether we have enough
12344 information about our args to allow the JIT to prove that effectful
12345 argument conversions won't happen.
12347 dependsOn = self.member.dependsOn
12348 assert dependsOn in IDLInterfaceMember.DependsOnValues
12350 if dependsOn == "Nothing" or dependsOn == "DeviceState":
12351 assert self.member.affects == "Nothing"
12352 return "AliasNone"
12354 if dependsOn == "DOMState":
12355 assert self.member.affects == "Nothing"
12356 return "AliasDOMSets"
12358 return "AliasEverything"
12360 @staticmethod
12361 def getJSReturnTypeTag(t):
12362 if t.nullable():
12363 # Sometimes it might return null, sometimes not
12364 return "JSVAL_TYPE_UNKNOWN"
12365 if t.isUndefined():
12366 # No return, every time
12367 return "JSVAL_TYPE_UNDEFINED"
12368 if t.isSequence():
12369 return "JSVAL_TYPE_OBJECT"
12370 if t.isRecord():
12371 return "JSVAL_TYPE_OBJECT"
12372 if t.isPromise():
12373 return "JSVAL_TYPE_OBJECT"
12374 if t.isGeckoInterface():
12375 return "JSVAL_TYPE_OBJECT"
12376 if t.isString():
12377 return "JSVAL_TYPE_STRING"
12378 if t.isEnum():
12379 return "JSVAL_TYPE_STRING"
12380 if t.isCallback():
12381 return "JSVAL_TYPE_OBJECT"
12382 if t.isAny():
12383 # The whole point is to return various stuff
12384 return "JSVAL_TYPE_UNKNOWN"
12385 if t.isObject():
12386 return "JSVAL_TYPE_OBJECT"
12387 if t.isSpiderMonkeyInterface():
12388 return "JSVAL_TYPE_OBJECT"
12389 if t.isUnion():
12390 u = t.unroll()
12391 if u.hasNullableType:
12392 # Might be null or not
12393 return "JSVAL_TYPE_UNKNOWN"
12394 return functools.reduce(
12395 CGMemberJITInfo.getSingleReturnType, u.flatMemberTypes, ""
12397 if t.isDictionary():
12398 return "JSVAL_TYPE_OBJECT"
12399 if t.isObservableArray():
12400 return "JSVAL_TYPE_OBJECT"
12401 if not t.isPrimitive():
12402 raise TypeError("No idea what type " + str(t) + " is.")
12403 tag = t.tag()
12404 if tag == IDLType.Tags.bool:
12405 return "JSVAL_TYPE_BOOLEAN"
12406 if tag in [
12407 IDLType.Tags.int8,
12408 IDLType.Tags.uint8,
12409 IDLType.Tags.int16,
12410 IDLType.Tags.uint16,
12411 IDLType.Tags.int32,
12413 return "JSVAL_TYPE_INT32"
12414 if tag in [
12415 IDLType.Tags.int64,
12416 IDLType.Tags.uint64,
12417 IDLType.Tags.unrestricted_float,
12418 IDLType.Tags.float,
12419 IDLType.Tags.unrestricted_double,
12420 IDLType.Tags.double,
12422 # These all use JS_NumberValue, which can return int or double.
12423 # But TI treats "double" as meaning "int or double", so we're
12424 # good to return JSVAL_TYPE_DOUBLE here.
12425 return "JSVAL_TYPE_DOUBLE"
12426 if tag != IDLType.Tags.uint32:
12427 raise TypeError("No idea what type " + str(t) + " is.")
12428 # uint32 is sometimes int and sometimes double.
12429 return "JSVAL_TYPE_DOUBLE"
12431 @staticmethod
12432 def getSingleReturnType(existingType, t):
12433 type = CGMemberJITInfo.getJSReturnTypeTag(t)
12434 if existingType == "":
12435 # First element of the list; just return its type
12436 return type
12438 if type == existingType:
12439 return existingType
12440 if (type == "JSVAL_TYPE_DOUBLE" and existingType == "JSVAL_TYPE_INT32") or (
12441 existingType == "JSVAL_TYPE_DOUBLE" and type == "JSVAL_TYPE_INT32"
12443 # Promote INT32 to DOUBLE as needed
12444 return "JSVAL_TYPE_DOUBLE"
12445 # Different types
12446 return "JSVAL_TYPE_UNKNOWN"
12448 @staticmethod
12449 def getJSArgType(t):
12450 assert not t.isUndefined()
12451 if t.nullable():
12452 # Sometimes it might return null, sometimes not
12453 return (
12454 "JSJitInfo::ArgType(JSJitInfo::Null | %s)"
12455 % CGMemberJITInfo.getJSArgType(t.inner)
12457 if t.isSequence():
12458 return "JSJitInfo::Object"
12459 if t.isPromise():
12460 return "JSJitInfo::Object"
12461 if t.isGeckoInterface():
12462 return "JSJitInfo::Object"
12463 if t.isString():
12464 return "JSJitInfo::String"
12465 if t.isEnum():
12466 return "JSJitInfo::String"
12467 if t.isCallback():
12468 return "JSJitInfo::Object"
12469 if t.isAny():
12470 # The whole point is to return various stuff
12471 return "JSJitInfo::Any"
12472 if t.isObject():
12473 return "JSJitInfo::Object"
12474 if t.isSpiderMonkeyInterface():
12475 return "JSJitInfo::Object"
12476 if t.isUnion():
12477 u = t.unroll()
12478 type = "JSJitInfo::Null" if u.hasNullableType else ""
12479 return "JSJitInfo::ArgType(%s)" % functools.reduce(
12480 CGMemberJITInfo.getSingleArgType, u.flatMemberTypes, type
12482 if t.isDictionary():
12483 return "JSJitInfo::Object"
12484 if not t.isPrimitive():
12485 raise TypeError("No idea what type " + str(t) + " is.")
12486 tag = t.tag()
12487 if tag == IDLType.Tags.bool:
12488 return "JSJitInfo::Boolean"
12489 if tag in [
12490 IDLType.Tags.int8,
12491 IDLType.Tags.uint8,
12492 IDLType.Tags.int16,
12493 IDLType.Tags.uint16,
12494 IDLType.Tags.int32,
12496 return "JSJitInfo::Integer"
12497 if tag in [
12498 IDLType.Tags.int64,
12499 IDLType.Tags.uint64,
12500 IDLType.Tags.unrestricted_float,
12501 IDLType.Tags.float,
12502 IDLType.Tags.unrestricted_double,
12503 IDLType.Tags.double,
12505 # These all use JS_NumberValue, which can return int or double.
12506 # But TI treats "double" as meaning "int or double", so we're
12507 # good to return JSVAL_TYPE_DOUBLE here.
12508 return "JSJitInfo::Double"
12509 if tag != IDLType.Tags.uint32:
12510 raise TypeError("No idea what type " + str(t) + " is.")
12511 # uint32 is sometimes int and sometimes double.
12512 return "JSJitInfo::Double"
12514 @staticmethod
12515 def getSingleArgType(existingType, t):
12516 type = CGMemberJITInfo.getJSArgType(t)
12517 if existingType == "":
12518 # First element of the list; just return its type
12519 return type
12521 if type == existingType:
12522 return existingType
12523 return "%s | %s" % (existingType, type)
12526 class CGStaticMethodJitinfo(CGGeneric):
12528 A class for generating the JITInfo for a promise-returning static method.
12531 def __init__(self, method):
12532 CGGeneric.__init__(
12533 self,
12534 "\n"
12535 "static const JSJitInfo %s_methodinfo = {\n"
12536 " { (JSJitGetterOp)%s },\n"
12537 " { prototypes::id::_ID_Count }, { 0 }, JSJitInfo::StaticMethod,\n"
12538 " JSJitInfo::AliasEverything, JSVAL_TYPE_OBJECT, false, false,\n"
12539 " false, false, 0\n"
12540 "};\n"
12542 IDLToCIdentifier(method.identifier.name),
12543 CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name)),
12548 def getEnumValueName(value):
12549 # Some enum values can be empty strings. Others might have weird
12550 # characters in them. Deal with the former by returning "_empty",
12551 # deal with possible name collisions from that by throwing if the
12552 # enum value is actually "_empty", and throw on any value
12553 # containing non-ASCII chars for now. Replace all chars other than
12554 # [0-9A-Za-z_] with '_'.
12555 if re.match("[^\x20-\x7E]", value):
12556 raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters')
12557 if re.match("^[0-9]", value):
12558 value = "_" + value
12559 value = re.sub(r"[^0-9A-Za-z_]", "_", value)
12560 if re.match("^_[A-Z]|__", value):
12561 raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec')
12562 if value == "_empty":
12563 raise SyntaxError('"_empty" is not an IDL enum value we support yet')
12564 if value == "":
12565 return "_empty"
12566 return MakeNativeName(value)
12569 class CGEnumToJSValue(CGAbstractMethod):
12570 def __init__(self, enum):
12571 enumType = enum.identifier.name
12572 self.stringsArray = "binding_detail::EnumStrings<" + enumType + ">::Values"
12573 CGAbstractMethod.__init__(
12574 self,
12575 None,
12576 "ToJSValue",
12577 "bool",
12579 Argument("JSContext*", "aCx"),
12580 Argument(enumType, "aArgument"),
12581 Argument("JS::MutableHandle<JS::Value>", "aValue"),
12585 def definition_body(self):
12586 return fill(
12588 MOZ_ASSERT(uint32_t(aArgument) < std::size(${strings}));
12589 JSString* resultStr =
12590 JS_NewStringCopyN(aCx, ${strings}[uint32_t(aArgument)].BeginReading(),
12591 ${strings}[uint32_t(aArgument)].Length());
12592 if (!resultStr) {
12593 return false;
12595 aValue.setString(resultStr);
12596 return true;
12597 """,
12598 strings=self.stringsArray,
12602 class CGEnum(CGThing):
12603 def __init__(self, enum):
12604 CGThing.__init__(self)
12605 self.enum = enum
12606 strings = CGNamespace(
12607 "binding_detail",
12608 CGGeneric(
12609 declare=fill(
12611 template <> struct EnumStrings<${name}> {
12612 static constexpr nsLiteralCString Values[${count}] {
12613 $*{entries}
12616 """,
12617 name=self.enum.identifier.name,
12618 count=self.nEnumStrings(),
12619 entries="".join('"%s"_ns,\n' % val for val in self.enum.values()),
12621 define=fill(
12623 constexpr nsLiteralCString EnumStrings<${name}>::Values[${count}];
12624 """,
12625 name=self.enum.identifier.name,
12626 count=self.nEnumStrings(),
12630 toJSValue = CGEnumToJSValue(enum)
12631 self.cgThings = CGList([strings, toJSValue], "\n")
12633 def nEnumStrings(self):
12634 return len(self.enum.values())
12636 @staticmethod
12637 def underlyingType(enum):
12638 count = len(enum.values())
12639 if count <= 256:
12640 return "uint8_t"
12641 if count <= 65536:
12642 return "uint16_t"
12643 raise ValueError("Enum " + enum.identifier.name + " has more than 65536 values")
12645 def declare(self):
12646 decl = fill(
12648 enum class ${name} : ${ty} {
12649 $*{enums}
12651 """,
12652 name=self.enum.identifier.name,
12653 ty=CGEnum.underlyingType(self.enum),
12654 enums=",\n".join(map(getEnumValueName, self.enum.values())) + ",\n",
12657 return decl + "\n" + self.cgThings.declare()
12659 def define(self):
12660 return self.cgThings.define()
12662 def deps(self):
12663 return self.enum.getDeps()
12666 class CGMaxContiguousEnumValue(CGThing):
12667 def __init__(self, enum):
12668 CGThing.__init__(self)
12669 self.enum = enum
12671 def declare(self):
12672 enumValues = self.enum.values()
12673 return fill(
12675 template <>
12676 struct MaxContiguousEnumValue<dom::${name}>
12678 static constexpr dom::${name} value = dom::${name}::${maxValue};
12680 static_assert(static_cast<${ty}>(dom::${name}::${minValue}) == 0,
12681 "We rely on this in ContiguousEnumValues");
12682 static_assert(std::size(dom::binding_detail::EnumStrings<dom::${name}>::Values) - 1 == UnderlyingValue(value),
12683 "Mismatch between enum strings and enum count");
12685 """,
12686 name=self.enum.identifier.name,
12687 ty=CGEnum.underlyingType(self.enum),
12688 maxValue=getEnumValueName(enumValues[-1]),
12689 minValue=getEnumValueName(enumValues[0]),
12692 def define(self):
12693 return ""
12695 def deps(self):
12696 return self.enum.getDeps()
12699 def getUnionAccessorSignatureType(type, descriptorProvider):
12701 Returns the types that are used in the getter and setter signatures for
12702 union types
12704 # Flat member types have already unwrapped nullables.
12705 assert not type.nullable()
12707 # Promise types can never appear in unions, because Promise is not
12708 # distinguishable from anything.
12709 assert not type.isPromise()
12711 if type.isSequence() or type.isRecord():
12712 if type.isSequence():
12713 wrapperType = "Sequence"
12714 else:
12715 wrapperType = "Record"
12716 # We don't use the returned template here, so it's OK to just pass no
12717 # sourceDescription.
12718 elementInfo = getJSToNativeConversionInfo(
12719 type.inner, descriptorProvider, isMember=wrapperType
12721 if wrapperType == "Sequence":
12722 innerType = elementInfo.declType
12723 else:
12724 innerType = [recordKeyDeclType(type), elementInfo.declType]
12726 return CGTemplatedType(wrapperType, innerType, isConst=True, isReference=True)
12728 # Nested unions are unwrapped automatically into our flatMemberTypes.
12729 assert not type.isUnion()
12731 if type.isGeckoInterface():
12732 descriptor = descriptorProvider.getDescriptor(
12733 type.unroll().inner.identifier.name
12735 typeName = CGGeneric(descriptor.nativeType)
12736 if not type.unroll().inner.isExternal():
12737 typeName = CGWrapper(typeName, post="&")
12738 elif descriptor.interface.identifier.name == "WindowProxy":
12739 typeName = CGGeneric("WindowProxyHolder const&")
12740 else:
12741 # Allow null pointers for old-binding classes.
12742 typeName = CGWrapper(typeName, post="*")
12743 return typeName
12745 if type.isSpiderMonkeyInterface():
12746 typeName = CGGeneric(type.name)
12747 return CGWrapper(typeName, post=" const &")
12749 if type.isJSString():
12750 raise TypeError("JSString not supported in unions")
12752 if type.isDOMString() or type.isUSVString():
12753 return CGGeneric("const nsAString&")
12755 if type.isUTF8String():
12756 return CGGeneric("const nsACString&")
12758 if type.isByteString():
12759 return CGGeneric("const nsCString&")
12761 if type.isEnum():
12762 return CGGeneric(type.inner.identifier.name)
12764 if type.isCallback():
12765 return CGGeneric("%s&" % type.unroll().callback.identifier.name)
12767 if type.isAny():
12768 return CGGeneric("JS::Value")
12770 if type.isObject():
12771 return CGGeneric("JSObject*")
12773 if type.isDictionary():
12774 return CGGeneric("const %s&" % type.inner.identifier.name)
12776 if not type.isPrimitive():
12777 raise TypeError("Need native type for argument type '%s'" % str(type))
12779 return CGGeneric(builtinNames[type.tag()])
12782 def getUnionTypeTemplateVars(unionType, type, descriptorProvider, isMember=False):
12783 assert not type.isUndefined()
12784 assert not isMember or isMember in ("Union", "OwningUnion")
12786 ownsMembers = isMember == "OwningUnion"
12787 name = getUnionMemberName(type)
12788 holderName = "m" + name + "Holder"
12790 # By the time tryNextCode is invoked, we're guaranteed the union has been
12791 # constructed as some type, since we've been trying to convert into the
12792 # corresponding member.
12793 tryNextCode = fill(
12795 Destroy${name}();
12796 tryNext = true;
12797 return true;
12798 """,
12799 name=name,
12802 sourceDescription = "%s branch of %s" % (type.prettyName(), unionType.prettyName())
12804 conversionInfo = getJSToNativeConversionInfo(
12805 type,
12806 descriptorProvider,
12807 failureCode=tryNextCode,
12808 isDefinitelyObject=not type.isDictionary(),
12809 isMember=isMember,
12810 sourceDescription=sourceDescription,
12813 ctorNeedsCx = conversionInfo.declArgs == "cx"
12814 ctorArgs = "cx" if ctorNeedsCx else ""
12816 structType = conversionInfo.declType.define()
12817 externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
12819 if type.isObject():
12820 if ownsMembers:
12821 setValue = "mValue.mObject.SetValue(obj);"
12822 else:
12823 setValue = "mValue.mObject.SetValue(cx, obj);"
12825 body = fill(
12827 MOZ_ASSERT(mType == eUninitialized);
12828 ${setValue}
12829 mType = eObject;
12830 """,
12831 setValue=setValue,
12834 # It's a bit sketchy to do the security check after setting the value,
12835 # but it keeps the code cleaner and lets us avoid rooting |obj| over the
12836 # call to CallerSubsumes().
12837 body = body + fill(
12839 if (passedToJSImpl && !CallerSubsumes(obj)) {
12840 cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}");
12841 return false;
12843 return true;
12844 """,
12845 sourceDescription=sourceDescription,
12848 setters = [
12849 ClassMethod(
12850 "SetToObject",
12851 "bool",
12853 Argument("BindingCallContext&", "cx"),
12854 Argument("JSObject*", "obj"),
12855 Argument("bool", "passedToJSImpl", default="false"),
12857 inline=True,
12858 bodyInHeader=True,
12859 body=body,
12862 elif type.isDictionary() and not type.inner.needsConversionFromJS:
12863 # In this case we are never initialized from JS to start with
12864 setters = None
12865 else:
12866 # Important: we need to not have our declName involve
12867 # maybe-GCing operations.
12868 jsConversion = fill(
12869 conversionInfo.template,
12870 val="value",
12871 maybeMutableVal="value",
12872 declName="memberSlot",
12873 holderName=(holderName if ownsMembers else "%s.ref()" % holderName),
12874 passedToJSImpl="passedToJSImpl",
12877 jsConversion = fill(
12879 tryNext = false;
12880 { // scope for memberSlot
12881 ${structType}& memberSlot = RawSetAs${name}(${ctorArgs});
12882 $*{jsConversion}
12884 return true;
12885 """,
12886 structType=structType,
12887 name=name,
12888 ctorArgs=ctorArgs,
12889 jsConversion=jsConversion,
12892 needCallContext = idlTypeNeedsCallContext(type)
12893 if needCallContext:
12894 cxType = "BindingCallContext&"
12895 else:
12896 cxType = "JSContext*"
12897 setters = [
12898 ClassMethod(
12899 "TrySetTo" + name,
12900 "bool",
12902 Argument(cxType, "cx"),
12903 Argument("JS::Handle<JS::Value>", "value"),
12904 Argument("bool&", "tryNext"),
12905 Argument("bool", "passedToJSImpl", default="false"),
12907 visibility="private",
12908 body=jsConversion,
12911 if needCallContext:
12912 # Add a method for non-binding uses of unions to allow them to set
12913 # things in the union without providing a call context (though if
12914 # they want good error reporting they'll provide one anyway).
12915 shimBody = fill(
12917 BindingCallContext cx(cx_, nullptr);
12918 return TrySetTo${name}(cx, value, tryNext, passedToJSImpl);
12919 """,
12920 name=name,
12922 setters.append(
12923 ClassMethod(
12924 "TrySetTo" + name,
12925 "bool",
12927 Argument("JSContext*", "cx_"),
12928 Argument("JS::Handle<JS::Value>", "value"),
12929 Argument("bool&", "tryNext"),
12930 Argument("bool", "passedToJSImpl", default="false"),
12932 visibility="private",
12933 body=shimBody,
12937 return {
12938 "name": name,
12939 "structType": structType,
12940 "externalType": externalType,
12941 "setters": setters,
12942 "ctorArgs": ctorArgs,
12943 "ctorArgList": [Argument("JSContext*", "cx")] if ctorNeedsCx else [],
12947 def getUnionConversionTemplate(type):
12948 assert type.isUnion()
12949 assert not type.nullable()
12951 memberTypes = type.flatMemberTypes
12952 prettyNames = []
12954 interfaceMemberTypes = [t for t in memberTypes if t.isNonCallbackInterface()]
12955 if len(interfaceMemberTypes) > 0:
12956 interfaceObject = []
12957 for memberType in interfaceMemberTypes:
12958 name = getUnionMemberName(memberType)
12959 interfaceObject.append(
12960 CGGeneric(
12961 "(failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext"
12962 % name
12965 prettyNames.append(memberType.prettyName())
12966 interfaceObject = CGWrapper(
12967 CGList(interfaceObject, " ||\n"),
12968 pre="done = ",
12969 post=";\n",
12970 reindent=True,
12972 else:
12973 interfaceObject = None
12975 sequenceObjectMemberTypes = [t for t in memberTypes if t.isSequence()]
12976 if len(sequenceObjectMemberTypes) > 0:
12977 assert len(sequenceObjectMemberTypes) == 1
12978 memberType = sequenceObjectMemberTypes[0]
12979 name = getUnionMemberName(memberType)
12980 sequenceObject = CGGeneric(
12981 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
12982 % name
12984 prettyNames.append(memberType.prettyName())
12985 else:
12986 sequenceObject = None
12988 callbackMemberTypes = [
12989 t for t in memberTypes if t.isCallback() or t.isCallbackInterface()
12991 if len(callbackMemberTypes) > 0:
12992 assert len(callbackMemberTypes) == 1
12993 memberType = callbackMemberTypes[0]
12994 name = getUnionMemberName(memberType)
12995 callbackObject = CGGeneric(
12996 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
12997 % name
12999 prettyNames.append(memberType.prettyName())
13000 else:
13001 callbackObject = None
13003 dictionaryMemberTypes = [t for t in memberTypes if t.isDictionary()]
13004 if len(dictionaryMemberTypes) > 0:
13005 assert len(dictionaryMemberTypes) == 1
13006 memberType = dictionaryMemberTypes[0]
13007 name = getUnionMemberName(memberType)
13008 setDictionary = CGGeneric(
13009 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
13010 % name
13012 prettyNames.append(memberType.prettyName())
13013 else:
13014 setDictionary = None
13016 recordMemberTypes = [t for t in memberTypes if t.isRecord()]
13017 if len(recordMemberTypes) > 0:
13018 assert len(recordMemberTypes) == 1
13019 memberType = recordMemberTypes[0]
13020 name = getUnionMemberName(memberType)
13021 recordObject = CGGeneric(
13022 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
13023 % name
13025 prettyNames.append(memberType.prettyName())
13026 else:
13027 recordObject = None
13029 objectMemberTypes = [t for t in memberTypes if t.isObject()]
13030 if len(objectMemberTypes) > 0:
13031 assert len(objectMemberTypes) == 1
13032 # Very important to NOT construct a temporary Rooted here, since the
13033 # SetToObject call can call a Rooted constructor and we need to keep
13034 # stack discipline for Rooted.
13035 object = CGGeneric(
13036 "if (!SetToObject(cx, &${val}.toObject(), ${passedToJSImpl})) {\n"
13037 " return false;\n"
13038 "}\n"
13039 "done = true;\n"
13041 prettyNames.append(objectMemberTypes[0].prettyName())
13042 else:
13043 object = None
13045 hasObjectTypes = (
13046 interfaceObject or sequenceObject or callbackObject or object or recordObject
13048 if hasObjectTypes:
13049 # "object" is not distinguishable from other types
13050 assert not object or not (
13051 interfaceObject or sequenceObject or callbackObject or recordObject
13053 if sequenceObject or callbackObject:
13054 # An object can be both an sequence object and a callback or
13055 # dictionary, but we shouldn't have both in the union's members
13056 # because they are not distinguishable.
13057 assert not (sequenceObject and callbackObject)
13058 templateBody = CGElseChain([sequenceObject, callbackObject])
13059 else:
13060 templateBody = None
13061 if interfaceObject:
13062 assert not object
13063 if templateBody:
13064 templateBody = CGIfWrapper(templateBody, "!done")
13065 templateBody = CGList([interfaceObject, templateBody])
13066 else:
13067 templateBody = CGList([templateBody, object])
13069 if recordObject:
13070 templateBody = CGList([templateBody, CGIfWrapper(recordObject, "!done")])
13072 templateBody = CGIfWrapper(templateBody, "${val}.isObject()")
13073 else:
13074 templateBody = CGGeneric()
13076 if setDictionary:
13077 assert not object
13078 templateBody = CGList([templateBody, CGIfWrapper(setDictionary, "!done")])
13080 stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
13081 numericTypes = [t for t in memberTypes if t.isNumeric()]
13082 booleanTypes = [t for t in memberTypes if t.isBoolean()]
13083 if stringTypes or numericTypes or booleanTypes:
13084 assert len(stringTypes) <= 1
13085 assert len(numericTypes) <= 1
13086 assert len(booleanTypes) <= 1
13088 # We will wrap all this stuff in a do { } while (0); so we
13089 # can use "break" for flow control.
13090 def getStringOrPrimitiveConversion(memberType):
13091 name = getUnionMemberName(memberType)
13092 return CGGeneric(
13093 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext)) || !tryNext;\n"
13094 "break;\n" % name
13097 other = CGList([])
13098 stringConversion = [getStringOrPrimitiveConversion(t) for t in stringTypes]
13099 numericConversion = [getStringOrPrimitiveConversion(t) for t in numericTypes]
13100 booleanConversion = [getStringOrPrimitiveConversion(t) for t in booleanTypes]
13101 if stringConversion:
13102 if booleanConversion:
13103 other.append(CGIfWrapper(booleanConversion[0], "${val}.isBoolean()"))
13104 if numericConversion:
13105 other.append(CGIfWrapper(numericConversion[0], "${val}.isNumber()"))
13106 other.append(stringConversion[0])
13107 elif numericConversion:
13108 if booleanConversion:
13109 other.append(CGIfWrapper(booleanConversion[0], "${val}.isBoolean()"))
13110 other.append(numericConversion[0])
13111 else:
13112 assert booleanConversion
13113 other.append(booleanConversion[0])
13115 other = CGWrapper(CGIndenter(other), pre="do {\n", post="} while (false);\n")
13116 if hasObjectTypes or setDictionary:
13117 other = CGWrapper(CGIndenter(other), "{\n", post="}\n")
13118 if object:
13119 templateBody = CGElseChain([templateBody, other])
13120 else:
13121 other = CGWrapper(other, pre="if (!done) ")
13122 templateBody = CGList([templateBody, other])
13123 else:
13124 assert templateBody.define() == ""
13125 templateBody = other
13126 else:
13127 other = None
13129 templateBody = CGWrapper(
13130 templateBody, pre="bool done = false, failed = false, tryNext;\n"
13132 throw = CGGeneric(
13133 fill(
13135 if (failed) {
13136 return false;
13138 if (!done) {
13139 cx.ThrowErrorMessage<MSG_NOT_IN_UNION>(sourceDescription, "${names}");
13140 return false;
13142 """,
13143 names=", ".join(prettyNames),
13147 templateBody = CGList([templateBody, throw])
13149 hasUndefinedType = any(t.isUndefined() for t in memberTypes)
13150 elseChain = []
13152 # The spec does this before anything else, but we do it after checking
13153 # for null in the case of a nullable union. In practice this shouldn't
13154 # make a difference, but it makes things easier because we first need to
13155 # call Construct on our Maybe<...>, before we can set the union type to
13156 # undefined, and we do that below after checking for null (see the
13157 # 'if nullable:' block below).
13158 if hasUndefinedType:
13159 elseChain.append(
13160 CGIfWrapper(
13161 CGGeneric("SetUndefined();\n"),
13162 "${val}.isUndefined()",
13166 if type.hasNullableType:
13167 nullTest = (
13168 "${val}.isNull()" if hasUndefinedType else "${val}.isNullOrUndefined()"
13170 elseChain.append(
13171 CGIfWrapper(
13172 CGGeneric("SetNull();\n"),
13173 nullTest,
13177 if len(elseChain) > 0:
13178 elseChain.append(CGWrapper(CGIndenter(templateBody), pre="{\n", post="}\n"))
13179 templateBody = CGElseChain(elseChain)
13181 return templateBody
13184 def getUnionInitMethods(type, isOwningUnion=False):
13185 assert type.isUnion()
13187 template = getUnionConversionTemplate(type).define()
13189 replacements = {
13190 "val": "value",
13191 "passedToJSImpl": "passedToJSImpl",
13194 initBody = fill(
13196 MOZ_ASSERT(mType == eUninitialized);
13198 $*{conversion}
13199 return true;
13200 """,
13201 conversion=string.Template(template).substitute(replacements),
13204 return [
13205 ClassMethod(
13206 "Init",
13207 "bool",
13209 Argument("BindingCallContext&", "cx"),
13210 Argument("JS::Handle<JS::Value>", "value"),
13211 Argument("const char*", "sourceDescription", default='"Value"'),
13212 Argument("bool", "passedToJSImpl", default="false"),
13214 visibility="public",
13215 body=initBody,
13217 ClassMethod(
13218 "Init",
13219 "bool",
13221 Argument("JSContext*", "cx_"),
13222 Argument("JS::Handle<JS::Value>", "value"),
13223 Argument("const char*", "sourceDescription", default='"Value"'),
13224 Argument("bool", "passedToJSImpl", default="false"),
13226 visibility="public",
13227 body=dedent(
13229 BindingCallContext cx(cx_, nullptr);
13230 return Init(cx, value, sourceDescription, passedToJSImpl);
13237 class CGUnionStruct(CGThing):
13238 def __init__(self, type, descriptorProvider, ownsMembers=False):
13239 CGThing.__init__(self)
13240 self.type = type.unroll()
13241 self.descriptorProvider = descriptorProvider
13242 self.ownsMembers = ownsMembers
13243 self.struct = self.getStruct()
13245 def declare(self):
13246 return self.struct.declare()
13248 def define(self):
13249 return self.struct.define()
13251 def deps(self):
13252 return self.type.getDeps()
13254 def getStruct(self):
13255 members = [
13256 ClassMember("mType", "TypeOrUninit", body="eUninitialized"),
13257 ClassMember("mValue", "Value"),
13259 ctor = ClassConstructor(
13260 [], bodyInHeader=True, visibility="public", explicit=True
13263 methods = []
13264 enumValues = ["eUninitialized"]
13265 toJSValCases = [
13266 CGCase(
13267 "eUninitialized", CGGeneric("return false;\n"), CGCase.DONT_ADD_BREAK
13270 destructorCases = [CGCase("eUninitialized", None)]
13271 assignmentCase = CGCase(
13272 "eUninitialized",
13273 CGGeneric(
13274 "MOZ_ASSERT(mType == eUninitialized,\n"
13275 ' "We need to destroy ourselves?");\n'
13278 assignmentCases = [assignmentCase]
13279 moveCases = [assignmentCase]
13280 traceCases = []
13281 unionValues = []
13283 def addSpecialType(typename):
13284 enumValue = "e" + typename
13285 enumValues.append(enumValue)
13286 methods.append(
13287 ClassMethod(
13288 "Is" + typename,
13289 "bool",
13291 const=True,
13292 inline=True,
13293 body="return mType == %s;\n" % enumValue,
13294 bodyInHeader=True,
13297 methods.append(
13298 ClassMethod(
13299 "Set" + typename,
13300 "void",
13302 inline=True,
13303 body=fill(
13305 Uninit();
13306 mType = ${enumValue};
13307 """,
13308 enumValue=enumValue,
13310 bodyInHeader=True,
13313 destructorCases.append(CGCase(enumValue, None))
13314 assignmentCase = CGCase(
13315 enumValue,
13316 CGGeneric(
13317 fill(
13319 MOZ_ASSERT(mType == eUninitialized);
13320 mType = ${enumValue};
13321 """,
13322 enumValue=enumValue,
13326 assignmentCases.append(assignmentCase)
13327 moveCases.append(assignmentCase)
13328 toJSValCases.append(
13329 CGCase(
13330 enumValue,
13331 CGGeneric(
13332 fill(
13334 rval.set${typename}();
13335 return true;
13336 """,
13337 typename=typename,
13340 CGCase.DONT_ADD_BREAK,
13344 if self.type.hasNullableType:
13345 addSpecialType("Null")
13347 hasObjectType = any(t.isObject() for t in self.type.flatMemberTypes)
13348 skipToJSVal = False
13349 for t in self.type.flatMemberTypes:
13350 if t.isUndefined():
13351 addSpecialType("Undefined")
13352 continue
13354 vars = getUnionTypeTemplateVars(
13355 self.type,
13357 self.descriptorProvider,
13358 isMember="OwningUnion" if self.ownsMembers else "Union",
13360 if vars["setters"]:
13361 methods.extend(vars["setters"])
13362 uninit = "Uninit();"
13363 if hasObjectType and not self.ownsMembers:
13364 uninit = (
13365 'MOZ_ASSERT(mType != eObject, "This will not play well with Rooted");\n'
13366 + uninit
13368 if not t.isObject() or self.ownsMembers:
13369 body = fill(
13371 if (mType == e${name}) {
13372 return mValue.m${name}.Value();
13375 mType = e${name};
13376 return mValue.m${name}.SetValue(${ctorArgs});
13377 """,
13378 **vars,
13381 # bodyInHeader must be false for return values because they own
13382 # their union members and we don't want include headers in
13383 # UnionTypes.h just to call Addref/Release
13384 methods.append(
13385 ClassMethod(
13386 "RawSetAs" + vars["name"],
13387 vars["structType"] + "&",
13388 vars["ctorArgList"],
13389 bodyInHeader=not self.ownsMembers,
13390 body=body % "MOZ_ASSERT(mType == eUninitialized);",
13391 noDiscard=True,
13394 methods.append(
13395 ClassMethod(
13396 "SetAs" + vars["name"],
13397 vars["structType"] + "&",
13398 vars["ctorArgList"],
13399 bodyInHeader=not self.ownsMembers,
13400 body=body % uninit,
13401 noDiscard=True,
13405 # Provide a SetStringLiteral() method to support string defaults.
13406 if t.isByteString() or t.isUTF8String():
13407 charType = "const nsCString::char_type"
13408 elif t.isString():
13409 charType = "const nsString::char_type"
13410 else:
13411 charType = None
13413 if charType:
13414 methods.append(
13415 ClassMethod(
13416 "SetStringLiteral",
13417 "void",
13418 # Hack, but it works...
13419 [Argument(charType, "(&aData)[N]")],
13420 inline=True,
13421 bodyInHeader=True,
13422 templateArgs=["int N"],
13423 body="RawSetAs%s().AssignLiteral(aData);\n" % t.name,
13427 body = fill("return mType == e${name};\n", **vars)
13428 methods.append(
13429 ClassMethod(
13430 "Is" + vars["name"],
13431 "bool",
13433 const=True,
13434 bodyInHeader=True,
13435 body=body,
13439 body = fill(
13441 MOZ_RELEASE_ASSERT(Is${name}(), "Wrong type!");
13442 mValue.m${name}.Destroy();
13443 mType = eUninitialized;
13444 """,
13445 **vars,
13447 methods.append(
13448 ClassMethod(
13449 "Destroy" + vars["name"],
13450 "void",
13452 visibility="private",
13453 bodyInHeader=not self.ownsMembers,
13454 body=body,
13458 body = fill(
13460 MOZ_RELEASE_ASSERT(Is${name}(), "Wrong type!");
13461 return mValue.m${name}.Value();
13462 """,
13463 **vars,
13465 # The non-const version of GetAs* returns our internal type
13466 getterReturnType = "%s&" % vars["structType"]
13467 methods.append(
13468 ClassMethod(
13469 "GetAs" + vars["name"],
13470 getterReturnType,
13472 bodyInHeader=True,
13473 body=body,
13476 # The const version of GetAs* returns our internal type
13477 # for owning unions, but our external type for non-owning
13478 # ones.
13479 if self.ownsMembers:
13480 getterReturnType = "%s const &" % vars["structType"]
13481 else:
13482 getterReturnType = vars["externalType"]
13483 methods.append(
13484 ClassMethod(
13485 "GetAs" + vars["name"],
13486 getterReturnType,
13488 const=True,
13489 bodyInHeader=True,
13490 body=body,
13494 unionValues.append(fill("UnionMember<${structType} > m${name}", **vars))
13495 destructorCases.append(
13496 CGCase("e" + vars["name"], CGGeneric("Destroy%s();\n" % vars["name"]))
13499 enumValues.append("e" + vars["name"])
13501 conversionToJS = self.getConversionToJS(vars, t)
13502 if conversionToJS:
13503 toJSValCases.append(
13504 CGCase("e" + vars["name"], conversionToJS, CGCase.DONT_ADD_BREAK)
13506 else:
13507 skipToJSVal = True
13509 assignmentCases.append(
13510 CGCase(
13511 "e" + vars["name"],
13512 CGGeneric(
13513 "SetAs%s() = aOther.GetAs%s();\n" % (vars["name"], vars["name"])
13517 moveCases.append(
13518 CGCase(
13519 "e" + vars["name"],
13520 CGGeneric(
13521 "mType = e%s;\n" % vars["name"]
13522 + "mValue.m%s.SetValue(std::move(aOther.mValue.m%s.Value()));\n"
13523 % (vars["name"], vars["name"])
13527 if self.ownsMembers and typeNeedsRooting(t):
13528 if t.isObject():
13529 traceCases.append(
13530 CGCase(
13531 "e" + vars["name"],
13532 CGGeneric(
13533 'JS::TraceRoot(trc, %s, "%s");\n'
13535 "&mValue.m" + vars["name"] + ".Value()",
13536 "mValue.m" + vars["name"],
13541 elif t.isDictionary():
13542 traceCases.append(
13543 CGCase(
13544 "e" + vars["name"],
13545 CGGeneric(
13546 "mValue.m%s.Value().TraceDictionary(trc);\n"
13547 % vars["name"]
13551 elif t.isSequence():
13552 traceCases.append(
13553 CGCase(
13554 "e" + vars["name"],
13555 CGGeneric(
13556 "DoTraceSequence(trc, mValue.m%s.Value());\n"
13557 % vars["name"]
13561 elif t.isRecord():
13562 traceCases.append(
13563 CGCase(
13564 "e" + vars["name"],
13565 CGGeneric(
13566 "TraceRecord(trc, mValue.m%s.Value());\n" % vars["name"]
13570 else:
13571 assert t.isSpiderMonkeyInterface()
13572 traceCases.append(
13573 CGCase(
13574 "e" + vars["name"],
13575 CGGeneric(
13576 "mValue.m%s.Value().TraceSelf(trc);\n" % vars["name"]
13581 dtor = CGSwitch("mType", destructorCases).define()
13583 methods.extend(getUnionInitMethods(self.type, isOwningUnion=self.ownsMembers))
13584 methods.append(
13585 ClassMethod(
13586 "Uninit",
13587 "void",
13589 visibility="public",
13590 body=dtor,
13591 bodyInHeader=not self.ownsMembers,
13592 inline=not self.ownsMembers,
13596 if not skipToJSVal:
13597 methods.append(
13598 ClassMethod(
13599 "ToJSVal",
13600 "bool",
13602 Argument("JSContext*", "cx"),
13603 Argument("JS::Handle<JSObject*>", "scopeObj"),
13604 Argument("JS::MutableHandle<JS::Value>", "rval"),
13606 body=CGSwitch(
13607 "mType", toJSValCases, default=CGGeneric("return false;\n")
13608 ).define(),
13609 const=True,
13613 constructors = [ctor]
13614 selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers)
13615 if self.ownsMembers:
13616 if traceCases:
13617 traceBody = CGSwitch(
13618 "mType", traceCases, default=CGGeneric("")
13619 ).define()
13620 methods.append(
13621 ClassMethod(
13622 "TraceUnion",
13623 "void",
13624 [Argument("JSTracer*", "trc")],
13625 body=traceBody,
13629 op_body = CGList([])
13630 op_body.append(CGSwitch("aOther.mType", moveCases))
13631 constructors.append(
13632 ClassConstructor(
13633 [Argument("%s&&" % selfName, "aOther")],
13634 visibility="public",
13635 body=op_body.define(),
13639 methods.append(
13640 ClassMethod(
13641 "operator=",
13642 "%s&" % selfName,
13643 [Argument("%s&&" % selfName, "aOther")],
13644 body="this->~%s();\nnew (this) %s (std::move(aOther));\nreturn *this;\n"
13645 % (selfName, selfName),
13649 body = dedent(
13651 MOZ_RELEASE_ASSERT(mType != eUninitialized);
13652 return static_cast<Type>(mType);
13655 methods.append(
13656 ClassMethod(
13657 "GetType",
13658 "Type",
13660 bodyInHeader=True,
13661 body=body,
13662 const=True,
13666 if CGUnionStruct.isUnionCopyConstructible(self.type):
13667 constructors.append(
13668 ClassConstructor(
13669 [Argument("const %s&" % selfName, "aOther")],
13670 bodyInHeader=True,
13671 visibility="public",
13672 explicit=True,
13673 body="*this = aOther;\n",
13676 op_body = CGList([])
13677 op_body.append(CGSwitch("aOther.mType", assignmentCases))
13678 op_body.append(CGGeneric("return *this;\n"))
13679 methods.append(
13680 ClassMethod(
13681 "operator=",
13682 "%s&" % selfName,
13683 [Argument("const %s&" % selfName, "aOther")],
13684 body=op_body.define(),
13687 disallowCopyConstruction = False
13688 else:
13689 disallowCopyConstruction = True
13690 else:
13691 disallowCopyConstruction = True
13693 if self.ownsMembers and idlTypeNeedsCycleCollection(self.type):
13694 friend = (
13695 " friend void ImplCycleCollectionUnlink(%s& aUnion);\n"
13696 % CGUnionStruct.unionTypeName(self.type, True)
13698 else:
13699 friend = ""
13701 enumValuesNoUninit = [x for x in enumValues if x != "eUninitialized"]
13703 enums = [
13704 ClassGroup(
13706 ClassEnum("TypeOrUninit", enumValues, visibility="private"),
13707 ClassEnum(
13708 "Type",
13709 enumValuesNoUninit,
13710 visibility="public",
13711 enumClass=True,
13712 values=["TypeOrUninit::" + x for x in enumValuesNoUninit],
13718 bases = [
13719 ClassBase("AllOwningUnionBase" if self.ownsMembers else "AllUnionBase")
13722 typeAliases = []
13723 bufferSourceTypes = [
13724 t.name for t in self.type.flatMemberTypes if t.isBufferSource()
13726 if len(bufferSourceTypes) > 0:
13727 bases.append(ClassBase("UnionWithTypedArraysBase"))
13728 memberTypesCount = len(self.type.flatMemberTypes)
13729 if self.type.hasNullableType:
13730 memberTypesCount += 1
13732 typeAliases = [
13733 ClassUsingDeclaration(
13734 "ApplyToTypedArrays",
13735 "binding_detail::ApplyToTypedArraysHelper<%s, %s, %s>"
13737 selfName,
13738 toStringBool(memberTypesCount > len(bufferSourceTypes)),
13739 ", ".join(bufferSourceTypes),
13744 return CGClass(
13745 selfName,
13746 bases=bases,
13747 typeAliases=typeAliases,
13748 members=members,
13749 constructors=constructors,
13750 methods=methods,
13751 disallowCopyConstruction=disallowCopyConstruction,
13752 extradeclarations=friend,
13753 destructor=ClassDestructor(
13754 visibility="public", body="Uninit();\n", bodyInHeader=True
13756 enums=enums,
13757 unions=[ClassUnion("Value", unionValues, visibility="private")],
13760 def getConversionToJS(self, templateVars, type):
13761 if type.isDictionary() and not type.inner.needsConversionToJS:
13762 # We won't be able to convert this dictionary to a JS value, nor
13763 # will we need to, since we don't need a ToJSVal method at all.
13764 return None
13766 assert not type.nullable() # flatMemberTypes never has nullable types
13767 val = "mValue.m%(name)s.Value()" % templateVars
13768 wrapCode = wrapForType(
13769 type,
13770 self.descriptorProvider,
13772 "jsvalRef": "rval",
13773 "jsvalHandle": "rval",
13774 "obj": "scopeObj",
13775 "result": val,
13776 "spiderMonkeyInterfacesAreStructs": True,
13779 return CGGeneric(wrapCode)
13781 @staticmethod
13782 def isUnionCopyConstructible(type):
13783 return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes)
13785 @staticmethod
13786 def unionTypeName(type, ownsMembers):
13788 Returns a string name for this known union type.
13790 assert type.isUnion() and not type.nullable()
13791 return ("Owning" if ownsMembers else "") + type.name
13793 @staticmethod
13794 def unionTypeDecl(type, ownsMembers):
13796 Returns a string for declaring this possibly-nullable union type.
13798 assert type.isUnion()
13799 nullable = type.nullable()
13800 if nullable:
13801 type = type.inner
13802 decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers))
13803 if nullable:
13804 decl = CGTemplatedType("Nullable", decl)
13805 return decl.define()
13808 class ClassItem:
13809 """Use with CGClass"""
13811 def __init__(self, name, visibility):
13812 self.name = name
13813 self.visibility = visibility
13815 def declare(self, cgClass):
13816 assert False
13818 def define(self, cgClass):
13819 assert False
13822 class ClassBase(ClassItem):
13823 def __init__(self, name, visibility="public"):
13824 ClassItem.__init__(self, name, visibility)
13826 def declare(self, cgClass):
13827 return "%s %s" % (self.visibility, self.name)
13829 def define(self, cgClass):
13830 # Only in the header
13831 return ""
13834 class ClassMethod(ClassItem):
13835 def __init__(
13836 self,
13837 name,
13838 returnType,
13839 args,
13840 inline=False,
13841 static=False,
13842 virtual=False,
13843 const=False,
13844 bodyInHeader=False,
13845 templateArgs=None,
13846 visibility="public",
13847 body=None,
13848 breakAfterReturnDecl="\n",
13849 breakAfterSelf="\n",
13850 override=False,
13851 canRunScript=False,
13852 noDiscard=False,
13853 delete=False,
13856 override indicates whether to flag the method as override
13858 assert not override or virtual
13859 assert not (override and static)
13860 assert not (delete and body)
13861 self.returnType = returnType
13862 self.args = args
13863 self.inline = inline or bodyInHeader
13864 self.static = static
13865 self.virtual = virtual
13866 self.const = const
13867 self.bodyInHeader = bodyInHeader
13868 self.templateArgs = templateArgs
13869 self.body = body
13870 self.breakAfterReturnDecl = breakAfterReturnDecl
13871 self.breakAfterSelf = breakAfterSelf
13872 self.override = override
13873 self.canRunScript = canRunScript
13874 self.noDiscard = noDiscard
13875 self.delete = delete
13876 ClassItem.__init__(self, name, visibility)
13878 def getDecorators(self, declaring):
13879 decorators = []
13880 if self.noDiscard:
13881 decorators.append("[[nodiscard]]")
13882 if self.canRunScript:
13883 decorators.append("MOZ_CAN_RUN_SCRIPT")
13884 if self.inline:
13885 decorators.append("inline")
13886 if declaring:
13887 if self.static:
13888 decorators.append("static")
13889 if self.virtual and not self.override:
13890 decorators.append("virtual")
13891 if decorators:
13892 return " ".join(decorators) + " "
13893 return ""
13895 def getBody(self):
13896 # Override me or pass a string to constructor
13897 assert self.body is not None
13898 return self.body
13900 def declare(self, cgClass):
13901 templateClause = (
13902 "template <%s>\n" % ", ".join(self.templateArgs)
13903 if self.bodyInHeader and self.templateArgs
13904 else ""
13906 args = ", ".join([a.declare() for a in self.args])
13907 if self.delete:
13908 body = " = delete;\n"
13909 elif self.bodyInHeader:
13910 body = indent(self.getBody())
13911 body = "\n{\n" + body + "}\n"
13912 else:
13913 body = ";\n"
13915 return fill(
13916 "${templateClause}${decorators}${returnType}${breakAfterReturnDecl}"
13917 "${name}(${args})${const}${override}${body}"
13918 "${breakAfterSelf}",
13919 templateClause=templateClause,
13920 decorators=self.getDecorators(True),
13921 returnType=self.returnType,
13922 breakAfterReturnDecl=self.breakAfterReturnDecl,
13923 name=self.name,
13924 args=args,
13925 const=" const" if self.const else "",
13926 override=" override" if self.override else "",
13927 body=body,
13928 breakAfterSelf=self.breakAfterSelf,
13931 def define(self, cgClass):
13932 if self.delete or self.bodyInHeader:
13933 return ""
13935 templateArgs = cgClass.templateArgs
13936 if templateArgs:
13937 if cgClass.templateSpecialization:
13938 templateArgs = templateArgs[len(cgClass.templateSpecialization) :]
13940 if templateArgs:
13941 templateClause = "template <%s>\n" % ", ".join(
13942 [str(a) for a in templateArgs]
13944 else:
13945 templateClause = ""
13947 return fill(
13949 ${templateClause}${decorators}${returnType}
13950 ${className}::${name}(${args})${const}
13952 $*{body}
13954 """,
13955 templateClause=templateClause,
13956 decorators=self.getDecorators(False),
13957 returnType=self.returnType,
13958 className=cgClass.getNameString(),
13959 name=self.name,
13960 args=", ".join([a.define() for a in self.args]),
13961 const=" const" if self.const else "",
13962 body=self.getBody(),
13966 class ClassUsingDeclaration(ClassItem):
13968 Used for declaring an alias for a type in a CGClass
13970 name is the name of the alias
13972 type is the type to declare an alias of
13974 visibility determines the visibility of the alias (public,
13975 protected, private), defaults to public.
13978 def __init__(self, name, type, visibility="public"):
13979 self.type = type
13980 ClassItem.__init__(self, name, visibility)
13982 def declare(self, cgClass):
13983 return "using %s = %s;\n\n" % (self.name, self.type)
13985 def define(self, cgClass):
13986 return ""
13989 class ClassUsingFromBaseDeclaration(ClassItem):
13991 Used for importing a name from a base class into a CGClass
13993 baseClass is the name of the base class to import the name from
13995 name is the name to import
13997 visibility determines the visibility of the name (public,
13998 protected, private), defaults to public.
14001 def __init__(self, baseClass, name, visibility="public"):
14002 self.baseClass = baseClass
14003 ClassItem.__init__(self, name, visibility)
14005 def declare(self, cgClass):
14006 return "using %s::%s;\n\n" % (self.baseClass, self.name)
14008 def define(self, cgClass):
14009 return ""
14012 class ClassConstructor(ClassItem):
14014 Used for adding a constructor to a CGClass.
14016 args is a list of Argument objects that are the arguments taken by the
14017 constructor.
14019 inline should be True if the constructor should be marked inline.
14021 bodyInHeader should be True if the body should be placed in the class
14022 declaration in the header.
14024 default should be True if the definition of the constructor should be
14025 `= default;`.
14027 visibility determines the visibility of the constructor (public,
14028 protected, private), defaults to private.
14030 explicit should be True if the constructor should be marked explicit.
14032 baseConstructors is a list of strings containing calls to base constructors,
14033 defaults to None.
14035 body contains a string with the code for the constructor, defaults to empty.
14038 def __init__(
14039 self,
14040 args,
14041 inline=False,
14042 bodyInHeader=False,
14043 default=False,
14044 visibility="private",
14045 explicit=False,
14046 constexpr=False,
14047 baseConstructors=None,
14048 body="",
14050 assert not (inline and constexpr)
14051 assert not (bodyInHeader and constexpr)
14052 assert not (default and body)
14053 self.args = args
14054 self.inline = inline or bodyInHeader
14055 self.bodyInHeader = bodyInHeader or constexpr or default
14056 self.default = default
14057 self.explicit = explicit
14058 self.constexpr = constexpr
14059 self.baseConstructors = baseConstructors or []
14060 self.body = body
14061 ClassItem.__init__(self, None, visibility)
14063 def getDecorators(self, declaring):
14064 decorators = []
14065 if declaring:
14066 if self.explicit:
14067 decorators.append("explicit")
14068 if self.inline:
14069 decorators.append("inline")
14070 if self.constexpr:
14071 decorators.append("constexpr")
14072 if decorators:
14073 return " ".join(decorators) + " "
14074 return ""
14076 def getInitializationList(self, cgClass):
14077 items = [str(c) for c in self.baseConstructors]
14078 for m in cgClass.members:
14079 if not m.static:
14080 initialize = m.body
14081 if initialize:
14082 items.append(m.name + "(" + initialize + ")")
14084 if len(items) > 0:
14085 return "\n : " + ",\n ".join(items)
14086 return ""
14088 def getBody(self):
14089 return self.body
14091 def declare(self, cgClass):
14092 args = ", ".join([a.declare() for a in self.args])
14093 if self.bodyInHeader:
14094 if self.default:
14095 body = " = default;\n"
14096 else:
14097 body = (
14098 self.getInitializationList(cgClass)
14099 + "\n{\n"
14100 + indent(self.getBody())
14101 + "}\n"
14103 else:
14104 body = ";\n"
14106 return fill(
14107 "${decorators}${className}(${args})${body}\n",
14108 decorators=self.getDecorators(True),
14109 className=cgClass.getNameString(),
14110 args=args,
14111 body=body,
14114 def define(self, cgClass):
14115 if self.bodyInHeader:
14116 return ""
14118 return fill(
14120 ${decorators}
14121 ${className}::${className}(${args})${initializationList}
14123 $*{body}
14125 """,
14126 decorators=self.getDecorators(False),
14127 className=cgClass.getNameString(),
14128 args=", ".join([a.define() for a in self.args]),
14129 initializationList=self.getInitializationList(cgClass),
14130 body=self.getBody(),
14134 class ClassDestructor(ClassItem):
14136 Used for adding a destructor to a CGClass.
14138 inline should be True if the destructor should be marked inline.
14140 bodyInHeader should be True if the body should be placed in the class
14141 declaration in the header.
14143 visibility determines the visibility of the destructor (public,
14144 protected, private), defaults to private.
14146 body contains a string with the code for the destructor, defaults to empty.
14148 virtual determines whether the destructor is virtual, defaults to False.
14151 def __init__(
14152 self,
14153 inline=False,
14154 bodyInHeader=False,
14155 visibility="private",
14156 body="",
14157 virtual=False,
14159 self.inline = inline or bodyInHeader
14160 self.bodyInHeader = bodyInHeader
14161 self.body = body
14162 self.virtual = virtual
14163 ClassItem.__init__(self, None, visibility)
14165 def getDecorators(self, declaring):
14166 decorators = []
14167 if self.virtual and declaring:
14168 decorators.append("virtual")
14169 if self.inline and declaring:
14170 decorators.append("inline")
14171 if decorators:
14172 return " ".join(decorators) + " "
14173 return ""
14175 def getBody(self):
14176 return self.body
14178 def declare(self, cgClass):
14179 if self.bodyInHeader:
14180 body = "\n{\n" + indent(self.getBody()) + "}\n"
14181 else:
14182 body = ";\n"
14184 return fill(
14185 "${decorators}~${className}()${body}\n",
14186 decorators=self.getDecorators(True),
14187 className=cgClass.getNameString(),
14188 body=body,
14191 def define(self, cgClass):
14192 if self.bodyInHeader:
14193 return ""
14194 return fill(
14196 ${decorators}
14197 ${className}::~${className}()
14199 $*{body}
14201 """,
14202 decorators=self.getDecorators(False),
14203 className=cgClass.getNameString(),
14204 body=self.getBody(),
14208 class ClassMember(ClassItem):
14209 def __init__(
14210 self,
14211 name,
14212 type,
14213 visibility="private",
14214 static=False,
14215 body=None,
14216 hasIgnoreInitCheckFlag=False,
14218 self.type = type
14219 self.static = static
14220 self.body = body
14221 self.hasIgnoreInitCheckFlag = hasIgnoreInitCheckFlag
14222 ClassItem.__init__(self, name, visibility)
14224 def declare(self, cgClass):
14225 return "%s%s%s %s;\n" % (
14226 "static " if self.static else "",
14227 "MOZ_INIT_OUTSIDE_CTOR " if self.hasIgnoreInitCheckFlag else "",
14228 self.type,
14229 self.name,
14232 def define(self, cgClass):
14233 if not self.static:
14234 return ""
14235 if self.body:
14236 body = " = " + self.body
14237 else:
14238 body = ""
14239 return "%s %s::%s%s;\n" % (self.type, cgClass.getNameString(), self.name, body)
14242 class ClassEnum(ClassItem):
14243 def __init__(
14244 self, name, entries, values=None, visibility="public", enumClass=False
14246 self.entries = entries
14247 self.values = values
14248 self.enumClass = enumClass
14249 ClassItem.__init__(self, name, visibility)
14251 def declare(self, cgClass):
14252 entries = []
14253 for i in range(0, len(self.entries)):
14254 if not self.values or i >= len(self.values):
14255 entry = "%s" % self.entries[i]
14256 else:
14257 entry = "%s = %s" % (self.entries[i], self.values[i])
14258 entries.append(entry)
14260 decl = ["enum"]
14261 self.enumClass and decl.append("class")
14262 self.name and decl.append(self.name)
14263 decl = " ".join(decl)
14265 return "%s\n{\n%s\n};\n" % (decl, indent(",\n".join(entries)))
14267 def define(self, cgClass):
14268 # Only goes in the header
14269 return ""
14272 class ClassUnion(ClassItem):
14273 def __init__(self, name, entries, visibility="public"):
14274 self.entries = [entry + ";\n" for entry in entries]
14275 ClassItem.__init__(self, name, visibility)
14277 def declare(self, cgClass):
14278 return "union %s\n{\n%s\n};\n" % (self.name, indent("".join(self.entries)))
14280 def define(self, cgClass):
14281 # Only goes in the header
14282 return ""
14285 class ClassGroup(ClassItem):
14286 def __init__(self, items):
14287 self.items = items
14288 ClassItem.__init__(self, "", items[0].visibility)
14290 def declare(self, cgClass):
14291 assert False
14293 def define(self, cgClass):
14294 assert False
14297 class CGClass(CGThing):
14298 def __init__(
14299 self,
14300 name,
14301 bases=[],
14302 typeAliases=[],
14303 members=[],
14304 constructors=[],
14305 destructor=None,
14306 methods=[],
14307 enums=[],
14308 unions=[],
14309 templateArgs=[],
14310 templateSpecialization=[],
14311 isStruct=False,
14312 disallowCopyConstruction=False,
14313 indent="",
14314 decorators="",
14315 extradeclarations="",
14316 extradefinitions="",
14318 CGThing.__init__(self)
14319 self.name = name
14320 self.bases = bases
14321 self.typeAliases = typeAliases
14322 self.members = members
14323 self.constructors = constructors
14324 # We store our single destructor in a list, since all of our
14325 # code wants lists of members.
14326 self.destructors = [destructor] if destructor else []
14327 self.methods = methods
14328 self.enums = enums
14329 self.unions = unions
14330 self.templateArgs = templateArgs
14331 self.templateSpecialization = templateSpecialization
14332 self.isStruct = isStruct
14333 self.disallowCopyConstruction = disallowCopyConstruction
14334 self.indent = indent
14335 self.defaultVisibility = "public" if isStruct else "private"
14336 self.decorators = decorators
14337 self.extradeclarations = extradeclarations
14338 self.extradefinitions = extradefinitions
14340 def getNameString(self):
14341 className = self.name
14342 if self.templateSpecialization:
14343 className += "<%s>" % ", ".join(
14344 [str(a) for a in self.templateSpecialization]
14346 return className
14348 @staticmethod
14349 def flattenClassItemLists(l):
14350 for item in l:
14351 if isinstance(item, ClassGroup):
14352 for inner in CGClass.flattenClassItemLists(item.items):
14353 yield inner
14354 else:
14355 yield item
14357 def declare(self):
14358 result = ""
14359 if self.templateArgs:
14360 templateArgs = [a.declare() for a in self.templateArgs]
14361 templateArgs = templateArgs[len(self.templateSpecialization) :]
14362 result += "template <%s>\n" % ",".join([str(a) for a in templateArgs])
14364 type = "struct" if self.isStruct else "class"
14366 if self.templateSpecialization:
14367 specialization = "<%s>" % ", ".join(
14368 [str(a) for a in self.templateSpecialization]
14370 else:
14371 specialization = ""
14373 myself = "%s %s%s" % (type, self.name, specialization)
14374 if self.decorators != "":
14375 myself += " " + self.decorators
14376 result += myself
14378 if self.bases:
14379 inherit = " : "
14380 result += inherit
14381 # Grab our first base
14382 baseItems = [CGGeneric(b.declare(self)) for b in self.bases]
14383 bases = baseItems[:1]
14384 # Indent the rest
14385 bases.extend(
14386 CGIndenter(b, len(myself) + len(inherit)) for b in baseItems[1:]
14388 result += ",\n".join(b.define() for b in bases)
14390 result += "\n{\n"
14392 result += self.extradeclarations
14394 def declareMembers(cgClass, memberList, defaultVisibility):
14395 members = {"private": [], "protected": [], "public": []}
14397 for member in memberList:
14398 members[member.visibility].append(member)
14400 if defaultVisibility == "public":
14401 order = ["public", "protected", "private"]
14402 else:
14403 order = ["private", "protected", "public"]
14405 result = ""
14407 lastVisibility = defaultVisibility
14408 for visibility in order:
14409 list = members[visibility]
14410 if list:
14411 for member in self.flattenClassItemLists(list):
14412 if member.visibility != lastVisibility:
14413 result += member.visibility + ":\n"
14414 result += indent(member.declare(cgClass))
14415 lastVisibility = member.visibility
14416 return (result, lastVisibility)
14418 if self.disallowCopyConstruction:
14420 class DisallowedCopyConstructor(object):
14421 def __init__(self):
14422 self.visibility = "private"
14424 def declare(self, cgClass):
14425 name = cgClass.getNameString()
14426 return (
14427 "%s(const %s&) = delete;\n"
14428 "%s& operator=(const %s&) = delete;\n"
14429 % (name, name, name, name)
14432 disallowedCopyConstructors = [DisallowedCopyConstructor()]
14433 else:
14434 disallowedCopyConstructors = []
14436 order = [
14437 self.typeAliases,
14438 self.enums,
14439 self.unions,
14440 self.members,
14441 self.constructors + disallowedCopyConstructors,
14442 self.destructors,
14443 self.methods,
14446 lastVisibility = self.defaultVisibility
14447 pieces = []
14448 for memberList in order:
14449 code, lastVisibility = declareMembers(self, memberList, lastVisibility)
14451 if code:
14452 code = code.rstrip() + "\n" # remove extra blank lines at the end
14453 pieces.append(code)
14455 result += "\n".join(pieces)
14456 result += "};\n"
14457 result = indent(result, len(self.indent))
14458 return result
14460 def define(self):
14461 def defineMembers(cgClass, memberList, itemCount, separator=""):
14462 result = ""
14463 for member in self.flattenClassItemLists(memberList):
14464 if itemCount != 0:
14465 result = result + separator
14466 definition = member.define(cgClass)
14467 if definition:
14468 # Member variables would only produce empty lines here.
14469 result += definition
14470 itemCount += 1
14471 return (result, itemCount)
14473 order = [
14474 (self.members, ""),
14475 (self.constructors, "\n"),
14476 (self.destructors, "\n"),
14477 (self.methods, "\n"),
14480 result = self.extradefinitions
14481 itemCount = 0
14482 for memberList, separator in order:
14483 memberString, itemCount = defineMembers(
14484 self, memberList, itemCount, separator
14486 result = result + memberString
14487 return result
14490 class CGResolveOwnPropertyViaResolve(CGAbstractBindingMethod):
14492 An implementation of Xray ResolveOwnProperty stuff for things that have a
14493 resolve hook.
14496 def __init__(self, descriptor):
14497 args = [
14498 Argument("JSContext*", "cx"),
14499 Argument("JS::Handle<JSObject*>", "wrapper"),
14500 Argument("JS::Handle<JSObject*>", "obj"),
14501 Argument("JS::Handle<jsid>", "id"),
14502 Argument("JS::MutableHandle<Maybe<JS::PropertyDescriptor>>", "desc"),
14504 CGAbstractBindingMethod.__init__(
14505 self,
14506 descriptor,
14507 "ResolveOwnPropertyViaResolve",
14508 args,
14509 getThisObj="",
14510 callArgs="",
14513 def generate_code(self):
14514 return CGGeneric(
14515 dedent(
14518 // Since we're dealing with an Xray, do the resolve on the
14519 // underlying object first. That gives it a chance to
14520 // define properties on the actual object as needed, and
14521 // then use the fact that it created the objects as a flag
14522 // to avoid re-resolving the properties if someone deletes
14523 // them.
14524 JSAutoRealm ar(cx, obj);
14525 JS_MarkCrossZoneId(cx, id);
14526 JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> objDesc(cx);
14527 if (!self->DoResolve(cx, obj, id, &objDesc)) {
14528 return false;
14530 // If desc->value() is undefined, then the DoResolve call
14531 // has already defined the property on the object. Don't
14532 // try to also define it.
14533 if (objDesc.isSome() &&
14534 !objDesc->value().isUndefined()) {
14535 JS::Rooted<JS::PropertyDescriptor> defineDesc(cx, *objDesc);
14536 if (!JS_DefinePropertyById(cx, obj, id, defineDesc)) {
14537 return false;
14541 return self->DoResolve(cx, wrapper, id, desc);
14547 class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod):
14549 An implementation of Xray EnumerateOwnProperties stuff for things
14550 that have a resolve hook.
14553 def __init__(self, descriptor):
14554 args = [
14555 Argument("JSContext*", "cx"),
14556 Argument("JS::Handle<JSObject*>", "wrapper"),
14557 Argument("JS::Handle<JSObject*>", "obj"),
14558 Argument("JS::MutableHandleVector<jsid>", "props"),
14560 CGAbstractBindingMethod.__init__(
14561 self,
14562 descriptor,
14563 "EnumerateOwnPropertiesViaGetOwnPropertyNames",
14564 args,
14565 getThisObj="",
14566 callArgs="",
14569 def generate_code(self):
14570 return CGGeneric(
14571 dedent(
14573 FastErrorResult rv;
14574 // This wants all own props, not just enumerable ones.
14575 self->GetOwnPropertyNames(cx, props, false, rv);
14576 if (rv.MaybeSetPendingException(cx)) {
14577 return false;
14579 return true;
14585 class CGPrototypeTraitsClass(CGClass):
14586 def __init__(self, descriptor, indent=""):
14587 templateArgs = [Argument("prototypes::ID", "PrototypeID")]
14588 templateSpecialization = ["prototypes::id::" + descriptor.name]
14589 enums = [ClassEnum("", ["Depth"], [descriptor.interface.inheritanceDepth()])]
14590 CGClass.__init__(
14591 self,
14592 "PrototypeTraits",
14593 indent=indent,
14594 templateArgs=templateArgs,
14595 templateSpecialization=templateSpecialization,
14596 enums=enums,
14597 isStruct=True,
14600 def deps(self):
14601 return set()
14604 class CGClassForwardDeclare(CGThing):
14605 def __init__(self, name, isStruct=False):
14606 CGThing.__init__(self)
14607 self.name = name
14608 self.isStruct = isStruct
14610 def declare(self):
14611 type = "struct" if self.isStruct else "class"
14612 return "%s %s;\n" % (type, self.name)
14614 def define(self):
14615 # Header only
14616 return ""
14618 def deps(self):
14619 return set()
14622 class CGProxySpecialOperation(CGPerSignatureCall):
14624 Base class for classes for calling an indexed or named special operation
14625 (don't use this directly, use the derived classes below).
14627 If checkFound is False, will just assert that the prop is found instead of
14628 checking that it is before wrapping the value.
14630 resultVar: See the docstring for CGCallGenerator.
14632 foundVar: For getters and deleters, the generated code can also set a bool
14633 variable, declared by the caller, if the given indexed or named property
14634 already existed. If the caller wants this, it should pass the name of the
14635 bool variable as the foundVar keyword argument to the constructor. The
14636 caller is responsible for declaring the variable and initializing it to
14637 false.
14640 def __init__(
14641 self,
14642 descriptor,
14643 operation,
14644 checkFound=True,
14645 argumentHandleValue=None,
14646 resultVar=None,
14647 foundVar=None,
14649 self.checkFound = checkFound
14650 self.foundVar = foundVar or "found"
14652 nativeName = MakeNativeName(descriptor.binaryNameFor(operation, False))
14653 operation = descriptor.operations[operation]
14654 assert len(operation.signatures()) == 1
14655 signature = operation.signatures()[0]
14657 returnType, arguments = signature
14659 # We pass len(arguments) as the final argument so that the
14660 # CGPerSignatureCall won't do any argument conversion of its own.
14661 CGPerSignatureCall.__init__(
14662 self,
14663 returnType,
14664 arguments,
14665 nativeName,
14666 False,
14667 descriptor,
14668 operation,
14669 len(arguments),
14670 resultVar=resultVar,
14671 objectName="proxy",
14674 if operation.isSetter():
14675 # arguments[0] is the index or name of the item that we're setting.
14676 argument = arguments[1]
14677 info = getJSToNativeConversionInfo(
14678 argument.type,
14679 descriptor,
14680 sourceDescription=(
14681 "value being assigned to %s setter"
14682 % descriptor.interface.identifier.name
14685 if argumentHandleValue is None:
14686 argumentHandleValue = "desc.value()"
14687 rootedValue = fill(
14689 JS::Rooted<JS::Value> rootedValue(cx, ${argumentHandleValue});
14690 """,
14691 argumentHandleValue=argumentHandleValue,
14693 templateValues = {
14694 "declName": argument.identifier.name,
14695 "holderName": argument.identifier.name + "_holder",
14696 "val": argumentHandleValue,
14697 "maybeMutableVal": "&rootedValue",
14698 "obj": "obj",
14699 "passedToJSImpl": "false",
14701 self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues))
14702 # rootedValue needs to come before the conversion, so we
14703 # need to prepend it last.
14704 self.cgRoot.prepend(CGGeneric(rootedValue))
14705 elif operation.isGetter() or operation.isDeleter():
14706 if foundVar is None:
14707 self.cgRoot.prepend(CGGeneric("bool found = false;\n"))
14709 def getArguments(self):
14710 args = [(a, a.identifier.name) for a in self.arguments]
14711 if self.idlNode.isGetter() or self.idlNode.isDeleter():
14712 args.append(
14714 FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean]),
14715 self.foundVar,
14718 return args
14720 def wrap_return_value(self):
14721 if not self.idlNode.isGetter() or self.templateValues is None:
14722 return ""
14724 wrap = CGGeneric(
14725 wrapForType(self.returnType, self.descriptor, self.templateValues)
14727 if self.checkFound:
14728 wrap = CGIfWrapper(wrap, self.foundVar)
14729 else:
14730 wrap = CGList([CGGeneric("MOZ_ASSERT(" + self.foundVar + ");\n"), wrap])
14731 return "\n" + wrap.define()
14734 class CGProxyIndexedOperation(CGProxySpecialOperation):
14736 Class to generate a call to an indexed operation.
14738 If doUnwrap is False, the caller is responsible for making sure a variable
14739 named 'self' holds the C++ object somewhere where the code we generate
14740 will see it.
14742 If checkFound is False, will just assert that the prop is found instead of
14743 checking that it is before wrapping the value.
14745 resultVar: See the docstring for CGCallGenerator.
14747 foundVar: See the docstring for CGProxySpecialOperation.
14750 def __init__(
14751 self,
14752 descriptor,
14753 name,
14754 doUnwrap=True,
14755 checkFound=True,
14756 argumentHandleValue=None,
14757 resultVar=None,
14758 foundVar=None,
14760 self.doUnwrap = doUnwrap
14761 CGProxySpecialOperation.__init__(
14762 self,
14763 descriptor,
14764 name,
14765 checkFound,
14766 argumentHandleValue=argumentHandleValue,
14767 resultVar=resultVar,
14768 foundVar=foundVar,
14771 def define(self):
14772 # Our first argument is the id we're getting.
14773 argName = self.arguments[0].identifier.name
14774 if argName == "index":
14775 # We already have our index in a variable with that name
14776 setIndex = ""
14777 else:
14778 setIndex = "uint32_t %s = index;\n" % argName
14779 if self.doUnwrap:
14780 unwrap = "%s* self = UnwrapProxy(proxy);\n" % self.descriptor.nativeType
14781 else:
14782 unwrap = ""
14783 return setIndex + unwrap + CGProxySpecialOperation.define(self)
14786 class CGProxyIndexedGetter(CGProxyIndexedOperation):
14788 Class to generate a call to an indexed getter. If templateValues is not None
14789 the returned value will be wrapped with wrapForType using templateValues.
14791 If doUnwrap is False, the caller is responsible for making sure a variable
14792 named 'self' holds the C++ object somewhere where the code we generate
14793 will see it.
14795 If checkFound is False, will just assert that the prop is found instead of
14796 checking that it is before wrapping the value.
14798 foundVar: See the docstring for CGProxySpecialOperation.
14801 def __init__(
14802 self,
14803 descriptor,
14804 templateValues=None,
14805 doUnwrap=True,
14806 checkFound=True,
14807 foundVar=None,
14809 self.templateValues = templateValues
14810 CGProxyIndexedOperation.__init__(
14811 self, descriptor, "IndexedGetter", doUnwrap, checkFound, foundVar=foundVar
14815 class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter):
14817 Class to generate a call that checks whether an indexed property exists.
14819 For now, we just delegate to CGProxyIndexedGetter
14821 foundVar: See the docstring for CGProxySpecialOperation.
14824 def __init__(self, descriptor, foundVar):
14825 CGProxyIndexedGetter.__init__(self, descriptor, foundVar=foundVar)
14826 self.cgRoot.append(CGGeneric("(void)result;\n"))
14829 class CGProxyIndexedSetter(CGProxyIndexedOperation):
14831 Class to generate a call to an indexed setter.
14834 def __init__(self, descriptor, argumentHandleValue=None):
14835 CGProxyIndexedOperation.__init__(
14836 self, descriptor, "IndexedSetter", argumentHandleValue=argumentHandleValue
14840 class CGProxyNamedOperation(CGProxySpecialOperation):
14842 Class to generate a call to a named operation.
14844 'value' is the jsval to use for the name; None indicates that it should be
14845 gotten from the property id.
14847 resultVar: See the docstring for CGCallGenerator.
14849 foundVar: See the docstring for CGProxySpecialOperation.
14851 tailCode: if we end up with a non-symbol string id, run this code after
14852 we do all our other work.
14855 def __init__(
14856 self,
14857 descriptor,
14858 name,
14859 value=None,
14860 argumentHandleValue=None,
14861 resultVar=None,
14862 foundVar=None,
14863 tailCode="",
14865 CGProxySpecialOperation.__init__(
14866 self,
14867 descriptor,
14868 name,
14869 argumentHandleValue=argumentHandleValue,
14870 resultVar=resultVar,
14871 foundVar=foundVar,
14873 self.value = value
14874 self.tailCode = tailCode
14876 def define(self):
14877 # Our first argument is the id we're getting.
14878 argName = self.arguments[0].identifier.name
14879 if argName == "id":
14880 # deal with the name collision
14881 decls = "JS::Rooted<jsid> id_(cx, id);\n"
14882 idName = "id_"
14883 else:
14884 decls = ""
14885 idName = "id"
14887 decls += "FakeString<char16_t> %s;\n" % argName
14889 main = fill(
14891 ${nativeType}* self = UnwrapProxy(proxy);
14892 $*{op}
14893 $*{tailCode}
14894 """,
14895 nativeType=self.descriptor.nativeType,
14896 op=CGProxySpecialOperation.define(self),
14897 tailCode=self.tailCode,
14900 if self.value is None:
14901 return fill(
14903 $*{decls}
14904 bool isSymbol;
14905 if (!ConvertIdToString(cx, ${idName}, ${argName}, isSymbol)) {
14906 return false;
14908 if (!isSymbol) {
14909 $*{main}
14911 """,
14912 decls=decls,
14913 idName=idName,
14914 argName=argName,
14915 main=main,
14918 # Sadly, we have to set up nameVal even if we have an atom id,
14919 # because we don't know for sure, and we can end up needing it
14920 # so it needs to be higher up the stack. Using a Maybe here
14921 # seems like probable overkill.
14922 return fill(
14924 $*{decls}
14925 JS::Rooted<JS::Value> nameVal(cx, ${value});
14926 if (!nameVal.isSymbol()) {
14927 if (!ConvertJSValueToString(cx, nameVal, eStringify, eStringify,
14928 ${argName})) {
14929 return false;
14931 $*{main}
14933 """,
14934 decls=decls,
14935 value=self.value,
14936 argName=argName,
14937 main=main,
14941 class CGProxyNamedGetter(CGProxyNamedOperation):
14943 Class to generate a call to an named getter. If templateValues is not None
14944 the returned value will be wrapped with wrapForType using templateValues.
14945 'value' is the jsval to use for the name; None indicates that it should be
14946 gotten from the property id.
14948 foundVar: See the docstring for CGProxySpecialOperation.
14951 def __init__(self, descriptor, templateValues=None, value=None, foundVar=None):
14952 self.templateValues = templateValues
14953 CGProxyNamedOperation.__init__(
14954 self, descriptor, "NamedGetter", value, foundVar=foundVar
14958 class CGProxyNamedPresenceChecker(CGProxyNamedGetter):
14960 Class to generate a call that checks whether a named property exists.
14962 For now, we just delegate to CGProxyNamedGetter
14964 foundVar: See the docstring for CGProxySpecialOperation.
14967 def __init__(self, descriptor, foundVar=None):
14968 CGProxyNamedGetter.__init__(self, descriptor, foundVar=foundVar)
14969 self.cgRoot.append(CGGeneric("(void)result;\n"))
14972 class CGProxyNamedSetter(CGProxyNamedOperation):
14974 Class to generate a call to a named setter.
14977 def __init__(self, descriptor, tailCode, argumentHandleValue=None):
14978 CGProxyNamedOperation.__init__(
14979 self,
14980 descriptor,
14981 "NamedSetter",
14982 argumentHandleValue=argumentHandleValue,
14983 tailCode=tailCode,
14987 class CGProxyNamedDeleter(CGProxyNamedOperation):
14989 Class to generate a call to a named deleter.
14991 resultVar: See the docstring for CGCallGenerator.
14993 foundVar: See the docstring for CGProxySpecialOperation.
14996 def __init__(self, descriptor, resultVar=None, foundVar=None):
14997 CGProxyNamedOperation.__init__(
14998 self, descriptor, "NamedDeleter", resultVar=resultVar, foundVar=foundVar
15002 class CGProxyIsProxy(CGAbstractMethod):
15003 def __init__(self, descriptor):
15004 args = [Argument("JSObject*", "obj")]
15005 CGAbstractMethod.__init__(
15006 self, descriptor, "IsProxy", "bool", args, alwaysInline=True
15009 def declare(self):
15010 return ""
15012 def definition_body(self):
15013 return "return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();\n"
15016 class CGProxyUnwrap(CGAbstractMethod):
15017 def __init__(self, descriptor):
15018 args = [Argument("JSObject*", "obj")]
15019 CGAbstractMethod.__init__(
15020 self,
15021 descriptor,
15022 "UnwrapProxy",
15023 descriptor.nativeType + "*",
15024 args,
15025 alwaysInline=True,
15028 def declare(self):
15029 return ""
15031 def definition_body(self):
15032 return fill(
15034 MOZ_ASSERT(js::IsProxy(obj));
15035 if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) {
15036 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj));
15037 obj = js::UncheckedUnwrap(obj);
15039 MOZ_ASSERT(IsProxy(obj));
15040 return static_cast<${type}*>(js::GetProxyReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate());
15041 """,
15042 type=self.descriptor.nativeType,
15046 MISSING_PROP_PREF = "dom.missing_prop_counters.enabled"
15049 def missingPropUseCountersForDescriptor(desc):
15050 if not desc.needsMissingPropUseCounters:
15051 return ""
15053 return fill(
15055 if (StaticPrefs::${pref}() && id.isAtom()) {
15056 CountMaybeMissingProperty(proxy, id);
15059 """,
15060 pref=prefIdentifier(MISSING_PROP_PREF),
15064 def findAncestorWithInstrumentedProps(desc):
15066 Find an ancestor of desc.interface (not including desc.interface
15067 itself) that has instrumented properties on it. May return None
15068 if there is no such ancestor.
15070 ancestor = desc.interface.parent
15071 while ancestor:
15072 if ancestor.getExtendedAttribute("InstrumentedProps"):
15073 return ancestor
15074 ancestor = ancestor.parent
15075 return None
15078 class CGCountMaybeMissingProperty(CGAbstractMethod):
15079 def __init__(self, descriptor):
15081 Returns whether we counted the property involved.
15083 CGAbstractMethod.__init__(
15084 self,
15085 descriptor,
15086 "CountMaybeMissingProperty",
15087 "bool",
15089 Argument("JS::Handle<JSObject*>", "proxy"),
15090 Argument("JS::Handle<jsid>", "id"),
15094 def gen_switch(self, switchDecriptor):
15096 Generate a switch from the switch descriptor. The descriptor
15097 dictionary must have the following properties:
15099 1) A "precondition" property that contains code to run before the
15100 switch statement. Its value ie a string.
15101 2) A "condition" property for the condition. Its value is a string.
15102 3) A "cases" property. Its value is an object that has property names
15103 corresponding to the case labels. The values of those properties
15104 are either new switch descriptor dictionaries (which will then
15105 generate nested switches) or strings to use for case bodies.
15107 cases = []
15108 for label, body in sorted(switchDecriptor["cases"].items()):
15109 if isinstance(body, dict):
15110 body = self.gen_switch(body)
15111 cases.append(
15112 fill(
15114 case ${label}: {
15115 $*{body}
15116 break;
15118 """,
15119 label=label,
15120 body=body,
15123 return fill(
15125 $*{precondition}
15126 switch (${condition}) {
15127 $*{cases}
15129 """,
15130 precondition=switchDecriptor["precondition"],
15131 condition=switchDecriptor["condition"],
15132 cases="".join(cases),
15135 def charSwitch(self, props, charIndex):
15137 Create a switch for the given props, based on the first char where
15138 they start to differ at index charIndex or more. Each prop is a tuple
15139 containing interface name and prop name.
15141 Incoming props should be a sorted list.
15143 if len(props) == 1:
15144 # We're down to one string: just check whether we match it.
15145 return fill(
15147 if (JS_LinearStringEqualsLiteral(str, "${name}")) {
15148 counter.emplace(eUseCounter_${iface}_${name});
15150 """,
15151 iface=self.descriptor.name,
15152 name=props[0],
15155 switch = dict()
15156 if charIndex == 0:
15157 switch["precondition"] = "StringIdChars chars(nogc, str);\n"
15158 else:
15159 switch["precondition"] = ""
15161 # Find the first place where we might actually have a difference.
15162 while all(prop[charIndex] == props[0][charIndex] for prop in props):
15163 charIndex += 1
15165 switch["condition"] = "chars[%d]" % charIndex
15166 switch["cases"] = dict()
15167 current_props = None
15168 curChar = None
15169 idx = 0
15170 while idx < len(props):
15171 nextChar = "'%s'" % props[idx][charIndex]
15172 if nextChar != curChar:
15173 if curChar:
15174 switch["cases"][curChar] = self.charSwitch(
15175 current_props, charIndex + 1
15177 current_props = []
15178 curChar = nextChar
15179 current_props.append(props[idx])
15180 idx += 1
15181 switch["cases"][curChar] = self.charSwitch(current_props, charIndex + 1)
15182 return switch
15184 def definition_body(self):
15185 ancestor = findAncestorWithInstrumentedProps(self.descriptor)
15187 if ancestor:
15188 body = fill(
15190 if (${ancestor}_Binding::CountMaybeMissingProperty(proxy, id)) {
15191 return true;
15194 """,
15195 ancestor=ancestor.identifier.name,
15197 else:
15198 body = ""
15200 instrumentedProps = self.descriptor.instrumentedProps
15201 if not instrumentedProps:
15202 return body + dedent(
15204 return false;
15208 lengths = set(len(prop) for prop in instrumentedProps)
15209 switchDesc = {"condition": "JS::GetLinearStringLength(str)", "precondition": ""}
15210 switchDesc["cases"] = dict()
15211 for length in sorted(lengths):
15212 switchDesc["cases"][str(length)] = self.charSwitch(
15213 list(sorted(prop for prop in instrumentedProps if len(prop) == length)),
15217 return body + fill(
15219 MOZ_ASSERT(StaticPrefs::${pref}() && id.isAtom());
15220 Maybe<UseCounter> counter;
15222 // Scope for our no-GC section, so we don't need to rely on SetUseCounter not GCing.
15223 JS::AutoCheckCannotGC nogc;
15224 JSLinearString* str = JS::AtomToLinearString(id.toAtom());
15225 // Don't waste time fetching the chars until we've done the length switch.
15226 $*{switch}
15228 if (counter) {
15229 SetUseCounter(proxy, *counter);
15230 return true;
15233 return false;
15234 """,
15235 pref=prefIdentifier(MISSING_PROP_PREF),
15236 switch=self.gen_switch(switchDesc),
15240 class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod):
15241 def __init__(self, descriptor):
15242 args = [
15243 Argument("JSContext*", "cx"),
15244 Argument("JS::Handle<JSObject*>", "proxy"),
15245 Argument("JS::Handle<jsid>", "id"),
15246 Argument("bool", "ignoreNamedProps"),
15247 Argument("JS::MutableHandle<Maybe<JS::PropertyDescriptor>>", "desc"),
15249 ClassMethod.__init__(
15250 self,
15251 "getOwnPropDescriptor",
15252 "bool",
15253 args,
15254 virtual=True,
15255 override=True,
15256 const=True,
15258 self.descriptor = descriptor
15260 def getBody(self):
15261 indexedSetter = self.descriptor.operations["IndexedSetter"]
15263 if self.descriptor.isMaybeCrossOriginObject():
15264 xrayDecl = dedent(
15266 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
15267 MOZ_ASSERT(IsPlatformObjectSameOrigin(cx, proxy),
15268 "getOwnPropertyDescriptor() and set() should have dealt");
15269 MOZ_ASSERT(js::IsObjectInContextCompartment(proxy, cx),
15270 "getOwnPropertyDescriptor() and set() should have dealt");
15274 xrayCheck = ""
15275 else:
15276 xrayDecl = "bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);\n"
15277 xrayCheck = "!isXray &&"
15279 if self.descriptor.supportsIndexedProperties():
15280 attributes = [
15281 "JS::PropertyAttribute::Configurable",
15282 "JS::PropertyAttribute::Enumerable",
15284 if indexedSetter is not None:
15285 attributes.append("JS::PropertyAttribute::Writable")
15286 setDescriptor = (
15287 "desc.set(mozilla::Some(JS::PropertyDescriptor::Data(value, { %s })));\nreturn true;\n"
15288 % ", ".join(attributes)
15290 templateValues = {
15291 "jsvalRef": "value",
15292 "jsvalHandle": "&value",
15293 "obj": "proxy",
15294 "successCode": setDescriptor,
15296 getIndexed = fill(
15298 uint32_t index = GetArrayIndexFromId(id);
15299 if (IsArrayIndex(index)) {
15300 JS::Rooted<JS::Value> value(cx);
15301 $*{callGetter}
15304 """,
15305 callGetter=CGProxyIndexedGetter(
15306 self.descriptor, templateValues
15307 ).define(),
15309 else:
15310 getIndexed = ""
15312 missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor)
15314 if self.descriptor.supportsNamedProperties():
15315 operations = self.descriptor.operations
15316 attributes = ["JS::PropertyAttribute::Configurable"]
15317 if self.descriptor.namedPropertiesEnumerable:
15318 attributes.append("JS::PropertyAttribute::Enumerable")
15319 if operations["NamedSetter"] is not None:
15320 attributes.append("JS::PropertyAttribute::Writable")
15321 setDescriptor = (
15322 "desc.set(mozilla::Some(JS::PropertyDescriptor::Data(value, { %s })));\nreturn true;\n"
15323 % ", ".join(attributes)
15325 templateValues = {
15326 "jsvalRef": "value",
15327 "jsvalHandle": "&value",
15328 "obj": "proxy",
15329 "successCode": setDescriptor,
15332 computeCondition = dedent(
15334 bool hasOnProto;
15335 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
15336 return false;
15338 callNamedGetter = !hasOnProto;
15341 if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
15342 computeCondition = fill(
15344 if (!isXray) {
15345 callNamedGetter = true;
15346 } else {
15347 $*{hasOnProto}
15349 """,
15350 hasOnProto=computeCondition,
15353 outerCondition = "!ignoreNamedProps"
15354 if self.descriptor.supportsIndexedProperties():
15355 outerCondition = "!IsArrayIndex(index) && " + outerCondition
15357 namedGetCode = CGProxyNamedGetter(self.descriptor, templateValues).define()
15358 namedGet = fill(
15360 bool callNamedGetter = false;
15361 if (${outerCondition}) {
15362 $*{computeCondition}
15364 if (callNamedGetter) {
15365 JS::Rooted<JS::Value> value(cx);
15366 $*{namedGetCode}
15368 """,
15369 outerCondition=outerCondition,
15370 computeCondition=computeCondition,
15371 namedGetCode=namedGetCode,
15373 namedGet += "\n"
15374 else:
15375 namedGet = ""
15377 return fill(
15379 $*{xrayDecl}
15380 $*{getIndexed}
15381 $*{missingPropUseCounters}
15382 JS::Rooted<JSObject*> expando(cx);
15383 if (${xrayCheck}(expando = GetExpandoObject(proxy))) {
15384 if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc)) {
15385 return false;
15387 if (desc.isSome()) {
15388 return true;
15392 $*{namedGet}
15393 desc.reset();
15394 return true;
15395 """,
15396 xrayDecl=xrayDecl,
15397 xrayCheck=xrayCheck,
15398 getIndexed=getIndexed,
15399 missingPropUseCounters=missingPropUseCounters,
15400 namedGet=namedGet,
15404 class CGDOMJSProxyHandler_defineProperty(ClassMethod):
15405 def __init__(self, descriptor):
15406 # The usual convention is to name the ObjectOpResult out-parameter
15407 # `result`, but that name is a bit overloaded around here.
15408 args = [
15409 Argument("JSContext*", "cx_"),
15410 Argument("JS::Handle<JSObject*>", "proxy"),
15411 Argument("JS::Handle<jsid>", "id"),
15412 Argument("JS::Handle<JS::PropertyDescriptor>", "desc"),
15413 Argument("JS::ObjectOpResult&", "opresult"),
15414 Argument("bool*", "done"),
15416 ClassMethod.__init__(
15417 self,
15418 "defineProperty",
15419 "bool",
15420 args,
15421 virtual=True,
15422 override=True,
15423 const=True,
15425 self.descriptor = descriptor
15427 def getBody(self):
15428 set = ""
15430 indexedSetter = self.descriptor.operations["IndexedSetter"]
15431 if indexedSetter:
15432 error_label = CGSpecializedMethod.error_reporting_label_helper(
15433 self.descriptor, indexedSetter, isConstructor=False
15435 if error_label:
15436 cxDecl = fill(
15438 BindingCallContext cx(cx_, ${error_label});
15439 """,
15440 error_label=error_label,
15442 else:
15443 cxDecl = dedent(
15445 JSContext* cx = cx_;
15448 set += fill(
15450 uint32_t index = GetArrayIndexFromId(id);
15451 if (IsArrayIndex(index)) {
15452 $*{cxDecl}
15453 *done = true;
15454 // https://webidl.spec.whatwg.org/#legacy-platform-object-defineownproperty
15455 // Step 1.1. The no-indexed-setter case is handled by step 1.2.
15456 if (!desc.isDataDescriptor()) {
15457 return opresult.failNotDataDescriptor();
15460 $*{callSetter}
15461 return opresult.succeed();
15463 """,
15464 cxDecl=cxDecl,
15465 callSetter=CGProxyIndexedSetter(self.descriptor).define(),
15467 elif self.descriptor.supportsIndexedProperties():
15468 # We allow untrusted content to prevent Xrays from setting a
15469 # property if that property is an indexed property and we have no
15470 # indexed setter. That's how the object would normally behave if
15471 # you tried to set the property on it. That means we don't need to
15472 # do anything special for Xrays here.
15473 set += dedent(
15475 if (IsArrayIndex(GetArrayIndexFromId(id))) {
15476 *done = true;
15477 return opresult.failNoIndexedSetter();
15482 namedSetter = self.descriptor.operations["NamedSetter"]
15483 if namedSetter:
15484 error_label = CGSpecializedMethod.error_reporting_label_helper(
15485 self.descriptor, namedSetter, isConstructor=False
15487 if error_label:
15488 set += fill(
15490 BindingCallContext cx(cx_, ${error_label});
15491 """,
15492 error_label=error_label,
15494 else:
15495 set += dedent(
15497 JSContext* cx = cx_;
15500 if self.descriptor.hasLegacyUnforgeableMembers:
15501 raise TypeError(
15502 "Can't handle a named setter on an interface "
15503 "that has unforgeables. Figure out how that "
15504 "should work!"
15506 set += dedent(
15508 // https://webidl.spec.whatwg.org/#legacy-platform-object-defineownproperty
15509 // Step 2.2.2.1.
15510 if (!desc.isDataDescriptor()) {
15511 *done = true;
15512 return opresult.failNotDataDescriptor();
15516 tailCode = dedent(
15518 *done = true;
15519 return opresult.succeed();
15522 set += CGProxyNamedSetter(self.descriptor, tailCode).define()
15523 else:
15524 # We allow untrusted content to prevent Xrays from setting a
15525 # property if that property is already a named property on the
15526 # object and we have no named setter. That's how the object would
15527 # normally behave if you tried to set the property on it. That
15528 # means we don't need to do anything special for Xrays here.
15529 if self.descriptor.supportsNamedProperties():
15530 set += fill(
15532 JSContext* cx = cx_;
15533 bool found = false;
15534 $*{presenceChecker}
15536 if (found) {
15537 *done = true;
15538 return opresult.failNoNamedSetter();
15540 """,
15541 presenceChecker=CGProxyNamedPresenceChecker(
15542 self.descriptor, foundVar="found"
15543 ).define(),
15545 if self.descriptor.isMaybeCrossOriginObject():
15546 set += dedent(
15548 MOZ_ASSERT(IsPlatformObjectSameOrigin(cx_, proxy),
15549 "Why did the MaybeCrossOriginObject defineProperty override fail?");
15550 MOZ_ASSERT(js::IsObjectInContextCompartment(proxy, cx_),
15551 "Why did the MaybeCrossOriginObject defineProperty override fail?");
15555 # In all cases we want to tail-call to our base class; we can
15556 # always land here for symbols.
15557 set += (
15558 "return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n"
15559 % ", ".join(a.name for a in self.args)
15561 return set
15564 def getDeleterBody(descriptor, type, foundVar=None):
15566 type should be "Named" or "Indexed"
15568 The possible outcomes:
15569 - an error happened (the emitted code returns false)
15570 - own property not found (foundVar=false, deleteSucceeded=true)
15571 - own property found and deleted (foundVar=true, deleteSucceeded=true)
15572 - own property found but can't be deleted (foundVar=true, deleteSucceeded=false)
15574 assert type in ("Named", "Indexed")
15575 deleter = descriptor.operations[type + "Deleter"]
15576 if deleter:
15577 assert type == "Named"
15578 assert foundVar is not None
15579 if descriptor.hasLegacyUnforgeableMembers:
15580 raise TypeError(
15581 "Can't handle a deleter on an interface "
15582 "that has unforgeables. Figure out how "
15583 "that should work!"
15585 # See if the deleter method is fallible.
15586 t = deleter.signatures()[0][0]
15587 if t.isPrimitive() and not t.nullable() and t.tag() == IDLType.Tags.bool:
15588 # The deleter method has a boolean return value. When a
15589 # property is found, the return value indicates whether it
15590 # was successfully deleted.
15591 setDS = fill(
15593 if (!${foundVar}) {
15594 deleteSucceeded = true;
15596 """,
15597 foundVar=foundVar,
15599 else:
15600 # No boolean return value: if a property is found,
15601 # deleting it always succeeds.
15602 setDS = "deleteSucceeded = true;\n"
15604 body = (
15605 CGProxyNamedDeleter(
15606 descriptor, resultVar="deleteSucceeded", foundVar=foundVar
15607 ).define()
15608 + setDS
15610 elif getattr(descriptor, "supports%sProperties" % type)():
15611 presenceCheckerClass = globals()["CGProxy%sPresenceChecker" % type]
15612 foundDecl = ""
15613 if foundVar is None:
15614 foundVar = "found"
15615 foundDecl = "bool found = false;\n"
15616 body = fill(
15618 $*{foundDecl}
15619 $*{presenceChecker}
15620 deleteSucceeded = !${foundVar};
15621 """,
15622 foundDecl=foundDecl,
15623 presenceChecker=presenceCheckerClass(
15624 descriptor, foundVar=foundVar
15625 ).define(),
15626 foundVar=foundVar,
15628 else:
15629 body = None
15630 return body
15633 class CGDeleteNamedProperty(CGAbstractStaticMethod):
15634 def __init__(self, descriptor):
15635 args = [
15636 Argument("JSContext*", "cx"),
15637 Argument("JS::Handle<JSObject*>", "xray"),
15638 Argument("JS::Handle<JSObject*>", "proxy"),
15639 Argument("JS::Handle<jsid>", "id"),
15640 Argument("JS::ObjectOpResult&", "opresult"),
15642 CGAbstractStaticMethod.__init__(
15643 self, descriptor, "DeleteNamedProperty", "bool", args
15646 def definition_body(self):
15647 return fill(
15649 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(xray));
15650 MOZ_ASSERT(js::IsProxy(proxy));
15651 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
15652 JSAutoRealm ar(cx, proxy);
15653 bool deleteSucceeded = false;
15654 bool found = false;
15655 $*{namedBody}
15656 if (!found || deleteSucceeded) {
15657 return opresult.succeed();
15659 return opresult.failCantDelete();
15660 """,
15661 namedBody=getDeleterBody(self.descriptor, "Named", foundVar="found"),
15665 class CGDOMJSProxyHandler_delete(ClassMethod):
15666 def __init__(self, descriptor):
15667 args = [
15668 Argument("JSContext*", "cx"),
15669 Argument("JS::Handle<JSObject*>", "proxy"),
15670 Argument("JS::Handle<jsid>", "id"),
15671 Argument("JS::ObjectOpResult&", "opresult"),
15673 ClassMethod.__init__(
15674 self, "delete_", "bool", args, virtual=True, override=True, const=True
15676 self.descriptor = descriptor
15678 def getBody(self):
15679 delete = dedent(
15681 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
15682 "Should not have a XrayWrapper here");
15687 if self.descriptor.isMaybeCrossOriginObject():
15688 delete += dedent(
15690 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
15691 return ReportCrossOriginDenial(cx, id, "delete"_ns);
15694 // Safe to enter the Realm of proxy now.
15695 JSAutoRealm ar(cx, proxy);
15696 JS_MarkCrossZoneId(cx, id);
15700 indexedBody = getDeleterBody(self.descriptor, "Indexed")
15701 if indexedBody is not None:
15702 # Can't handle cross-origin objects here.
15703 assert not self.descriptor.isMaybeCrossOriginObject()
15704 delete += fill(
15706 uint32_t index = GetArrayIndexFromId(id);
15707 if (IsArrayIndex(index)) {
15708 bool deleteSucceeded;
15709 $*{indexedBody}
15710 return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
15712 """,
15713 indexedBody=indexedBody,
15716 namedBody = getDeleterBody(self.descriptor, "Named", foundVar="found")
15717 if namedBody is not None:
15718 delete += dedent(
15720 // Try named delete only if the named property visibility
15721 // algorithm says the property is visible.
15722 bool tryNamedDelete = true;
15723 { // Scope for expando
15724 JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
15725 if (expando) {
15726 bool hasProp;
15727 if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
15728 return false;
15730 tryNamedDelete = !hasProp;
15736 if not self.descriptor.interface.getExtendedAttribute(
15737 "LegacyOverrideBuiltIns"
15739 delete += dedent(
15741 if (tryNamedDelete) {
15742 bool hasOnProto;
15743 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
15744 return false;
15746 tryNamedDelete = !hasOnProto;
15751 # We always return above for an index id in the case when we support
15752 # indexed properties, so we can just treat the id as a name
15753 # unconditionally here.
15754 delete += fill(
15756 if (tryNamedDelete) {
15757 bool found = false;
15758 bool deleteSucceeded;
15759 $*{namedBody}
15760 if (found) {
15761 return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
15764 """,
15765 namedBody=namedBody,
15768 delete += dedent(
15771 return dom::DOMProxyHandler::delete_(cx, proxy, id, opresult);
15775 return delete
15778 class CGDOMJSProxyHandler_ownPropNames(ClassMethod):
15779 def __init__(
15780 self,
15781 descriptor,
15783 args = [
15784 Argument("JSContext*", "cx"),
15785 Argument("JS::Handle<JSObject*>", "proxy"),
15786 Argument("unsigned", "flags"),
15787 Argument("JS::MutableHandleVector<jsid>", "props"),
15789 ClassMethod.__init__(
15790 self, "ownPropNames", "bool", args, virtual=True, override=True, const=True
15792 self.descriptor = descriptor
15794 def getBody(self):
15795 if self.descriptor.isMaybeCrossOriginObject():
15796 xrayDecl = dedent(
15798 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
15799 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
15800 if (!(flags & JSITER_HIDDEN)) {
15801 // There are no enumerable cross-origin props, so we're done.
15802 return true;
15805 JS::Rooted<JSObject*> holder(cx);
15806 if (!EnsureHolder(cx, proxy, &holder)) {
15807 return false;
15810 if (!js::GetPropertyKeys(cx, holder, flags, props)) {
15811 return false;
15814 return xpc::AppendCrossOriginWhitelistedPropNames(cx, props);
15819 xrayCheck = ""
15820 else:
15821 xrayDecl = "bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);\n"
15822 xrayCheck = "!isXray &&"
15824 # Per spec, we do indices, then named props, then everything else.
15825 if self.descriptor.supportsIndexedProperties():
15826 if self.descriptor.lengthNeedsCallerType():
15827 callerType = callerTypeGetterForDescriptor(self.descriptor)
15828 else:
15829 callerType = ""
15830 addIndices = fill(
15833 uint32_t length = UnwrapProxy(proxy)->Length(${callerType});
15834 MOZ_ASSERT(int32_t(length) >= 0);
15835 for (int32_t i = 0; i < int32_t(length); ++i) {
15836 if (!props.append(JS::PropertyKey::Int(i))) {
15837 return false;
15840 """,
15841 callerType=callerType,
15843 else:
15844 addIndices = ""
15846 if self.descriptor.supportsNamedProperties():
15847 if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
15848 shadow = "!isXray"
15849 else:
15850 shadow = "false"
15852 if self.descriptor.supportedNamesNeedCallerType():
15853 callerType = ", " + callerTypeGetterForDescriptor(self.descriptor)
15854 else:
15855 callerType = ""
15857 addNames = fill(
15859 nsTArray<nsString> names;
15860 UnwrapProxy(proxy)->GetSupportedNames(names${callerType});
15861 if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) {
15862 return false;
15864 """,
15865 callerType=callerType,
15866 shadow=shadow,
15868 if not self.descriptor.namedPropertiesEnumerable:
15869 addNames = CGIfWrapper(
15870 CGGeneric(addNames), "flags & JSITER_HIDDEN"
15871 ).define()
15872 addNames = "\n" + addNames
15873 else:
15874 addNames = ""
15876 addExpandoProps = fill(
15878 JS::Rooted<JSObject*> expando(cx);
15879 if (${xrayCheck}(expando = DOMProxyHandler::GetExpandoObject(proxy)) &&
15880 !js::GetPropertyKeys(cx, expando, flags, props)) {
15881 return false;
15883 """,
15884 xrayCheck=xrayCheck,
15887 if self.descriptor.isMaybeCrossOriginObject():
15888 # We need to enter our compartment (which we might not be
15889 # in right now) to get the expando props.
15890 addExpandoProps = fill(
15892 { // Scope for accessing the expando.
15893 // Safe to enter our compartment, because IsPlatformObjectSameOrigin tested true.
15894 JSAutoRealm ar(cx, proxy);
15895 $*{addExpandoProps}
15897 for (auto& id : props) {
15898 JS_MarkCrossZoneId(cx, id);
15900 """,
15901 addExpandoProps=addExpandoProps,
15904 return fill(
15906 $*{xrayDecl}
15907 $*{addIndices}
15908 $*{addNames}
15910 $*{addExpandoProps}
15912 return true;
15913 """,
15914 xrayDecl=xrayDecl,
15915 addIndices=addIndices,
15916 addNames=addNames,
15917 addExpandoProps=addExpandoProps,
15921 class CGDOMJSProxyHandler_hasOwn(ClassMethod):
15922 def __init__(self, descriptor):
15923 args = [
15924 Argument("JSContext*", "cx"),
15925 Argument("JS::Handle<JSObject*>", "proxy"),
15926 Argument("JS::Handle<jsid>", "id"),
15927 Argument("bool*", "bp"),
15929 ClassMethod.__init__(
15930 self, "hasOwn", "bool", args, virtual=True, override=True, const=True
15932 self.descriptor = descriptor
15934 def getBody(self):
15935 if self.descriptor.isMaybeCrossOriginObject():
15936 maybeCrossOrigin = dedent(
15938 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
15939 // Just hand this off to BaseProxyHandler to do the slow-path thing.
15940 // The BaseProxyHandler code is OK with this happening without entering the
15941 // compartment of "proxy", which is important to get the right answers.
15942 return js::BaseProxyHandler::hasOwn(cx, proxy, id, bp);
15945 // Now safe to enter the Realm of proxy and do the rest of the work there.
15946 JSAutoRealm ar(cx, proxy);
15947 JS_MarkCrossZoneId(cx, id);
15950 else:
15951 maybeCrossOrigin = ""
15953 if self.descriptor.supportsIndexedProperties():
15954 indexed = fill(
15956 uint32_t index = GetArrayIndexFromId(id);
15957 if (IsArrayIndex(index)) {
15958 bool found = false;
15959 $*{presenceChecker}
15961 *bp = found;
15962 return true;
15965 """,
15966 presenceChecker=CGProxyIndexedPresenceChecker(
15967 self.descriptor, foundVar="found"
15968 ).define(),
15970 else:
15971 indexed = ""
15973 if self.descriptor.supportsNamedProperties():
15974 # If we support indexed properties we always return above for index
15975 # property names, so no need to check for those here.
15976 named = fill(
15978 bool found = false;
15979 $*{presenceChecker}
15981 *bp = found;
15982 """,
15983 presenceChecker=CGProxyNamedPresenceChecker(
15984 self.descriptor, foundVar="found"
15985 ).define(),
15987 if not self.descriptor.interface.getExtendedAttribute(
15988 "LegacyOverrideBuiltIns"
15990 named = fill(
15992 bool hasOnProto;
15993 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
15994 return false;
15996 if (!hasOnProto) {
15997 $*{protoLacksProperty}
15998 return true;
16000 """,
16001 protoLacksProperty=named,
16003 named += "*bp = false;\n"
16004 else:
16005 named += "\n"
16006 else:
16007 named = "*bp = false;\n"
16009 missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor)
16011 return fill(
16013 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
16014 "Should not have a XrayWrapper here");
16015 $*{maybeCrossOrigin}
16016 $*{indexed}
16018 $*{missingPropUseCounters}
16019 JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy));
16020 if (expando) {
16021 bool b = true;
16022 bool ok = JS_HasPropertyById(cx, expando, id, &b);
16023 *bp = !!b;
16024 if (!ok || *bp) {
16025 return ok;
16029 $*{named}
16030 return true;
16031 """,
16032 maybeCrossOrigin=maybeCrossOrigin,
16033 indexed=indexed,
16034 missingPropUseCounters=missingPropUseCounters,
16035 named=named,
16039 class CGDOMJSProxyHandler_get(ClassMethod):
16040 def __init__(self, descriptor):
16041 args = [
16042 Argument("JSContext*", "cx"),
16043 Argument("JS::Handle<JSObject*>", "proxy"),
16044 Argument("JS::Handle<JS::Value>", "receiver"),
16045 Argument("JS::Handle<jsid>", "id"),
16046 Argument("JS::MutableHandle<JS::Value>", "vp"),
16048 ClassMethod.__init__(
16049 self, "get", "bool", args, virtual=True, override=True, const=True
16051 self.descriptor = descriptor
16053 def getBody(self):
16054 missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor)
16056 getUnforgeableOrExpando = dedent(
16058 bool expandoHasProp = false;
16059 { // Scope for expando
16060 JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
16061 if (expando) {
16062 if (!JS_HasPropertyById(cx, expando, id, &expandoHasProp)) {
16063 return false;
16066 if (expandoHasProp) {
16067 // Forward the get to the expando object, but our receiver is whatever our
16068 // receiver is.
16069 if (!JS_ForwardGetPropertyTo(cx, expando, id, ${receiver}, vp)) {
16070 return false;
16078 getOnPrototype = dedent(
16080 bool foundOnPrototype;
16081 if (!GetPropertyOnPrototype(cx, proxy, ${receiver}, id, &foundOnPrototype, vp)) {
16082 return false;
16087 if self.descriptor.isMaybeCrossOriginObject():
16088 # We can't handle these for cross-origin objects
16089 assert not self.descriptor.supportsIndexedProperties()
16090 assert not self.descriptor.supportsNamedProperties()
16092 return fill(
16094 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
16095 "Should not have a XrayWrapper here");
16097 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
16098 return CrossOriginGet(cx, proxy, receiver, id, vp);
16101 $*{missingPropUseCounters}
16102 { // Scope for the JSAutoRealm accessing expando and prototype.
16103 JSAutoRealm ar(cx, proxy);
16104 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
16105 if (!MaybeWrapValue(cx, &wrappedReceiver)) {
16106 return false;
16108 JS_MarkCrossZoneId(cx, id);
16110 $*{getUnforgeableOrExpando}
16111 if (!expandoHasProp) {
16112 $*{getOnPrototype}
16113 if (!foundOnPrototype) {
16114 MOZ_ASSERT(vp.isUndefined());
16115 return true;
16120 return MaybeWrapValue(cx, vp);
16121 """,
16122 missingPropUseCounters=missingPropUseCountersForDescriptor(
16123 self.descriptor
16125 getUnforgeableOrExpando=fill(
16126 getUnforgeableOrExpando, receiver="wrappedReceiver"
16128 getOnPrototype=fill(getOnPrototype, receiver="wrappedReceiver"),
16131 templateValues = {"jsvalRef": "vp", "jsvalHandle": "vp", "obj": "proxy"}
16133 getUnforgeableOrExpando = fill(
16134 getUnforgeableOrExpando, receiver="receiver"
16135 ) + dedent(
16138 if (expandoHasProp) {
16139 return true;
16143 if self.descriptor.supportsIndexedProperties():
16144 getIndexedOrExpando = fill(
16146 uint32_t index = GetArrayIndexFromId(id);
16147 if (IsArrayIndex(index)) {
16148 $*{callGetter}
16149 // Even if we don't have this index, we don't forward the
16150 // get on to our expando object.
16151 } else {
16152 $*{getUnforgeableOrExpando}
16154 """,
16155 callGetter=CGProxyIndexedGetter(
16156 self.descriptor, templateValues
16157 ).define(),
16158 getUnforgeableOrExpando=getUnforgeableOrExpando,
16160 else:
16161 getIndexedOrExpando = getUnforgeableOrExpando
16163 if self.descriptor.supportsNamedProperties():
16164 getNamed = CGProxyNamedGetter(self.descriptor, templateValues)
16165 if self.descriptor.supportsIndexedProperties():
16166 getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)")
16167 getNamed = getNamed.define() + "\n"
16168 else:
16169 getNamed = ""
16171 getOnPrototype = fill(getOnPrototype, receiver="receiver") + dedent(
16174 if (foundOnPrototype) {
16175 return true;
16178 MOZ_ASSERT(vp.isUndefined());
16182 if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
16183 getNamedOrOnPrototype = getNamed + getOnPrototype
16184 else:
16185 getNamedOrOnPrototype = getOnPrototype + getNamed
16187 return fill(
16189 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
16190 "Should not have a XrayWrapper here");
16192 $*{missingPropUseCounters}
16193 $*{indexedOrExpando}
16195 $*{namedOrOnPropotype}
16196 return true;
16197 """,
16198 missingPropUseCounters=missingPropUseCounters,
16199 indexedOrExpando=getIndexedOrExpando,
16200 namedOrOnPropotype=getNamedOrOnPrototype,
16204 class CGDOMJSProxyHandler_setCustom(ClassMethod):
16205 def __init__(self, descriptor):
16206 args = [
16207 Argument("JSContext*", "cx_"),
16208 Argument("JS::Handle<JSObject*>", "proxy"),
16209 Argument("JS::Handle<jsid>", "id"),
16210 Argument("JS::Handle<JS::Value>", "v"),
16211 Argument("bool*", "done"),
16213 ClassMethod.__init__(
16214 self, "setCustom", "bool", args, virtual=True, override=True, const=True
16216 self.descriptor = descriptor
16218 def getBody(self):
16219 assertion = (
16220 "MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),\n"
16221 ' "Should not have a XrayWrapper here");\n'
16224 # Correctness first. If we have a NamedSetter and [LegacyOverrideBuiltIns],
16225 # always call the NamedSetter and never do anything else.
16226 namedSetter = self.descriptor.operations["NamedSetter"]
16227 if namedSetter is not None and self.descriptor.interface.getExtendedAttribute(
16228 "LegacyOverrideBuiltIns"
16230 # Check assumptions.
16231 if self.descriptor.supportsIndexedProperties():
16232 raise ValueError(
16233 "In interface "
16234 + self.descriptor.name
16235 + ": "
16236 + "Can't cope with [LegacyOverrideBuiltIns] and an indexed getter"
16238 if self.descriptor.hasLegacyUnforgeableMembers:
16239 raise ValueError(
16240 "In interface "
16241 + self.descriptor.name
16242 + ": "
16243 + "Can't cope with [LegacyOverrideBuiltIns] and unforgeable members"
16246 tailCode = dedent(
16248 *done = true;
16249 return true;
16252 callSetter = CGProxyNamedSetter(
16253 self.descriptor, tailCode, argumentHandleValue="v"
16255 error_label = CGSpecializedMethod.error_reporting_label_helper(
16256 self.descriptor, namedSetter, isConstructor=False
16258 if error_label:
16259 cxDecl = fill(
16261 BindingCallContext cx(cx_, ${error_label});
16262 """,
16263 error_label=error_label,
16265 else:
16266 cxDecl = dedent(
16268 JSContext* cx = cx_;
16271 return fill(
16273 $*{assertion}
16274 $*{cxDecl}
16275 $*{callSetter}
16276 *done = false;
16277 return true;
16278 """,
16279 assertion=assertion,
16280 cxDecl=cxDecl,
16281 callSetter=callSetter.define(),
16284 # As an optimization, if we are going to call an IndexedSetter, go
16285 # ahead and call it and have done.
16286 indexedSetter = self.descriptor.operations["IndexedSetter"]
16287 if indexedSetter is not None:
16288 error_label = CGSpecializedMethod.error_reporting_label_helper(
16289 self.descriptor, indexedSetter, isConstructor=False
16291 if error_label:
16292 cxDecl = fill(
16294 BindingCallContext cx(cx_, ${error_label});
16295 """,
16296 error_label=error_label,
16298 else:
16299 cxDecl = dedent(
16301 JSContext* cx = cx_;
16304 setIndexed = fill(
16306 uint32_t index = GetArrayIndexFromId(id);
16307 if (IsArrayIndex(index)) {
16308 $*{cxDecl}
16309 $*{callSetter}
16310 *done = true;
16311 return true;
16314 """,
16315 cxDecl=cxDecl,
16316 callSetter=CGProxyIndexedSetter(
16317 self.descriptor, argumentHandleValue="v"
16318 ).define(),
16320 else:
16321 setIndexed = ""
16323 return assertion + setIndexed + "*done = false;\n" "return true;\n"
16326 class CGDOMJSProxyHandler_className(ClassMethod):
16327 def __init__(self, descriptor):
16328 args = [
16329 Argument("JSContext*", "cx"),
16330 Argument("JS::Handle<JSObject*>", "proxy"),
16332 ClassMethod.__init__(
16333 self,
16334 "className",
16335 "const char*",
16336 args,
16337 virtual=True,
16338 override=True,
16339 const=True,
16341 self.descriptor = descriptor
16343 def getBody(self):
16344 if self.descriptor.isMaybeCrossOriginObject():
16345 crossOrigin = dedent(
16347 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
16348 return "Object";
16353 else:
16354 crossOrigin = ""
16355 return fill(
16357 $*{crossOrigin}
16358 return "${name}";
16359 """,
16360 crossOrigin=crossOrigin,
16361 name=self.descriptor.name,
16365 class CGDOMJSProxyHandler_trace(ClassMethod):
16366 def __init__(self, descriptor):
16367 args = [Argument("JSTracer*", "trc"), Argument("JSObject*", "proxy")]
16368 ClassMethod.__init__(
16369 self,
16370 "trace",
16371 "void",
16372 args,
16373 virtual=True,
16374 override=True,
16375 const=True,
16377 self.descriptor = descriptor
16379 def getBody(self):
16380 iface = getReflectedHTMLAttributesIface(self.descriptor)
16381 return fill(
16383 ${reflectedAttributesBase}::ReflectedHTMLAttributeSlots::Trace(${trc}, ${proxy});
16384 return Base::trace(${trc}, ${proxy});
16385 """,
16386 reflectedAttributesBase=toBindingNamespace(iface.identifier.name),
16387 trc=self.args[0].name,
16388 proxy=self.args[1].name,
16392 class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod):
16393 def __init__(self, descriptor):
16394 args = [Argument("const JS::Value&", "priv")]
16395 ClassMethod.__init__(
16396 self,
16397 "finalizeInBackground",
16398 "bool",
16399 args,
16400 virtual=True,
16401 override=True,
16402 const=True,
16404 self.descriptor = descriptor
16406 def getBody(self):
16407 return "return false;\n"
16410 class CGDOMJSProxyHandler_finalize(ClassMethod):
16411 def __init__(self, descriptor):
16412 args = [Argument("JS::GCContext*", "gcx"), Argument("JSObject*", "proxy")]
16413 ClassMethod.__init__(
16414 self, "finalize", "void", args, virtual=True, override=True, const=True
16416 self.descriptor = descriptor
16418 def getBody(self):
16419 return (
16420 "%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(proxy);\n"
16421 % (self.descriptor.nativeType, self.descriptor.nativeType)
16422 ) + finalizeHook(
16423 self.descriptor,
16424 FINALIZE_HOOK_NAME,
16425 self.args[0].name,
16426 self.args[1].name,
16427 ).define()
16430 class CGDOMJSProxyHandler_objectMoved(ClassMethod):
16431 def __init__(self, descriptor):
16432 args = [Argument("JSObject*", "obj"), Argument("JSObject*", "old")]
16433 ClassMethod.__init__(
16434 self, "objectMoved", "size_t", args, virtual=True, override=True, const=True
16436 self.descriptor = descriptor
16438 def getBody(self):
16439 return (
16440 "%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n"
16441 % (self.descriptor.nativeType, self.descriptor.nativeType)
16442 ) + objectMovedHook(
16443 self.descriptor,
16444 OBJECT_MOVED_HOOK_NAME,
16445 self.args[0].name,
16446 self.args[1].name,
16450 class CGDOMJSProxyHandler_getElements(ClassMethod):
16451 def __init__(self, descriptor):
16452 assert descriptor.supportsIndexedProperties()
16454 args = [
16455 Argument("JSContext*", "cx"),
16456 Argument("JS::Handle<JSObject*>", "proxy"),
16457 Argument("uint32_t", "begin"),
16458 Argument("uint32_t", "end"),
16459 Argument("js::ElementAdder*", "adder"),
16461 ClassMethod.__init__(
16462 self, "getElements", "bool", args, virtual=True, override=True, const=True
16464 self.descriptor = descriptor
16466 def getBody(self):
16467 # Just like ownPropertyKeys we'll assume that we have no holes, so
16468 # we have all properties from 0 to length. If that ever changes
16469 # (unlikely), we'll need to do something a bit more clever with how we
16470 # forward on to our ancestor.
16472 templateValues = {
16473 "jsvalRef": "temp",
16474 "jsvalHandle": "&temp",
16475 "obj": "proxy",
16476 "successCode": (
16477 "if (!adder->append(cx, temp)) return false;\n" "continue;\n"
16480 get = CGProxyIndexedGetter(
16481 self.descriptor, templateValues, False, False
16482 ).define()
16484 if self.descriptor.lengthNeedsCallerType():
16485 callerType = callerTypeGetterForDescriptor(self.descriptor)
16486 else:
16487 callerType = ""
16489 return fill(
16491 JS::Rooted<JS::Value> temp(cx);
16492 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
16493 "Should not have a XrayWrapper here");
16495 ${nativeType}* self = UnwrapProxy(proxy);
16496 uint32_t length = self->Length(${callerType});
16497 // Compute the end of the indices we'll get ourselves
16498 uint32_t ourEnd = std::clamp(length, begin, end);
16500 for (uint32_t index = begin; index < ourEnd; ++index) {
16501 $*{get}
16504 if (end > ourEnd) {
16505 JS::Rooted<JSObject*> proto(cx);
16506 if (!js::GetObjectProto(cx, proxy, &proto)) {
16507 return false;
16509 return js::GetElementsWithAdder(cx, proto, proxy, ourEnd, end, adder);
16512 return true;
16513 """,
16514 nativeType=self.descriptor.nativeType,
16515 callerType=callerType,
16516 get=get,
16520 class CGJSProxyHandler_getInstance(ClassMethod):
16521 def __init__(self, type):
16522 self.type = type
16523 ClassMethod.__init__(
16524 self, "getInstance", "const %s*" % self.type, [], static=True
16527 def getBody(self):
16528 return fill(
16530 static const ${type} instance;
16531 return &instance;
16532 """,
16533 type=self.type,
16537 class CGDOMJSProxyHandler_call(ClassMethod):
16538 def __init__(self):
16539 args = [
16540 Argument("JSContext*", "cx"),
16541 Argument("JS::Handle<JSObject*>", "proxy"),
16542 Argument("const JS::CallArgs&", "args"),
16545 ClassMethod.__init__(
16546 self, "call", "bool", args, virtual=True, override=True, const=True
16549 def getBody(self):
16550 return fill(
16552 return js::ForwardToNative(cx, ${legacyCaller}, args);
16553 """,
16554 legacyCaller=LEGACYCALLER_HOOK_NAME,
16558 class CGDOMJSProxyHandler_isCallable(ClassMethod):
16559 def __init__(self):
16560 ClassMethod.__init__(
16561 self,
16562 "isCallable",
16563 "bool",
16564 [Argument("JSObject*", "obj")],
16565 virtual=True,
16566 override=True,
16567 const=True,
16570 def getBody(self):
16571 return dedent(
16573 return true;
16578 class CGDOMJSProxyHandler_canNurseryAllocate(ClassMethod):
16580 Override the default canNurseryAllocate in BaseProxyHandler, for cases when
16581 we should be nursery-allocated.
16584 def __init__(self):
16585 ClassMethod.__init__(
16586 self,
16587 "canNurseryAllocate",
16588 "bool",
16590 virtual=True,
16591 override=True,
16592 const=True,
16595 def getBody(self):
16596 return dedent(
16598 return true;
16603 class CGDOMJSProxyHandler_getOwnPropertyDescriptor(ClassMethod):
16605 Implementation of getOwnPropertyDescriptor. We only use this for
16606 cross-origin objects.
16609 def __init__(self, descriptor):
16610 assert descriptor.isMaybeCrossOriginObject()
16612 args = [
16613 Argument("JSContext*", "cx"),
16614 Argument("JS::Handle<JSObject*>", "proxy"),
16615 Argument("JS::Handle<jsid>", "id"),
16616 Argument("JS::MutableHandle<Maybe<JS::PropertyDescriptor>>", "desc"),
16618 ClassMethod.__init__(
16619 self,
16620 "getOwnPropertyDescriptor",
16621 "bool",
16622 args,
16623 virtual=True,
16624 override=True,
16625 const=True,
16627 self.descriptor = descriptor
16629 def getBody(self):
16630 return dedent(
16632 // Implementation of <https://html.spec.whatwg.org/multipage/history.html#location-getownproperty>.
16633 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
16635 // Step 1.
16636 if (IsPlatformObjectSameOrigin(cx, proxy)) {
16637 { // Scope so we can wrap our PropertyDescriptor back into
16638 // the caller compartment.
16639 // Enter the Realm of "proxy" so we can work with it.
16640 JSAutoRealm ar(cx, proxy);
16642 JS_MarkCrossZoneId(cx, id);
16644 // The spec messes around with configurability of the returned
16645 // descriptor here, but it's not clear what should actually happen
16646 // here. See <https://github.com/whatwg/html/issues/4157>. For
16647 // now, keep our old behavior and don't do any magic.
16648 if (!dom::DOMProxyHandler::getOwnPropertyDescriptor(cx, proxy, id, desc)) {
16649 return false;
16652 return JS_WrapPropertyDescriptor(cx, desc);
16655 // Step 2.
16656 if (!CrossOriginGetOwnPropertyHelper(cx, proxy, id, desc)) {
16657 return false;
16660 // Step 3.
16661 if (desc.isSome()) {
16662 return true;
16665 // And step 4.
16666 return CrossOriginPropertyFallback(cx, proxy, id, desc);
16671 class CGDOMJSProxyHandler_getSameOriginPrototype(ClassMethod):
16673 Implementation of getSameOriginPrototype. We only use this for
16674 cross-origin objects.
16677 def __init__(self, descriptor):
16678 assert descriptor.isMaybeCrossOriginObject()
16680 args = [Argument("JSContext*", "cx")]
16681 ClassMethod.__init__(
16682 self,
16683 "getSameOriginPrototype",
16684 "JSObject*",
16685 args,
16686 virtual=True,
16687 override=True,
16688 const=True,
16690 self.descriptor = descriptor
16692 def getBody(self):
16693 return dedent(
16695 return GetProtoObjectHandle(cx);
16700 class CGDOMJSProxyHandler_definePropertySameOrigin(ClassMethod):
16702 Implementation of definePropertySameOrigin. We only use this for
16703 cross-origin objects.
16706 def __init__(self, descriptor):
16707 assert descriptor.isMaybeCrossOriginObject()
16709 args = [
16710 Argument("JSContext*", "cx"),
16711 Argument("JS::Handle<JSObject*>", "proxy"),
16712 Argument("JS::Handle<jsid>", "id"),
16713 Argument("JS::Handle<JS::PropertyDescriptor>", "desc"),
16714 Argument("JS::ObjectOpResult&", "result"),
16716 ClassMethod.__init__(
16717 self,
16718 "definePropertySameOrigin",
16719 "bool",
16720 args,
16721 virtual=True,
16722 override=True,
16723 const=True,
16725 self.descriptor = descriptor
16727 def getBody(self):
16728 return dedent(
16730 return dom::DOMProxyHandler::defineProperty(cx, proxy, id, desc, result);
16735 class CGDOMJSProxyHandler_set(ClassMethod):
16737 Implementation of set(). We only use this for cross-origin objects.
16740 def __init__(self, descriptor):
16741 assert descriptor.isMaybeCrossOriginObject()
16743 args = [
16744 Argument("JSContext*", "cx"),
16745 Argument("JS::Handle<JSObject*>", "proxy"),
16746 Argument("JS::Handle<jsid>", "id"),
16747 Argument("JS::Handle<JS::Value>", "v"),
16748 Argument("JS::Handle<JS::Value>", "receiver"),
16749 Argument("JS::ObjectOpResult&", "result"),
16751 ClassMethod.__init__(
16752 self, "set", "bool", args, virtual=True, override=True, const=True
16754 self.descriptor = descriptor
16756 def getBody(self):
16757 return dedent(
16759 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
16760 return CrossOriginSet(cx, proxy, id, v, receiver, result);
16763 // Safe to enter the Realm of proxy now, since it's same-origin with us.
16764 JSAutoRealm ar(cx, proxy);
16765 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
16766 if (!MaybeWrapValue(cx, &wrappedReceiver)) {
16767 return false;
16770 JS::Rooted<JS::Value> wrappedValue(cx, v);
16771 if (!MaybeWrapValue(cx, &wrappedValue)) {
16772 return false;
16775 JS_MarkCrossZoneId(cx, id);
16777 return dom::DOMProxyHandler::set(cx, proxy, id, wrappedValue, wrappedReceiver, result);
16782 class CGDOMJSProxyHandler_EnsureHolder(ClassMethod):
16784 Implementation of EnsureHolder(). We only use this for cross-origin objects.
16787 def __init__(self, descriptor):
16788 args = [
16789 Argument("JSContext*", "cx"),
16790 Argument("JS::Handle<JSObject*>", "proxy"),
16791 Argument("JS::MutableHandle<JSObject*>", "holder"),
16793 ClassMethod.__init__(
16794 self, "EnsureHolder", "bool", args, virtual=True, override=True, const=True
16796 self.descriptor = descriptor
16798 def getBody(self):
16799 return dedent(
16801 return EnsureHolder(cx, proxy,
16802 JSCLASS_RESERVED_SLOTS(JS::GetClass(proxy)) - 1,
16803 sCrossOriginProperties, holder);
16808 class CGDOMJSProxyHandler(CGClass):
16809 def __init__(self, descriptor):
16810 assert (
16811 descriptor.supportsIndexedProperties()
16812 or descriptor.supportsNamedProperties()
16813 or descriptor.isMaybeCrossOriginObject()
16816 if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
16817 assert not descriptor.isMaybeCrossOriginObject()
16818 parentClass = "ShadowingDOMProxyHandler"
16819 elif descriptor.isMaybeCrossOriginObject():
16820 parentClass = "MaybeCrossOriginObject<mozilla::dom::DOMProxyHandler>"
16821 else:
16822 parentClass = "mozilla::dom::DOMProxyHandler"
16824 typeAliases = [
16825 ClassUsingDeclaration("Base", parentClass),
16827 methods = [
16828 CGDOMJSProxyHandler_getOwnPropDescriptor(descriptor),
16829 CGDOMJSProxyHandler_defineProperty(descriptor),
16830 ClassUsingFromBaseDeclaration(
16831 "mozilla::dom::DOMProxyHandler", "defineProperty"
16833 CGDOMJSProxyHandler_ownPropNames(descriptor),
16834 CGDOMJSProxyHandler_hasOwn(descriptor),
16835 CGDOMJSProxyHandler_get(descriptor),
16836 CGDOMJSProxyHandler_className(descriptor),
16837 CGDOMJSProxyHandler_finalizeInBackground(descriptor),
16838 CGDOMJSProxyHandler_finalize(descriptor),
16839 CGJSProxyHandler_getInstance("DOMProxyHandler"),
16840 CGDOMJSProxyHandler_delete(descriptor),
16842 if getReflectedHTMLAttributesIface(descriptor):
16843 methods.append(CGDOMJSProxyHandler_trace(descriptor))
16844 constructors = [
16845 ClassConstructor([], constexpr=True, visibility="public", explicit=True)
16848 if descriptor.supportsIndexedProperties():
16849 methods.append(CGDOMJSProxyHandler_getElements(descriptor))
16850 if descriptor.operations["IndexedSetter"] is not None or (
16851 descriptor.operations["NamedSetter"] is not None
16852 and descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns")
16854 methods.append(CGDOMJSProxyHandler_setCustom(descriptor))
16855 if descriptor.operations["LegacyCaller"]:
16856 methods.append(CGDOMJSProxyHandler_call())
16857 methods.append(CGDOMJSProxyHandler_isCallable())
16858 if descriptor.interface.hasProbablyShortLivingWrapper():
16859 if not descriptor.wrapperCache:
16860 raise TypeError(
16861 "Need a wrapper cache to support nursery "
16862 "allocation of DOM objects"
16864 methods.append(CGDOMJSProxyHandler_canNurseryAllocate())
16865 if descriptor.wrapperCache:
16866 methods.append(CGDOMJSProxyHandler_objectMoved(descriptor))
16868 if descriptor.isMaybeCrossOriginObject():
16869 methods.extend(
16871 CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor),
16872 CGDOMJSProxyHandler_getSameOriginPrototype(descriptor),
16873 CGDOMJSProxyHandler_definePropertySameOrigin(descriptor),
16874 CGDOMJSProxyHandler_set(descriptor),
16875 CGDOMJSProxyHandler_EnsureHolder(descriptor),
16876 ClassUsingFromBaseDeclaration(
16877 "MaybeCrossOriginObjectMixins", "EnsureHolder"
16882 CGClass.__init__(
16883 self,
16884 "DOMProxyHandler",
16885 bases=[ClassBase(parentClass)],
16886 typeAliases=typeAliases,
16887 constructors=constructors,
16888 methods=methods,
16892 class CGDOMJSProxyHandlerDeclarer(CGThing):
16894 A class for declaring a DOMProxyHandler.
16897 def __init__(self, handlerThing):
16898 self.handlerThing = handlerThing
16900 def declare(self):
16901 # Our class declaration should happen when we're defining
16902 return ""
16904 def define(self):
16905 return self.handlerThing.declare()
16908 class CGDOMJSProxyHandlerDefiner(CGThing):
16910 A class for defining a DOMProxyHandler.
16913 def __init__(self, handlerThing):
16914 self.handlerThing = handlerThing
16916 def declare(self):
16917 return ""
16919 def define(self):
16920 return self.handlerThing.define()
16923 def stripTrailingWhitespace(text):
16924 tail = "\n" if text.endswith("\n") else ""
16925 lines = text.splitlines()
16926 return "\n".join(line.rstrip() for line in lines) + tail
16929 class MemberProperties:
16930 def __init__(self):
16931 self.isCrossOriginMethod = False
16932 self.isCrossOriginGetter = False
16933 self.isCrossOriginSetter = False
16936 def memberProperties(m, descriptor):
16937 props = MemberProperties()
16938 if m.isMethod():
16939 if not m.isIdentifierLess() or m == descriptor.operations["Stringifier"]:
16940 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
16941 if m.getExtendedAttribute("CrossOriginCallable"):
16942 props.isCrossOriginMethod = True
16943 elif m.isAttr():
16944 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
16945 if m.getExtendedAttribute("CrossOriginReadable"):
16946 props.isCrossOriginGetter = True
16947 if not m.readonly:
16948 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
16949 if m.getExtendedAttribute("CrossOriginWritable"):
16950 props.isCrossOriginSetter = True
16951 elif m.getExtendedAttribute("PutForwards"):
16952 if m.getExtendedAttribute("CrossOriginWritable"):
16953 props.isCrossOriginSetter = True
16954 elif m.getExtendedAttribute("Replaceable") or m.getExtendedAttribute(
16955 "LegacyLenientSetter"
16957 if m.getExtendedAttribute("CrossOriginWritable"):
16958 props.isCrossOriginSetter = True
16960 return props
16963 class CGDescriptor(CGThing):
16964 def __init__(self, descriptor, attributeTemplates):
16965 CGThing.__init__(self)
16967 assert (
16968 not descriptor.concrete
16969 or descriptor.interface.hasInterfacePrototypeObject()
16970 or descriptor.hasOrdinaryObjectPrototype()
16973 self._deps = descriptor.interface.getDeps()
16975 iteratorCGThings = None
16976 if (
16977 descriptor.interface.isIterable()
16978 and descriptor.interface.maplikeOrSetlikeOrIterable.isPairIterator()
16979 ) or descriptor.interface.isAsyncIterable():
16980 # We need the Wrap function when using the [Async]IterableIterator type, so we want to declare it before we need it. We don't really want to expose it in the header file, so we make it static too.
16981 iteratorCGThings = []
16982 itr_iface = (
16983 descriptor.interface.maplikeOrSetlikeOrIterable.iteratorType.inner
16985 iteratorDescriptor = descriptor.getDescriptor(itr_iface.identifier.name)
16986 iteratorCGThings.append(
16987 CGWrapNonWrapperCacheMethod(
16988 iteratorDescriptor, static=True, signatureOnly=True
16991 iteratorCGThings = CGList(
16992 (CGIndenter(t, declareOnly=True) for t in iteratorCGThings), "\n"
16994 iteratorCGThings = CGWrapper(iteratorCGThings, pre="\n", post="\n")
16995 iteratorCGThings = CGWrapper(
16996 CGNamespace(
16997 toBindingNamespace(iteratorDescriptor.name), iteratorCGThings
16999 post="\n",
17002 cgThings = []
17004 isIteratorInterface = (
17005 descriptor.interface.isIteratorInterface()
17006 or descriptor.interface.isAsyncIteratorInterface()
17008 if not isIteratorInterface:
17009 cgThings.append(
17010 CGGeneric(declare="typedef %s NativeType;\n" % descriptor.nativeType)
17012 parent = descriptor.interface.parent
17013 if parent:
17014 cgThings.append(
17015 CGGeneric(
17016 "static_assert(IsRefcounted<NativeType>::value == IsRefcounted<%s::NativeType>::value,\n"
17017 ' "Can\'t inherit from an interface with a different ownership model.");\n'
17018 % toBindingNamespace(descriptor.parentPrototypeName)
17022 defaultToJSONMethod = None
17023 needCrossOriginPropertyArrays = False
17024 unscopableNames = list()
17026 for n in descriptor.interface.legacyFactoryFunctions:
17027 cgThings.append(
17028 CGClassConstructor(descriptor, n, LegacyFactoryFunctionName(n))
17031 if descriptor.attributeTemplates is not None:
17032 for template in descriptor.attributeTemplates:
17033 if template.getter is not None:
17034 cgThings.append(
17035 CGTemplateForSpecializedGetter(descriptor, template)
17037 if template.setter is not None:
17038 cgThings.append(
17039 CGTemplateForSpecializedSetter(descriptor, template)
17042 if descriptor.interface.reflectedHTMLAttributesReturningFrozenArray:
17043 cgThings.append(CGDefineHTMLAttributeSlots(descriptor))
17045 for m in descriptor.interface.members:
17046 if m.isMethod() and m.identifier.name == "QueryInterface":
17047 continue
17049 props = memberProperties(m, descriptor)
17051 if m.isMethod():
17052 if m.getExtendedAttribute("Unscopable"):
17053 assert not m.isStatic()
17054 unscopableNames.append(m.identifier.name)
17055 if m.isDefaultToJSON():
17056 defaultToJSONMethod = m
17057 elif (
17058 not m.isIdentifierLess()
17059 or m == descriptor.operations["Stringifier"]
17061 if m.isStatic():
17062 assert descriptor.interface.hasInterfaceObject()
17063 cgThings.append(CGStaticMethod(descriptor, m))
17064 if m.returnsPromise():
17065 cgThings.append(CGStaticMethodJitinfo(m))
17066 elif descriptor.interface.hasInterfacePrototypeObject():
17067 specializedMethod = CGSpecializedMethod(descriptor, m)
17068 cgThings.append(specializedMethod)
17069 if m.returnsPromise():
17070 cgThings.append(
17071 CGMethodPromiseWrapper(descriptor, specializedMethod)
17073 cgThings.append(CGMemberJITInfo(descriptor, m))
17074 if props.isCrossOriginMethod:
17075 needCrossOriginPropertyArrays = True
17076 # If we've hit the maplike/setlike member itself, go ahead and
17077 # generate its convenience functions.
17078 elif m.isMaplikeOrSetlike():
17079 cgThings.append(CGMaplikeOrSetlikeHelperGenerator(descriptor, m))
17080 elif m.isAttr():
17081 if m.type.isObservableArray():
17082 cgThings.append(
17083 CGObservableArrayProxyHandlerGenerator(descriptor, m)
17085 cgThings.append(CGObservableArrayHelperGenerator(descriptor, m))
17086 if m.getExtendedAttribute("Unscopable"):
17087 assert not m.isStatic()
17088 unscopableNames.append(m.identifier.name)
17089 if m.isStatic():
17090 assert descriptor.interface.hasInterfaceObject()
17091 cgThings.append(CGStaticGetter(descriptor, m))
17092 elif descriptor.interface.hasInterfacePrototypeObject():
17093 template = m.getExtendedAttribute("BindingTemplate")
17094 if template is not None:
17095 templateName = template[0][0]
17096 additionalArg = template[0][1]
17097 if not (m.type.isPrimitive() or m.type.isString()):
17098 raise TypeError(
17099 "We only support primitives or strings on templated attributes. "
17100 "Attribute '%s' on interface '%s' has type '%s' but tries to "
17101 "use template '%s'"
17103 m.identifier.name,
17104 descriptor.interface.identifier.name,
17105 m.type,
17106 templateName,
17109 template = attributeTemplates.get(templateName)
17110 specializedGetter = CGSpecializedTemplatedGetter(
17111 descriptor, m, template, additionalArg
17113 else:
17114 specializedGetter = CGSpecializedGetter(descriptor, m)
17115 cgThings.append(specializedGetter)
17116 if m.type.isPromise():
17117 cgThings.append(
17118 CGGetterPromiseWrapper(descriptor, specializedGetter)
17120 if props.isCrossOriginGetter:
17121 needCrossOriginPropertyArrays = True
17122 if not m.readonly:
17123 if m.isStatic():
17124 assert descriptor.interface.hasInterfaceObject()
17125 cgThings.append(CGStaticSetter(descriptor, m))
17126 elif descriptor.interface.hasInterfacePrototypeObject():
17127 template = m.getExtendedAttribute("BindingTemplate")
17128 if template is not None:
17129 if isinstance(template[0], list):
17130 templateName = template[0][0]
17131 additionalArg = template[0][1]
17132 else:
17133 templateName = template[0]
17134 additionalArg = None
17135 template = attributeTemplates.get(templateName)
17136 specializedSetter = CGSpecializedTemplatedSetter(
17137 descriptor, m, template, additionalArg
17139 else:
17140 specializedSetter = CGSpecializedSetter(descriptor, m)
17141 cgThings.append(specializedSetter)
17142 if props.isCrossOriginSetter:
17143 needCrossOriginPropertyArrays = True
17144 elif m.getExtendedAttribute("PutForwards"):
17145 cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
17146 if props.isCrossOriginSetter:
17147 needCrossOriginPropertyArrays = True
17148 elif m.getExtendedAttribute("Replaceable"):
17149 cgThings.append(CGSpecializedReplaceableSetter(descriptor, m))
17150 elif m.getExtendedAttribute("LegacyLenientSetter"):
17151 # XXX In this case, we need to add an include for mozilla/dom/Document.h to the generated cpp file.
17152 cgThings.append(CGSpecializedLenientSetter(descriptor, m))
17153 if (
17154 not m.isStatic()
17155 and descriptor.interface.hasInterfacePrototypeObject()
17157 cgThings.append(CGMemberJITInfo(descriptor, m))
17158 if m.isConst() and m.type.isPrimitive():
17159 cgThings.append(CGConstDefinition(m))
17161 if defaultToJSONMethod:
17162 cgThings.append(CGDefaultToJSONMethod(descriptor, defaultToJSONMethod))
17163 cgThings.append(CGMemberJITInfo(descriptor, defaultToJSONMethod))
17165 if descriptor.concrete and not descriptor.proxy:
17166 if wantsAddProperty(descriptor):
17167 cgThings.append(CGAddPropertyHook(descriptor))
17169 # Always have a finalize hook, regardless of whether the class
17170 # wants a custom hook.
17171 cgThings.append(CGClassFinalizeHook(descriptor))
17173 if wantsGetWrapperCache(descriptor):
17174 cgThings.append(CGGetWrapperCacheHook(descriptor))
17176 if descriptor.concrete and descriptor.wrapperCache and not descriptor.proxy:
17177 cgThings.append(CGClassObjectMovedHook(descriptor))
17179 properties = PropertyArrays(descriptor)
17180 cgThings.append(CGGeneric(define=str(properties)))
17181 cgThings.append(CGNativeProperties(descriptor, properties))
17183 if defaultToJSONMethod:
17184 # Now that we know about our property arrays, we can
17185 # output our "collect attribute values" method, which uses those.
17186 cgThings.append(
17187 CGCollectJSONAttributesMethod(descriptor, defaultToJSONMethod)
17190 # Declare our DOMProxyHandler.
17191 if descriptor.concrete and descriptor.proxy:
17192 cgThings.append(
17193 CGGeneric(
17194 fill(
17196 static_assert(std::is_base_of_v<nsISupports, ${nativeType}>,
17197 "We don't support non-nsISupports native classes for "
17198 "proxy-based bindings yet");
17200 """,
17201 nativeType=descriptor.nativeType,
17205 if not descriptor.wrapperCache:
17206 raise TypeError(
17207 "We need a wrappercache to support expandos for proxy-based "
17208 "bindings (" + descriptor.name + ")"
17210 handlerThing = CGDOMJSProxyHandler(descriptor)
17211 cgThings.append(CGDOMJSProxyHandlerDeclarer(handlerThing))
17212 cgThings.append(CGProxyIsProxy(descriptor))
17213 cgThings.append(CGProxyUnwrap(descriptor))
17215 # Set up our Xray callbacks as needed. This needs to come
17216 # after we have our DOMProxyHandler defined.
17217 if descriptor.wantsXrays:
17218 if descriptor.concrete and descriptor.proxy:
17219 if descriptor.needsXrayNamedDeleterHook():
17220 cgThings.append(CGDeleteNamedProperty(descriptor))
17221 elif descriptor.needsXrayResolveHooks():
17222 cgThings.append(CGResolveOwnPropertyViaResolve(descriptor))
17223 cgThings.append(
17224 CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor)
17226 if descriptor.wantsXrayExpandoClass:
17227 cgThings.append(CGXrayExpandoJSClass(descriptor))
17229 # Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff
17230 # done, set up our NativePropertyHooks.
17231 cgThings.append(CGNativePropertyHooks(descriptor, properties))
17233 if descriptor.interface.isNamespace():
17234 cgThings.append(CGNamespaceObjectJSClass(descriptor))
17235 elif descriptor.interface.hasInterfaceObject():
17236 cgThings.append(CGClassConstructor(descriptor, descriptor.interface.ctor()))
17237 cgThings.append(CGInterfaceObjectInfo(descriptor))
17238 cgThings.append(CGLegacyFactoryFunctions(descriptor))
17240 cgThings.append(CGLegacyCallHook(descriptor))
17241 if descriptor.interface.getExtendedAttribute("NeedResolve"):
17242 cgThings.append(CGResolveHook(descriptor))
17243 cgThings.append(CGMayResolveHook(descriptor))
17244 cgThings.append(CGEnumerateHook(descriptor))
17246 if descriptor.hasNamedPropertiesObject:
17247 cgThings.append(CGGetNamedPropertiesObjectMethod(descriptor))
17249 if descriptor.interface.hasInterfacePrototypeObject():
17250 cgThings.append(CGPrototypeJSClass(descriptor, properties))
17252 if (
17253 descriptor.interface.hasInterfaceObject()
17254 and not descriptor.interface.isExternal()
17255 and descriptor.isExposedConditionally()
17257 cgThings.append(CGConstructorEnabled(descriptor))
17259 if (
17260 descriptor.interface.hasMembersInSlots()
17261 and descriptor.interface.hasChildInterfaces()
17263 raise TypeError(
17264 "We don't support members in slots on "
17265 "non-leaf interfaces like %s" % descriptor.interface.identifier.name
17268 if descriptor.needsMissingPropUseCounters:
17269 cgThings.append(CGCountMaybeMissingProperty(descriptor))
17271 # CGDOMProxyJSClass/CGDOMJSClass need GetProtoObjectHandle, but we don't
17272 # want to export it for the iterator interfaces, or if we don't need it
17273 # for child interfaces or for the named properties object.
17274 protoObjectHandleGetterIsStatic = descriptor.concrete and (
17275 isIteratorInterface
17276 or (
17277 descriptor.interface.hasInterfacePrototypeObject()
17278 and not descriptor.interface.hasChildInterfaces()
17279 and not descriptor.hasNamedPropertiesObject
17282 if descriptor.concrete:
17283 if descriptor.interface.isSerializable():
17284 cgThings.append(CGSerializer(descriptor))
17285 cgThings.append(CGDeserializer(descriptor))
17287 if protoObjectHandleGetterIsStatic:
17288 cgThings.append(
17289 CGGetProtoObjectHandleMethod(
17290 descriptor, static=True, signatureOnly=True
17294 if descriptor.proxy:
17295 cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing))
17296 cgThings.append(CGDOMProxyJSClass(descriptor))
17297 else:
17298 cgThings.append(CGDOMJSClass(descriptor))
17300 if descriptor.interface.hasMembersInSlots():
17301 cgThings.append(CGUpdateMemberSlotsMethod(descriptor))
17303 if descriptor.isGlobal():
17304 assert descriptor.wrapperCache
17305 cgThings.append(CGWrapGlobalMethod(descriptor, properties))
17306 elif descriptor.wrapperCache:
17307 cgThings.append(CGWrapWithCacheMethod(descriptor))
17308 cgThings.append(CGWrapMethod(descriptor))
17309 else:
17310 cgThings.append(
17311 CGWrapNonWrapperCacheMethod(descriptor, static=isIteratorInterface)
17314 # If we're not wrappercached, we don't know how to clear our
17315 # cached values, since we can't get at the JSObject.
17316 if descriptor.wrapperCache:
17317 cgThings.extend(
17318 CGClearCachedValueMethod(descriptor, m)
17319 for m in clearableCachedAttrs(descriptor)
17322 haveUnscopables = (
17323 len(unscopableNames) != 0
17324 and descriptor.interface.hasInterfacePrototypeObject()
17326 if haveUnscopables:
17327 cgThings.append(
17328 CGList(
17330 CGGeneric("static const char* const unscopableNames[] = {"),
17331 CGIndenter(
17332 CGList(
17333 [CGGeneric('"%s"' % name) for name in unscopableNames]
17334 + [CGGeneric("nullptr")],
17335 ",\n",
17338 CGGeneric("};\n"),
17340 "\n",
17344 legacyWindowAliases = descriptor.interface.legacyWindowAliases
17345 haveLegacyWindowAliases = len(legacyWindowAliases) != 0
17346 if haveLegacyWindowAliases:
17347 cgThings.append(
17348 CGList(
17350 CGGeneric("static const char* const legacyWindowAliases[] = {"),
17351 CGIndenter(
17352 CGList(
17354 CGGeneric('"%s"' % name)
17355 for name in legacyWindowAliases
17357 + [CGGeneric("nullptr")],
17358 ",\n",
17361 CGGeneric("};\n"),
17363 "\n",
17367 if not descriptor.hasOrdinaryObjectPrototype():
17368 # CGCreateInterfaceObjectsMethod needs to come after our
17369 # CGDOMJSClass and unscopables, if any.
17370 cgThings.append(
17371 CGCreateInterfaceObjectsMethod(
17372 descriptor,
17373 properties,
17374 haveUnscopables,
17375 haveLegacyWindowAliases,
17376 static=isIteratorInterface,
17380 # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need
17381 # to come after CGCreateInterfaceObjectsMethod.
17382 if descriptor.interface.hasInterfacePrototypeObject():
17383 cgThings.append(
17384 CGGetProtoObjectHandleMethod(
17385 descriptor, static=protoObjectHandleGetterIsStatic
17388 if descriptor.interface.hasChildInterfaces():
17389 assert not isIteratorInterface
17390 cgThings.append(CGGetProtoObjectMethod(descriptor))
17391 if descriptor.interface.hasInterfaceObject():
17392 cgThings.append(CGGetConstructorObjectHandleMethod(descriptor))
17393 cgThings.append(
17394 CGCreateAndDefineOnGlobalMethod(
17395 descriptor,
17399 # See whether we need to generate cross-origin property arrays.
17400 if needCrossOriginPropertyArrays:
17401 cgThings.append(CGCrossOriginProperties(descriptor))
17403 cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
17404 cgThings = CGWrapper(cgThings, pre="\n", post="\n")
17405 cgThings = CGWrapper(
17406 CGNamespace(toBindingNamespace(descriptor.name), cgThings), post="\n"
17408 self.cgRoot = CGList([iteratorCGThings, cgThings], "\n")
17410 def declare(self):
17411 return self.cgRoot.declare()
17413 def define(self):
17414 return self.cgRoot.define()
17416 def deps(self):
17417 return self._deps
17420 class CGNamespacedEnum(CGThing):
17421 def __init__(self, namespace, enumName, names, values, comment=""):
17422 if not values:
17423 values = []
17425 # Account for explicit enum values.
17426 entries = []
17427 for i in range(0, len(names)):
17428 if len(values) > i and values[i] is not None:
17429 entry = "%s = %s" % (names[i], values[i])
17430 else:
17431 entry = names[i]
17432 entries.append(entry)
17434 # Append a Count.
17435 entries.append("_" + enumName + "_Count")
17437 # Indent.
17438 entries = [" " + e for e in entries]
17440 # Build the enum body.
17441 enumstr = comment + "enum %s : uint16_t\n{\n%s\n};\n" % (
17442 enumName,
17443 ",\n".join(entries),
17445 curr = CGGeneric(declare=enumstr)
17447 # Add some whitespace padding.
17448 curr = CGWrapper(curr, pre="\n", post="\n")
17450 # Add the namespace.
17451 curr = CGNamespace(namespace, curr)
17453 # Add the typedef
17454 typedef = "\ntypedef %s::%s %s;\n\n" % (namespace, enumName, enumName)
17455 curr = CGList([curr, CGGeneric(declare=typedef)])
17457 # Save the result.
17458 self.node = curr
17460 def declare(self):
17461 return self.node.declare()
17463 def define(self):
17464 return ""
17467 def initIdsClassMethod(identifiers, atomCacheName):
17468 idinit = [
17469 '!atomsCache->%s.init(cx, "%s")' % (CGDictionary.makeIdName(id), id)
17470 for id in identifiers
17472 idinit.reverse()
17473 body = fill(
17475 MOZ_ASSERT(reinterpret_cast<jsid*>(atomsCache)->isVoid());
17477 // Initialize these in reverse order so that any failure leaves the first one
17478 // uninitialized.
17479 if (${idinit}) {
17480 return false;
17482 return true;
17483 """,
17484 idinit=" ||\n ".join(idinit),
17486 return ClassMethod(
17487 "InitIds",
17488 "bool",
17489 [Argument("JSContext*", "cx"), Argument("%s*" % atomCacheName, "atomsCache")],
17490 static=True,
17491 body=body,
17492 visibility="private",
17496 class CGDictionary(CGThing):
17497 def __init__(self, dictionary, descriptorProvider):
17498 self.dictionary = dictionary
17499 self.descriptorProvider = descriptorProvider
17500 self.needToInitIds = len(dictionary.members) > 0
17501 self.memberInfo = [
17503 member,
17504 getJSToNativeConversionInfo(
17505 member.type,
17506 descriptorProvider,
17507 isMember="Dictionary",
17508 isOptional=member.canHaveMissingValue(),
17509 isKnownMissing=not dictionary.needsConversionFromJS,
17510 defaultValue=member.defaultValue,
17511 sourceDescription=self.getMemberSourceDescription(member),
17514 for member in dictionary.members
17517 # If we have a union member which is going to be declared in a different
17518 # header but contains something that will be declared in the same header
17519 # as us, bail: the C++ includes won't work out.
17520 for member in dictionary.members:
17521 type = member.type.unroll()
17522 if type.isUnion() and CGHeaders.getUnionDeclarationFilename(
17523 descriptorProvider.getConfig(), type
17524 ) != CGHeaders.getDeclarationFilename(dictionary):
17525 for t in type.flatMemberTypes:
17526 if t.isDictionary() and CGHeaders.getDeclarationFilename(
17527 t.inner
17528 ) == CGHeaders.getDeclarationFilename(dictionary):
17529 raise TypeError(
17530 "Dictionary contains a union that will live in a different "
17531 "header that contains a dictionary from the same header as "
17532 "the original dictionary. This won't compile. Move the "
17533 "inner dictionary to a different Web IDL file to move it "
17534 "to a different header.\n%s\n%s"
17535 % (t.location, t.inner.location)
17537 self.structs = self.getStructs()
17539 def declare(self):
17540 return self.structs.declare()
17542 def define(self):
17543 return self.structs.define()
17545 def base(self):
17546 if self.dictionary.parent:
17547 return self.makeClassName(self.dictionary.parent)
17548 return "DictionaryBase"
17550 def initMethod(self):
17552 This function outputs the body of the Init() method for the dictionary.
17554 For the most part, this is some bookkeeping for our atoms so
17555 we can avoid atomizing strings all the time, then we just spit
17556 out the getMemberConversion() output for each member,
17557 separated by newlines.
17560 body = dedent(
17562 // Passing a null JSContext is OK only if we're initing from null,
17563 // Since in that case we will not have to do any property gets
17564 // Also evaluate isNullOrUndefined in order to avoid false-positive
17565 // checkers by static analysis tools
17566 MOZ_ASSERT_IF(!cx, val.isNull() && val.isNullOrUndefined());
17570 if self.needToInitIds:
17571 body += fill(
17573 ${dictName}Atoms* atomsCache = nullptr;
17574 if (cx) {
17575 atomsCache = GetAtomCache<${dictName}Atoms>(cx);
17576 if (reinterpret_cast<jsid*>(atomsCache)->isVoid() &&
17577 !InitIds(cx, atomsCache)) {
17578 return false;
17582 """,
17583 dictName=self.makeClassName(self.dictionary),
17586 if self.dictionary.parent:
17587 body += fill(
17589 // Per spec, we init the parent's members first
17590 if (!${dictName}::Init(cx, val)) {
17591 return false;
17594 """,
17595 dictName=self.makeClassName(self.dictionary.parent),
17597 else:
17598 body += dedent(
17600 if (!IsConvertibleToDictionary(val)) {
17601 return cx.ThrowErrorMessage<MSG_CONVERSION_ERROR>(sourceDescription, "dictionary");
17607 memberInits = [self.getMemberConversion(m).define() for m in self.memberInfo]
17608 if memberInits:
17609 body += fill(
17611 bool isNull = val.isNullOrUndefined();
17612 // We only need these if !isNull, in which case we have |cx|.
17613 Maybe<JS::Rooted<JSObject *> > object;
17614 Maybe<JS::Rooted<JS::Value> > temp;
17615 if (!isNull) {
17616 MOZ_ASSERT(cx);
17617 object.emplace(cx, &val.toObject());
17618 temp.emplace(cx);
17620 $*{memberInits}
17621 """,
17622 memberInits="\n".join(memberInits),
17625 body += "return true;\n"
17627 return ClassMethod(
17628 "Init",
17629 "bool",
17631 Argument("BindingCallContext&", "cx"),
17632 Argument("JS::Handle<JS::Value>", "val"),
17633 Argument("const char*", "sourceDescription", default='"Value"'),
17634 Argument("bool", "passedToJSImpl", default="false"),
17636 body=body,
17639 def initWithoutCallContextMethod(self):
17641 This function outputs the body of an Init() method for the dictionary
17642 that takes just a JSContext*. This is needed for non-binding consumers.
17644 body = dedent(
17646 // We don't want to use sourceDescription for our context here;
17647 // that's not really what it's formatted for.
17648 BindingCallContext cx(cx_, nullptr);
17649 return Init(cx, val, sourceDescription, passedToJSImpl);
17652 return ClassMethod(
17653 "Init",
17654 "bool",
17656 Argument("JSContext*", "cx_"),
17657 Argument("JS::Handle<JS::Value>", "val"),
17658 Argument("const char*", "sourceDescription", default='"Value"'),
17659 Argument("bool", "passedToJSImpl", default="false"),
17661 body=body,
17664 def simpleInitMethod(self):
17666 This function outputs the body of the Init() method for the dictionary,
17667 for cases when we are just default-initializing it.
17670 relevantMembers = [
17672 for m in self.memberInfo
17673 # We only need to init the things that can have
17674 # default values.
17675 if m[0].optional and m[0].defaultValue
17678 # We mostly avoid outputting code that uses cx in our native-to-JS
17679 # conversions, but there is one exception: we may have a
17680 # dictionary-typed member that _does_ generally support conversion from
17681 # JS. If we have such a thing, we can pass it a null JSContext and
17682 # JS::NullHandleValue to default-initialize it, but since the
17683 # native-to-JS templates hardcode `cx` as the JSContext value, we're
17684 # going to need to provide that.
17685 haveMemberThatNeedsCx = any(
17686 m[0].type.isDictionary() and m[0].type.unroll().inner.needsConversionFromJS
17687 for m in relevantMembers
17689 if haveMemberThatNeedsCx:
17690 body = dedent(
17692 JSContext* cx = nullptr;
17695 else:
17696 body = ""
17698 if self.dictionary.parent:
17699 if self.dictionary.parent.needsConversionFromJS:
17700 args = "nullptr, JS::NullHandleValue"
17701 else:
17702 args = ""
17703 body += fill(
17705 // We init the parent's members first
17706 if (!${dictName}::Init(${args})) {
17707 return false;
17710 """,
17711 dictName=self.makeClassName(self.dictionary.parent),
17712 args=args,
17715 memberInits = [
17716 self.getMemberConversion(m, isKnownMissing=True).define()
17717 for m in relevantMembers
17719 if memberInits:
17720 body += fill(
17722 $*{memberInits}
17723 """,
17724 memberInits="\n".join(memberInits),
17727 body += "return true;\n"
17729 return ClassMethod(
17730 "Init",
17731 "bool",
17733 Argument("const char*", "sourceDescription", default='"Value"'),
17734 Argument("bool", "passedToJSImpl", default="false"),
17736 body=body,
17739 def initFromJSONMethod(self):
17740 return ClassMethod(
17741 "Init",
17742 "bool",
17743 [Argument("const nsAString&", "aJSON")],
17744 body=dedent(
17746 AutoJSAPI jsapi;
17747 JSObject* cleanGlobal = SimpleGlobalObject::Create(SimpleGlobalObject::GlobalType::BindingDetail);
17748 if (!cleanGlobal) {
17749 return false;
17751 if (!jsapi.Init(cleanGlobal)) {
17752 return false;
17754 JSContext* cx = jsapi.cx();
17755 JS::Rooted<JS::Value> json(cx);
17756 bool ok = ParseJSON(cx, aJSON, &json);
17757 NS_ENSURE_TRUE(ok, false);
17758 return Init(cx, json);
17763 def toJSONMethod(self):
17764 return ClassMethod(
17765 "ToJSON",
17766 "bool",
17767 [Argument("nsAString&", "aJSON")],
17768 body=dedent(
17770 AutoJSAPI jsapi;
17771 jsapi.Init();
17772 JSContext *cx = jsapi.cx();
17773 // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here
17774 // because we'll only be creating objects, in ways that have no
17775 // side-effects, followed by a call to JS::ToJSONMaybeSafely,
17776 // which likewise guarantees no side-effects for the sorts of
17777 // things we will pass it.
17778 JSObject* scope = UnprivilegedJunkScopeOrWorkerGlobal(fallible);
17779 if (!scope) {
17780 JS_ReportOutOfMemory(cx);
17781 return false;
17783 JSAutoRealm ar(cx, scope);
17784 JS::Rooted<JS::Value> val(cx);
17785 if (!ToObjectInternal(cx, &val)) {
17786 return false;
17788 JS::Rooted<JSObject*> obj(cx, &val.toObject());
17789 return StringifyToJSON(cx, obj, aJSON);
17792 const=True,
17795 def toObjectInternalMethod(self):
17796 body = ""
17797 if self.needToInitIds:
17798 body += fill(
17800 ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx);
17801 if (reinterpret_cast<jsid*>(atomsCache)->isVoid() &&
17802 !InitIds(cx, atomsCache)) {
17803 return false;
17806 """,
17807 dictName=self.makeClassName(self.dictionary),
17810 if self.dictionary.parent:
17811 body += fill(
17813 // Per spec, we define the parent's members first
17814 if (!${dictName}::ToObjectInternal(cx, rval)) {
17815 return false;
17817 JS::Rooted<JSObject*> obj(cx, &rval.toObject());
17819 """,
17820 dictName=self.makeClassName(self.dictionary.parent),
17822 else:
17823 body += dedent(
17825 JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
17826 if (!obj) {
17827 return false;
17829 rval.set(JS::ObjectValue(*obj));
17834 if self.memberInfo:
17835 body += "\n".join(
17836 self.getMemberDefinition(m).define() for m in self.memberInfo
17838 body += "\nreturn true;\n"
17840 return ClassMethod(
17841 "ToObjectInternal",
17842 "bool",
17844 Argument("JSContext*", "cx"),
17845 Argument("JS::MutableHandle<JS::Value>", "rval"),
17847 const=True,
17848 body=body,
17851 def initIdsMethod(self):
17852 assert self.needToInitIds
17853 return initIdsClassMethod(
17854 [m.identifier.name for m in self.dictionary.members],
17855 "%sAtoms" % self.makeClassName(self.dictionary),
17858 def traceDictionaryMethod(self):
17859 body = ""
17860 if self.dictionary.parent:
17861 cls = self.makeClassName(self.dictionary.parent)
17862 body += "%s::TraceDictionary(trc);\n" % cls
17864 memberTraces = [
17865 self.getMemberTrace(m)
17866 for m in self.dictionary.members
17867 if typeNeedsRooting(m.type)
17870 if memberTraces:
17871 body += "\n".join(memberTraces)
17873 return ClassMethod(
17874 "TraceDictionary",
17875 "void",
17877 Argument("JSTracer*", "trc"),
17879 body=body,
17882 @staticmethod
17883 def dictionaryNeedsCycleCollection(dictionary):
17884 return any(idlTypeNeedsCycleCollection(m.type) for m in dictionary.members) or (
17885 dictionary.parent
17886 and CGDictionary.dictionaryNeedsCycleCollection(dictionary.parent)
17889 def traverseForCCMethod(self):
17890 body = ""
17891 if self.dictionary.parent and self.dictionaryNeedsCycleCollection(
17892 self.dictionary.parent
17894 cls = self.makeClassName(self.dictionary.parent)
17895 body += "%s::TraverseForCC(aCallback, aFlags);\n" % cls
17897 for m, _ in self.memberInfo:
17898 if idlTypeNeedsCycleCollection(m.type):
17899 memberName = self.makeMemberName(m.identifier.name)
17900 body += (
17901 'ImplCycleCollectionTraverse(aCallback, %s, "%s", aFlags);\n'
17902 % (memberName, memberName)
17905 return ClassMethod(
17906 "TraverseForCC",
17907 "void",
17909 Argument("nsCycleCollectionTraversalCallback&", "aCallback"),
17910 Argument("uint32_t", "aFlags"),
17912 body=body,
17913 # Inline so we don't pay a codesize hit unless someone actually uses
17914 # this traverse method.
17915 inline=True,
17916 bodyInHeader=True,
17919 def unlinkForCCMethod(self):
17920 body = ""
17921 if self.dictionary.parent and self.dictionaryNeedsCycleCollection(
17922 self.dictionary.parent
17924 cls = self.makeClassName(self.dictionary.parent)
17925 body += "%s::UnlinkForCC();\n" % cls
17927 for m, _ in self.memberInfo:
17928 if idlTypeNeedsCycleCollection(m.type):
17929 memberName = self.makeMemberName(m.identifier.name)
17930 body += "ImplCycleCollectionUnlink(%s);\n" % memberName
17932 return ClassMethod(
17933 "UnlinkForCC",
17934 "void",
17936 body=body,
17937 # Inline so we don't pay a codesize hit unless someone actually uses
17938 # this unlink method.
17939 inline=True,
17940 bodyInHeader=True,
17943 def assignmentOperator(self):
17944 body = CGList([])
17945 body.append(CGGeneric("%s::operator=(aOther);\n" % self.base()))
17947 for m, _ in self.memberInfo:
17948 memberName = self.makeMemberName(m.identifier.name)
17949 if m.canHaveMissingValue():
17950 memberAssign = CGGeneric(
17951 fill(
17953 ${name}.Reset();
17954 if (aOther.${name}.WasPassed()) {
17955 ${name}.Construct(aOther.${name}.Value());
17957 """,
17958 name=memberName,
17961 else:
17962 memberAssign = CGGeneric("%s = aOther.%s;\n" % (memberName, memberName))
17963 body.append(memberAssign)
17964 body.append(CGGeneric("return *this;\n"))
17965 return ClassMethod(
17966 "operator=",
17967 "%s&" % self.makeClassName(self.dictionary),
17968 [Argument("const %s&" % self.makeClassName(self.dictionary), "aOther")],
17969 body=body.define(),
17972 def equalityOperator(self):
17973 # For now we only allow equality operators if our members have a string
17974 # type, a primitive type or an enum type.
17975 if not all(
17976 m.type.isString() or m.type.isPrimitive() or m.type.isEnum()
17977 for m in self.dictionary.members
17979 err = (
17980 "[GenerateEqualityOperator] set on %s, but it"
17981 % self.dictionary.needsEqualityOperator.identifier.name
17983 if self.dictionary.needsEqualityOperator != self.dictionary:
17984 err += "s ancestor %s" % self.dictionary.identifier.name
17985 err += " contains types other than string, primitive or enum types."
17986 raise TypeError(err)
17988 body = CGList([])
17990 if self.dictionary.parent:
17991 # If we have a parent dictionary we have to call its equals
17992 # operator.
17993 parentTest = CGGeneric(
17994 fill(
17996 if (!${base}::operator==(aOther)) {
17997 return false;
17999 """,
18000 base=self.makeClassName(self.dictionary.parent),
18003 body.append(parentTest)
18005 for m, _ in self.memberInfo:
18006 memberName = self.makeMemberName(m.identifier.name)
18007 memberTest = CGGeneric(
18008 fill(
18010 if (${memberName} != aOther.${memberName}) {
18011 return false;
18013 """,
18014 memberName=memberName,
18017 body.append(memberTest)
18018 body.append(CGGeneric("return true;\n"))
18019 return ClassMethod(
18020 "operator==",
18021 "bool",
18022 [Argument("const %s&" % self.makeClassName(self.dictionary), "aOther")],
18023 const=True,
18024 body=body.define(),
18027 def getStructs(self):
18028 d = self.dictionary
18029 selfName = self.makeClassName(d)
18030 members = [
18031 ClassMember(
18032 self.makeMemberName(m[0].identifier.name),
18033 self.getMemberType(m),
18034 visibility="public",
18035 body=self.getMemberInitializer(m),
18036 hasIgnoreInitCheckFlag=True,
18038 for m in self.memberInfo
18040 if d.parent:
18041 # We always want to init our parent with our non-initializing
18042 # constructor arg, because either we're about to init ourselves (and
18043 # hence our parent) or we don't want any init happening.
18044 baseConstructors = [
18045 "%s(%s)"
18046 % (self.makeClassName(d.parent), self.getNonInitializingCtorArg())
18048 else:
18049 baseConstructors = None
18051 if d.needsConversionFromJS:
18052 initArgs = "nullptr, JS::NullHandleValue"
18053 else:
18054 initArgs = ""
18055 ctors = [
18056 ClassConstructor(
18058 visibility="public",
18059 baseConstructors=baseConstructors,
18060 body=(
18061 "// Safe to pass a null context if we pass a null value\n"
18062 "Init(%s);\n" % initArgs
18065 ClassConstructor(
18066 [Argument("const FastDictionaryInitializer&", "")],
18067 visibility="public",
18068 baseConstructors=baseConstructors,
18069 explicit=True,
18070 bodyInHeader=True,
18071 body='// Do nothing here; this is used by our "Fast" subclass\n',
18074 methods = []
18076 if self.needToInitIds:
18077 methods.append(self.initIdsMethod())
18079 if d.needsConversionFromJS:
18080 methods.append(self.initMethod())
18081 methods.append(self.initWithoutCallContextMethod())
18082 else:
18083 methods.append(self.simpleInitMethod())
18085 canBeRepresentedAsJSON = self.dictionarySafeToJSONify(d)
18086 if canBeRepresentedAsJSON and d.getExtendedAttribute("GenerateInitFromJSON"):
18087 methods.append(self.initFromJSONMethod())
18089 if d.needsConversionToJS:
18090 methods.append(self.toObjectInternalMethod())
18092 if canBeRepresentedAsJSON and d.getExtendedAttribute("GenerateToJSON"):
18093 methods.append(self.toJSONMethod())
18095 methods.append(self.traceDictionaryMethod())
18097 try:
18098 if self.dictionaryNeedsCycleCollection(d):
18099 methods.append(self.traverseForCCMethod())
18100 methods.append(self.unlinkForCCMethod())
18101 except CycleCollectionUnsupported:
18102 # We have some member that we don't know how to CC. Don't output
18103 # our cycle collection overloads, so attempts to CC us will fail to
18104 # compile instead of misbehaving.
18105 pass
18107 ctors.append(
18108 ClassConstructor(
18109 [Argument("%s&&" % selfName, "aOther")],
18110 default=True,
18111 visibility="public",
18112 baseConstructors=baseConstructors,
18116 if CGDictionary.isDictionaryCopyConstructible(d):
18117 disallowCopyConstruction = False
18118 # Note: gcc's -Wextra has a warning against not initializng our
18119 # base explicitly. If we have one. Use our non-initializing base
18120 # constructor to get around that.
18121 ctors.append(
18122 ClassConstructor(
18123 [Argument("const %s&" % selfName, "aOther")],
18124 bodyInHeader=True,
18125 visibility="public",
18126 baseConstructors=baseConstructors,
18127 explicit=True,
18128 body="*this = aOther;\n",
18131 methods.append(self.assignmentOperator())
18132 else:
18133 disallowCopyConstruction = True
18135 if d.needsEqualityOperator:
18136 methods.append(self.equalityOperator())
18137 elif d.parent and d.parent.needsEqualityOperator:
18138 methods.append(
18139 ClassMethod(
18140 "operator==",
18141 "bool",
18143 Argument(
18144 "const %s&" % self.makeClassName(self.dictionary), "aOther"
18147 visibility="public",
18148 delete=True,
18152 struct = CGClass(
18153 selfName,
18154 bases=[ClassBase(self.base())],
18155 members=members,
18156 constructors=ctors,
18157 methods=methods,
18158 isStruct=True,
18159 disallowCopyConstruction=disallowCopyConstruction,
18162 fastDictionaryCtor = ClassConstructor(
18164 visibility="public",
18165 bodyInHeader=True,
18166 baseConstructors=["%s(%s)" % (selfName, self.getNonInitializingCtorArg())],
18167 body="// Doesn't matter what int we pass to the parent constructor\n",
18170 fastStruct = CGClass(
18171 "Fast" + selfName,
18172 bases=[ClassBase(selfName)],
18173 constructors=[fastDictionaryCtor],
18174 isStruct=True,
18177 return CGList([struct, CGNamespace("binding_detail", fastStruct)], "\n")
18179 def deps(self):
18180 return self.dictionary.getDeps()
18182 @staticmethod
18183 def makeDictionaryName(dictionary):
18184 return dictionary.identifier.name
18186 def makeClassName(self, dictionary):
18187 return self.makeDictionaryName(dictionary)
18189 @staticmethod
18190 def makeMemberName(name):
18191 return "m" + name[0].upper() + IDLToCIdentifier(name[1:])
18193 def getMemberType(self, memberInfo):
18194 member, conversionInfo = memberInfo
18195 # We can't handle having a holderType here
18196 assert conversionInfo.holderType is None
18198 if member.getExtendedAttribute("BinaryType"):
18199 return member.getExtendedAttribute("BinaryType")[0]
18201 declType = conversionInfo.declType
18202 if conversionInfo.dealWithOptional:
18203 declType = CGTemplatedType("Optional", declType)
18204 return declType.define()
18206 def getMemberConversion(self, memberInfo, isKnownMissing=False):
18208 A function that outputs the initialization of a single dictionary
18209 member from the given dictionary value.
18211 We start with our conversionInfo, which tells us how to
18212 convert a JS::Value to whatever type this member is. We
18213 substiture the template from the conversionInfo with values
18214 that point to our "temp" JS::Value and our member (which is
18215 the C++ value we want to produce). The output is a string of
18216 code to do the conversion. We store this string in
18217 conversionReplacements["convert"].
18219 Now we have three different ways we might use (or skip) this
18220 string of code, depending on whether the value is required,
18221 optional with default value, or optional without default
18222 value. We set up a template in the 'conversion' variable for
18223 exactly how to do this, then substitute into it from the
18224 conversionReplacements dictionary.
18226 member, conversionInfo = memberInfo
18228 # We should only be initializing things with default values if
18229 # we're always-missing.
18230 assert not isKnownMissing or (member.optional and member.defaultValue)
18232 replacements = {
18233 "declName": self.makeMemberName(member.identifier.name),
18234 # We need a holder name for external interfaces, but
18235 # it's scoped down to the conversion so we can just use
18236 # anything we want.
18237 "holderName": "holder",
18238 "passedToJSImpl": "passedToJSImpl",
18241 if isKnownMissing:
18242 replacements["val"] = "(JS::NullHandleValue)"
18243 else:
18244 replacements["val"] = "temp.ref()"
18245 replacements["maybeMutableVal"] = "temp.ptr()"
18247 # We can't handle having a holderType here
18248 assert conversionInfo.holderType is None
18249 if conversionInfo.dealWithOptional:
18250 replacements["declName"] = "(" + replacements["declName"] + ".Value())"
18251 if member.defaultValue:
18252 if isKnownMissing:
18253 replacements["haveValue"] = "false"
18254 else:
18255 replacements["haveValue"] = "!isNull && !temp->isUndefined()"
18257 propId = self.makeIdName(member.identifier.name)
18258 propGet = "JS_GetPropertyById(cx, *object, atomsCache->%s, temp.ptr())" % propId
18260 conversionReplacements = {
18261 "prop": self.makeMemberName(member.identifier.name),
18262 "convert": string.Template(conversionInfo.template).substitute(
18263 replacements
18265 "propGet": propGet,
18267 # The conversion code will only run where a default value or a value passed
18268 # by the author needs to get converted, so we can remember if we have any
18269 # members present here.
18270 conversionReplacements["convert"] += "mIsAnyMemberPresent = true;\n"
18271 if isKnownMissing:
18272 conversion = ""
18273 else:
18274 setTempValue = CGGeneric(
18275 dedent(
18277 if (!${propGet}) {
18278 return false;
18283 conditions = getConditionList(member, "cx", "*object")
18284 if len(conditions) != 0:
18285 setTempValue = CGIfElseWrapper(
18286 conditions.define(),
18287 setTempValue,
18288 CGGeneric("temp->setUndefined();\n"),
18290 setTempValue = CGIfWrapper(setTempValue, "!isNull")
18291 conversion = setTempValue.define()
18293 if member.defaultValue:
18294 if member.type.isUnion() and (
18295 not member.type.nullable()
18296 or not isinstance(member.defaultValue, IDLNullValue)
18298 # Since this has a default value, it might have been initialized
18299 # already. Go ahead and uninit it before we try to init it
18300 # again.
18301 memberName = self.makeMemberName(member.identifier.name)
18302 if member.type.nullable():
18303 conversion += fill(
18305 if (!${memberName}.IsNull()) {
18306 ${memberName}.Value().Uninit();
18308 """,
18309 memberName=memberName,
18311 else:
18312 conversion += "%s.Uninit();\n" % memberName
18313 conversion += "${convert}"
18314 elif not conversionInfo.dealWithOptional:
18315 # We're required, but have no default value. Make sure
18316 # that we throw if we have no value provided.
18317 conversion += dedent(
18319 if (!isNull && !temp->isUndefined()) {
18320 ${convert}
18321 } else if (cx) {
18322 // Don't error out if we have no cx. In that
18323 // situation the caller is default-constructing us and we'll
18324 // just assume they know what they're doing.
18325 return cx.ThrowErrorMessage<MSG_MISSING_REQUIRED_DICTIONARY_MEMBER>("%s");
18328 % self.getMemberSourceDescription(member)
18330 conversionReplacements["convert"] = indent(
18331 conversionReplacements["convert"]
18332 ).rstrip()
18333 else:
18334 conversion += (
18335 "if (!isNull && !temp->isUndefined()) {\n"
18336 " ${prop}.Construct();\n"
18337 "${convert}"
18338 "}\n"
18340 conversionReplacements["convert"] = indent(
18341 conversionReplacements["convert"]
18344 return CGGeneric(string.Template(conversion).substitute(conversionReplacements))
18346 def getMemberDefinition(self, memberInfo):
18347 member = memberInfo[0]
18348 declType = memberInfo[1].declType
18349 memberLoc = self.makeMemberName(member.identifier.name)
18350 if not member.canHaveMissingValue():
18351 memberData = memberLoc
18352 else:
18353 # The data is inside the Optional<>
18354 memberData = "%s.InternalValue()" % memberLoc
18356 # If you have to change this list (which you shouldn't!), make sure it
18357 # continues to match the list in test_Object.prototype_props.html
18358 if member.identifier.name in [
18359 "constructor",
18360 "toString",
18361 "toLocaleString",
18362 "valueOf",
18363 "hasOwnProperty",
18364 "isPrototypeOf",
18365 "propertyIsEnumerable",
18366 "__defineGetter__",
18367 "__defineSetter__",
18368 "__lookupGetter__",
18369 "__lookupSetter__",
18370 "__proto__",
18372 raise TypeError(
18373 "'%s' member of %s dictionary shadows "
18374 "a property of Object.prototype, and Xrays to "
18375 "Object can't handle that.\n"
18376 "%s"
18378 member.identifier.name,
18379 self.dictionary.identifier.name,
18380 member.location,
18384 propDef = (
18385 "JS_DefinePropertyById(cx, obj, atomsCache->%s, temp, JSPROP_ENUMERATE)"
18386 % self.makeIdName(member.identifier.name)
18389 innerTemplate = wrapForType(
18390 member.type,
18391 self.descriptorProvider,
18393 "result": "currentValue",
18394 "successCode": (
18395 "if (!%s) {\n" " return false;\n" "}\n" "break;\n" % propDef
18397 "jsvalRef": "temp",
18398 "jsvalHandle": "&temp",
18399 "returnsNewObject": False,
18400 # 'obj' can just be allowed to be the string "obj", since that
18401 # will be our dictionary object, which is presumably itself in
18402 # the right scope.
18403 "spiderMonkeyInterfacesAreStructs": True,
18406 conversion = CGGeneric(innerTemplate)
18407 conversion = CGWrapper(
18408 conversion,
18409 pre=(
18410 "JS::Rooted<JS::Value> temp(cx);\n"
18411 "%s const & currentValue = %s;\n" % (declType.define(), memberData)
18415 # Now make sure that our successCode can actually break out of the
18416 # conversion. This incidentally gives us a scope for 'temp' and
18417 # 'currentValue'.
18418 conversion = CGWrapper(
18419 CGIndenter(conversion),
18420 pre=(
18421 "do {\n"
18422 " // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n"
18424 post="} while(false);\n",
18426 if member.canHaveMissingValue():
18427 # Only do the conversion if we have a value
18428 conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc)
18429 conditions = getConditionList(member, "cx", "obj")
18430 if len(conditions) != 0:
18431 conversion = CGIfWrapper(conversion, conditions.define())
18432 return conversion
18434 def getMemberTrace(self, member):
18435 type = member.type
18436 assert typeNeedsRooting(type)
18437 memberLoc = self.makeMemberName(member.identifier.name)
18438 if not member.canHaveMissingValue():
18439 memberData = memberLoc
18440 else:
18441 # The data is inside the Optional<>
18442 memberData = "%s.Value()" % memberLoc
18444 memberName = "%s.%s" % (self.makeClassName(self.dictionary), memberLoc)
18446 if type.isObject():
18447 trace = CGGeneric(
18448 'JS::TraceRoot(trc, %s, "%s");\n' % ("&" + memberData, memberName)
18450 if type.nullable():
18451 trace = CGIfWrapper(trace, memberData)
18452 elif type.isAny():
18453 trace = CGGeneric(
18454 'JS::TraceRoot(trc, %s, "%s");\n' % ("&" + memberData, memberName)
18456 elif (
18457 type.isSequence()
18458 or type.isDictionary()
18459 or type.isSpiderMonkeyInterface()
18460 or type.isUnion()
18461 or type.isRecord()
18463 if type.nullable():
18464 memberNullable = memberData
18465 memberData = "%s.Value()" % memberData
18466 if type.isSequence():
18467 trace = CGGeneric("DoTraceSequence(trc, %s);\n" % memberData)
18468 elif type.isDictionary():
18469 trace = CGGeneric("%s.TraceDictionary(trc);\n" % memberData)
18470 elif type.isUnion():
18471 trace = CGGeneric("%s.TraceUnion(trc);\n" % memberData)
18472 elif type.isRecord():
18473 trace = CGGeneric("TraceRecord(trc, %s);\n" % memberData)
18474 else:
18475 assert type.isSpiderMonkeyInterface()
18476 trace = CGGeneric("%s.TraceSelf(trc);\n" % memberData)
18477 if type.nullable():
18478 trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable)
18479 else:
18480 assert False # unknown type
18482 if member.canHaveMissingValue():
18483 trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc)
18485 return trace.define()
18487 def getMemberInitializer(self, memberInfo):
18489 Get the right initializer for the member. Most members don't need one,
18490 but we need to pre-initialize 'object' that have a default value or are
18491 required (and hence are not inside Optional), so they're safe to trace
18492 at all times. And we can optimize a bit for dictionary-typed members.
18494 member, _ = memberInfo
18495 if member.canHaveMissingValue():
18496 # Allowed missing value means no need to set it up front, since it's
18497 # inside an Optional and won't get traced until it's actually set
18498 # up.
18499 return None
18500 type = member.type
18501 if type.isDictionary():
18502 # When we construct ourselves, we don't want to init our member
18503 # dictionaries. Either we're being constructed-but-not-initialized
18504 # ourselves (and then we don't want to init them) or we're about to
18505 # init ourselves and then we'll init them anyway.
18506 return CGDictionary.getNonInitializingCtorArg()
18507 return initializerForType(type)
18509 def getMemberSourceDescription(self, member):
18510 return "'%s' member of %s" % (
18511 member.identifier.name,
18512 self.dictionary.identifier.name,
18515 @staticmethod
18516 def makeIdName(name):
18517 return IDLToCIdentifier(name) + "_id"
18519 @staticmethod
18520 def getNonInitializingCtorArg():
18521 return "FastDictionaryInitializer()"
18523 @staticmethod
18524 def isDictionaryCopyConstructible(dictionary):
18525 if dictionary.parent and not CGDictionary.isDictionaryCopyConstructible(
18526 dictionary.parent
18528 return False
18529 return all(isTypeCopyConstructible(m.type) for m in dictionary.members)
18531 @staticmethod
18532 def typeSafeToJSONify(type):
18534 Determine whether the given type is safe to convert to JSON. The
18535 restriction is that this needs to be safe while in a global controlled
18536 by an adversary, and "safe" means no side-effects when the JS
18537 representation of this type is converted to JSON. That means that we
18538 have to be pretty restrictive about what things we can allow. For
18539 example, "object" is out, because it may have accessor properties on it.
18541 if type.nullable():
18542 # Converting null to JSON is always OK.
18543 return CGDictionary.typeSafeToJSONify(type.inner)
18545 if type.isSequence():
18546 # Sequences are arrays we create ourselves, with no holes. They
18547 # should be safe if their contents are safe, as long as we suppress
18548 # invocation of .toJSON on objects.
18549 return CGDictionary.typeSafeToJSONify(type.inner)
18551 if type.isUnion():
18552 # OK if everything in it is ok.
18553 return all(CGDictionary.typeSafeToJSONify(t) for t in type.flatMemberTypes)
18555 if type.isDictionary():
18556 # OK if the dictionary is OK
18557 return CGDictionary.dictionarySafeToJSONify(type.inner)
18559 if type.isUndefined() or type.isString() or type.isEnum():
18560 # Strings are always OK.
18561 return True
18563 if type.isPrimitive():
18564 # Primitives (numbers and booleans) are ok, as long as
18565 # they're not unrestricted float/double.
18566 return not type.isFloat() or not type.isUnrestricted()
18568 if type.isRecord():
18569 # Records are okay, as long as the value type is.
18570 # Per spec, only strings are allowed as keys.
18571 return CGDictionary.typeSafeToJSONify(type.inner)
18573 return False
18575 @staticmethod
18576 def dictionarySafeToJSONify(dictionary):
18577 # The dictionary itself is OK, so we're good if all our types are.
18578 return all(CGDictionary.typeSafeToJSONify(m.type) for m in dictionary.members)
18581 def RegisterNonWindowBindings(descriptors):
18582 conditions = []
18583 for desc in descriptors:
18584 bindingNS = toBindingNamespace(desc.name)
18585 condition = "!%s::CreateAndDefineOnGlobal(aCx)" % bindingNS
18586 if desc.isExposedConditionally():
18587 condition = "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
18588 conditions.append(condition)
18589 lines = [
18590 CGIfWrapper(CGGeneric("return false;\n"), condition) for condition in conditions
18592 lines.append(CGGeneric("return true;\n"))
18593 return CGList(lines, "\n").define()
18596 class CGRegisterWorkerBindings(CGAbstractMethod):
18597 def __init__(self, config):
18598 CGAbstractMethod.__init__(
18599 self,
18600 None,
18601 "RegisterWorkerBindings",
18602 "bool",
18603 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
18605 self.config = config
18607 def definition_body(self):
18608 return RegisterNonWindowBindings(
18609 self.config.getDescriptors(
18610 hasInterfaceObject=True, isExposedInAnyWorker=True, register=True
18615 class CGRegisterWorkerDebuggerBindings(CGAbstractMethod):
18616 def __init__(self, config):
18617 CGAbstractMethod.__init__(
18618 self,
18619 None,
18620 "RegisterWorkerDebuggerBindings",
18621 "bool",
18622 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
18624 self.config = config
18626 def definition_body(self):
18627 return RegisterNonWindowBindings(
18628 self.config.getDescriptors(
18629 hasInterfaceObject=True, isExposedInWorkerDebugger=True, register=True
18634 class CGRegisterWorkletBindings(CGAbstractMethod):
18635 def __init__(self, config):
18636 CGAbstractMethod.__init__(
18637 self,
18638 None,
18639 "RegisterWorkletBindings",
18640 "bool",
18641 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
18643 self.config = config
18645 def definition_body(self):
18646 return RegisterNonWindowBindings(
18647 self.config.getDescriptors(
18648 hasInterfaceObject=True, isExposedInAnyWorklet=True, register=True
18653 class CGRegisterShadowRealmBindings(CGAbstractMethod):
18654 def __init__(self, config):
18655 CGAbstractMethod.__init__(
18656 self,
18657 None,
18658 "RegisterShadowRealmBindings",
18659 "bool",
18660 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
18662 self.config = config
18664 def definition_body(self):
18665 return RegisterNonWindowBindings(
18666 self.config.getDescriptors(
18667 hasInterfaceObject=True, isExposedInShadowRealms=True, register=True
18672 def BindingNamesOffsetEnum(name):
18673 return CppKeywords.checkMethodName(name.replace(" ", "_"))
18676 class CGGlobalNames(CGGeneric):
18677 def __init__(self, names):
18679 names is expected to be a list of tuples of the name and the descriptor it refers to.
18682 strings = []
18683 entries = []
18684 for name, desc in names:
18685 # Generate the entry declaration
18686 # XXX(nika): mCreate & mEnabled require relocations. If we want to
18687 # reduce those, we could move them into separate tables.
18688 nativeEntry = fill(
18691 /* mNameOffset */ BindingNamesOffset::${nameOffset},
18692 /* mNameLength */ ${nameLength},
18693 /* mConstructorId */ constructors::id::${realname},
18694 /* mCreate */ ${realname}_Binding::CreateInterfaceObjects,
18695 /* mEnabled */ ${enabled}
18697 """,
18698 nameOffset=BindingNamesOffsetEnum(name),
18699 nameLength=len(name),
18700 name=name,
18701 realname=desc.name,
18702 enabled=(
18703 "%s_Binding::ConstructorEnabled" % desc.name
18704 if desc.isExposedConditionally()
18705 else "nullptr"
18709 entries.append((name.encode(), nativeEntry))
18711 # Unfortunately, when running tests, we may have no entries.
18712 # PerfectHash will assert if we give it an empty set of entries, so we
18713 # just generate a dummy value.
18714 if len(entries) == 0:
18715 CGGeneric.__init__(
18716 self,
18717 define=dedent(
18719 static_assert(false, "No WebIDL global name entries!");
18723 return
18725 # Build the perfect hash function.
18726 phf = PerfectHash(entries, GLOBAL_NAMES_PHF_SIZE)
18728 # Generate code for the PHF
18729 phfCodegen = phf.codegen(
18730 "WebIDLGlobalNameHash::sEntries", "WebIDLNameTableEntry"
18732 entries = phfCodegen.gen_entries(lambda e: e[1])
18733 getter = phfCodegen.gen_jslinearstr_getter(
18734 name="WebIDLGlobalNameHash::GetEntry",
18735 return_type="const WebIDLNameTableEntry*",
18736 return_entry=dedent(
18738 if (JS_LinearStringEqualsAscii(aKey, BindingName(entry.mNameOffset), entry.mNameLength)) {
18739 return &entry;
18741 return nullptr;
18746 define = fill(
18748 const uint32_t WebIDLGlobalNameHash::sCount = ${count};
18750 $*{entries}
18752 $*{getter}
18753 """,
18754 count=len(phf.entries),
18755 strings="\n".join(strings) + ";\n",
18756 entries=entries,
18757 getter=getter,
18759 CGGeneric.__init__(self, define=define)
18762 def dependencySortObjects(objects, dependencyGetter, nameGetter):
18764 Sort IDL objects with dependencies on each other such that if A
18765 depends on B then B will come before A. This is needed for
18766 declaring C++ classes in the right order, for example. Objects
18767 that have no dependencies are just sorted by name.
18769 objects should be something that can produce a set of objects
18770 (e.g. a set, iterator, list, etc).
18772 dependencyGetter is something that, given an object, should return
18773 the set of objects it depends on.
18775 # XXXbz this will fail if we have two webidl files F1 and F2 such that F1
18776 # declares an object which depends on an object in F2, and F2 declares an
18777 # object (possibly a different one!) that depends on an object in F1. The
18778 # good news is that I expect this to never happen.
18779 sortedObjects = []
18780 objects = set(objects)
18781 while len(objects) != 0:
18782 # Find the dictionaries that don't depend on anything else
18783 # anymore and move them over.
18784 toMove = [o for o in objects if len(dependencyGetter(o) & objects) == 0]
18785 if len(toMove) == 0:
18786 raise TypeError(
18787 "Loop in dependency graph\n" + "\n".join(o.location for o in objects)
18789 objects = objects - set(toMove)
18790 sortedObjects.extend(sorted(toMove, key=nameGetter))
18791 return sortedObjects
18794 class ForwardDeclarationBuilder:
18796 Create a canonical representation of a set of namespaced forward
18797 declarations.
18800 def __init__(self):
18802 The set of declarations is represented as a tree of nested namespaces.
18803 Each tree node has a set of declarations |decls| and a dict |children|.
18804 Each declaration is a pair consisting of the class name and a boolean
18805 that is true iff the class is really a struct. |children| maps the
18806 names of inner namespaces to the declarations in that namespace.
18808 self.decls = set()
18809 self.children = {}
18811 def _ensureNonTemplateType(self, type):
18812 if "<" in type:
18813 # This is a templated type. We don't really know how to
18814 # forward-declare those, and trying to do it naively is not going to
18815 # go well (e.g. we may have :: characters inside the type we're
18816 # templated on!). Just bail out.
18817 raise TypeError(
18818 "Attempt to use ForwardDeclarationBuilder on "
18819 "templated type %s. We don't know how to do that "
18820 "yet." % type
18823 def _listAdd(self, namespaces, name, isStruct=False):
18825 Add a forward declaration, where |namespaces| is a list of namespaces.
18826 |name| should not contain any other namespaces.
18828 if namespaces:
18829 child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder())
18830 child._listAdd(namespaces[1:], name, isStruct)
18831 else:
18832 assert "::" not in name
18833 self.decls.add((name, isStruct))
18835 def addInMozillaDom(self, name, isStruct=False):
18837 Add a forward declaration to the mozilla::dom:: namespace. |name| should not
18838 contain any other namespaces.
18840 self._ensureNonTemplateType(name)
18841 self._listAdd(["mozilla", "dom"], name, isStruct)
18843 def add(self, nativeType, isStruct=False):
18845 Add a forward declaration, where |nativeType| is a string containing
18846 the type and its namespaces, in the usual C++ way.
18848 self._ensureNonTemplateType(nativeType)
18849 components = nativeType.split("::")
18850 self._listAdd(components[:-1], components[-1], isStruct)
18852 def _build(self, atTopLevel):
18854 Return a codegenerator for the forward declarations.
18856 decls = []
18857 if self.decls:
18858 decls.append(
18859 CGList(
18861 CGClassForwardDeclare(cname, isStruct)
18862 for cname, isStruct in sorted(self.decls)
18866 for namespace, child in sorted(self.children.items()):
18867 decls.append(CGNamespace(namespace, child._build(atTopLevel=False)))
18869 cg = CGList(decls, "\n")
18870 if not atTopLevel and len(decls) + len(self.decls) > 1:
18871 cg = CGWrapper(cg, pre="\n", post="\n")
18872 return cg
18874 def build(self):
18875 return self._build(atTopLevel=True)
18877 def forwardDeclareForType(self, t, config):
18878 t = t.unroll()
18879 if t.isGeckoInterface():
18880 name = t.inner.identifier.name
18881 try:
18882 desc = config.getDescriptor(name)
18883 self.add(desc.nativeType)
18884 except NoSuchDescriptorError:
18885 pass
18887 # Note: SpiderMonkey interfaces are typedefs, so can't be
18888 # forward-declared
18889 elif t.isPromise():
18890 self.addInMozillaDom("Promise")
18891 elif t.isCallback():
18892 self.addInMozillaDom(t.callback.identifier.name)
18893 elif t.isDictionary():
18894 self.addInMozillaDom(t.inner.identifier.name, isStruct=True)
18895 elif t.isCallbackInterface():
18896 self.addInMozillaDom(t.inner.identifier.name)
18897 elif t.isUnion():
18898 # Forward declare both the owning and non-owning version,
18899 # since we don't know which one we might want
18900 self.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
18901 self.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
18902 elif t.isRecord():
18903 self.forwardDeclareForType(t.inner, config)
18904 # Don't need to do anything for void, primitive, string, any or object.
18905 # There may be some other cases we are missing.
18908 class CGForwardDeclarations(CGWrapper):
18910 Code generate the forward declarations for a header file.
18911 additionalDeclarations is a list of tuples containing a classname and a
18912 boolean. If the boolean is true we will declare a struct, otherwise we'll
18913 declare a class.
18916 def __init__(
18917 self,
18918 config,
18919 descriptors,
18920 callbacks,
18921 dictionaries,
18922 callbackInterfaces,
18923 additionalDeclarations=[],
18925 builder = ForwardDeclarationBuilder()
18927 # Needed for at least Wrap.
18928 for d in descriptors:
18929 # If this is a generated iterator interface, we only create these
18930 # in the generated bindings, and don't need to forward declare.
18931 if (
18932 d.interface.isIteratorInterface()
18933 or d.interface.isAsyncIteratorInterface()
18935 continue
18936 builder.add(d.nativeType)
18937 if d.interface.isSerializable():
18938 builder.add("nsIGlobalObject")
18939 # If we're an interface and we have a maplike/setlike declaration,
18940 # we'll have helper functions exposed to the native side of our
18941 # bindings, which will need to show up in the header. If either of
18942 # our key/value types are interfaces, they'll be passed as
18943 # arguments to helper functions, and they'll need to be forward
18944 # declared in the header.
18945 if d.interface.maplikeOrSetlikeOrIterable:
18946 if d.interface.maplikeOrSetlikeOrIterable.hasKeyType():
18947 builder.forwardDeclareForType(
18948 d.interface.maplikeOrSetlikeOrIterable.keyType, config
18950 if d.interface.maplikeOrSetlikeOrIterable.hasValueType():
18951 builder.forwardDeclareForType(
18952 d.interface.maplikeOrSetlikeOrIterable.valueType, config
18955 for m in d.interface.members:
18956 if m.isAttr() and m.type.isObservableArray():
18957 builder.forwardDeclareForType(m.type, config)
18959 # We just about always need NativePropertyHooks
18960 builder.addInMozillaDom("NativePropertyHooks", isStruct=True)
18961 builder.addInMozillaDom("ProtoAndIfaceCache")
18963 for callback in callbacks:
18964 builder.addInMozillaDom(callback.identifier.name)
18965 for t in getTypesFromCallback(callback):
18966 builder.forwardDeclareForType(t, config)
18968 for d in callbackInterfaces:
18969 builder.add(d.nativeType)
18970 builder.add(d.nativeType + "Atoms", isStruct=True)
18971 for t in getTypesFromDescriptor(d):
18972 builder.forwardDeclareForType(t, config)
18973 if d.hasCEReactions():
18974 builder.addInMozillaDom("DocGroup")
18976 for d in dictionaries:
18977 if len(d.members) > 0:
18978 builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True)
18979 for t in getTypesFromDictionary(d):
18980 builder.forwardDeclareForType(t, config)
18982 for className, isStruct in additionalDeclarations:
18983 builder.add(className, isStruct=isStruct)
18985 CGWrapper.__init__(self, builder.build())
18988 def dependencySortDictionariesAndUnionsAndCallbacks(types):
18989 def getDependenciesFromType(type):
18990 if type.isDictionary():
18991 return set([type.unroll().inner])
18992 if type.isSequence():
18993 return getDependenciesFromType(type.unroll())
18994 if type.isUnion():
18995 return set([type.unroll()])
18996 if type.isRecord():
18997 return set([type.unroll().inner])
18998 if type.isCallback():
18999 return set([type.unroll()])
19000 return set()
19002 def getDependencies(unionTypeOrDictionaryOrCallback):
19003 if isinstance(unionTypeOrDictionaryOrCallback, IDLDictionary):
19004 deps = set()
19005 if unionTypeOrDictionaryOrCallback.parent:
19006 deps.add(unionTypeOrDictionaryOrCallback.parent)
19007 for member in unionTypeOrDictionaryOrCallback.members:
19008 deps |= getDependenciesFromType(member.type)
19009 return deps
19011 if (
19012 unionTypeOrDictionaryOrCallback.isType()
19013 and unionTypeOrDictionaryOrCallback.isUnion()
19015 deps = set()
19016 for member in unionTypeOrDictionaryOrCallback.flatMemberTypes:
19017 deps |= getDependenciesFromType(member)
19018 return deps
19020 assert unionTypeOrDictionaryOrCallback.isCallback()
19021 return set()
19023 def getName(unionTypeOrDictionaryOrCallback):
19024 if isinstance(unionTypeOrDictionaryOrCallback, IDLDictionary):
19025 return unionTypeOrDictionaryOrCallback.identifier.name
19027 if (
19028 unionTypeOrDictionaryOrCallback.isType()
19029 and unionTypeOrDictionaryOrCallback.isUnion()
19031 return unionTypeOrDictionaryOrCallback.name
19033 assert unionTypeOrDictionaryOrCallback.isCallback()
19034 return unionTypeOrDictionaryOrCallback.identifier.name
19036 return dependencySortObjects(types, getDependencies, getName)
19039 class CGBindingRoot(CGThing):
19041 Root codegen class for binding generation. Instantiate the class, and call
19042 declare or define to generate header or cpp code (respectively).
19045 def __init__(self, config, prefix, webIDLFile):
19046 bindingHeaders = dict.fromkeys(
19047 ("mozilla/dom/NonRefcountedDOMObject.h", "MainThreadUtils.h"), True
19049 bindingDeclareHeaders = dict.fromkeys(
19051 "mozilla/dom/BindingDeclarations.h",
19052 "mozilla/dom/Nullable.h",
19054 True,
19057 descriptors = config.getDescriptors(
19058 webIDLFile=webIDLFile, hasInterfaceOrInterfacePrototypeObject=True
19060 descriptors.extend(
19061 config.getDescriptors(
19062 webIDLFile=webIDLFile, hasOrdinaryObjectPrototype=True
19066 unionTypes = UnionsForFile(config, webIDLFile)
19069 unionHeaders,
19070 unionImplheaders,
19071 unionDeclarations,
19072 traverseMethods,
19073 unlinkMethods,
19074 unionStructs,
19075 ) = UnionTypes(unionTypes, config)
19077 bindingDeclareHeaders.update(dict.fromkeys(unionHeaders, True))
19078 bindingHeaders.update(dict.fromkeys(unionImplheaders, True))
19079 bindingDeclareHeaders["mozilla/dom/UnionMember.h"] = len(unionStructs) > 0
19080 bindingDeclareHeaders["mozilla/dom/FakeString.h"] = len(unionStructs) > 0
19081 # BindingUtils.h is only needed for SetToObject.
19082 # If it stops being inlined or stops calling CallerSubsumes
19083 # both this bit and the bit in UnionTypes can be removed.
19084 bindingDeclareHeaders["mozilla/dom/BindingUtils.h"] = any(
19085 d.isObject() for t in unionTypes for d in t.flatMemberTypes
19087 bindingDeclareHeaders["mozilla/dom/JSSlots.h"] = any(
19088 d.interface.reflectedHTMLAttributesReturningFrozenArray for d in descriptors
19090 bindingHeaders["mozilla/dom/IterableIterator.h"] = any(
19092 d.interface.isIteratorInterface()
19093 and d.interface.maplikeOrSetlikeOrIterable.isPairIterator()
19095 or d.interface.isAsyncIteratorInterface()
19096 or d.interface.isIterable()
19097 or d.interface.isAsyncIterable()
19098 for d in descriptors
19101 def memberNeedsSubjectPrincipal(d, m):
19102 if m.isAttr():
19103 return (
19104 "needsSubjectPrincipal" in d.getExtendedAttributes(m, getter=True)
19105 ) or (
19106 not m.readonly
19107 and "needsSubjectPrincipal"
19108 in d.getExtendedAttributes(m, setter=True)
19110 return m.isMethod() and "needsSubjectPrincipal" in d.getExtendedAttributes(
19114 if any(
19115 memberNeedsSubjectPrincipal(d, m)
19116 for d in descriptors
19117 for m in d.interface.members
19119 bindingHeaders["mozilla/BasePrincipal.h"] = True
19120 bindingHeaders["nsJSPrincipals.h"] = True
19122 # The conditions for which we generate profiler labels are fairly
19123 # complicated. The check below is a little imprecise to make it simple.
19124 # It includes the profiler header in all cases where it is necessary and
19125 # generates only a few false positives.
19126 bindingHeaders["mozilla/ProfilerLabels.h"] = any(
19127 # constructor profiler label
19128 d.interface.legacyFactoryFunctions
19129 or (d.interface.hasInterfaceObject() and d.interface.ctor())
19130 or any(
19131 # getter/setter profiler labels
19132 m.isAttr()
19133 # method profiler label
19134 or m.isMethod()
19135 for m in d.interface.members
19137 for d in descriptors
19140 def descriptorHasCrossOriginProperties(desc):
19141 def hasCrossOriginProperty(m):
19142 props = memberProperties(m, desc)
19143 return (
19144 props.isCrossOriginMethod
19145 or props.isCrossOriginGetter
19146 or props.isCrossOriginSetter
19149 return any(hasCrossOriginProperty(m) for m in desc.interface.members)
19151 def descriptorHasObservableArrayTypes(desc):
19152 def hasObservableArrayTypes(m):
19153 return m.isAttr() and m.type.isObservableArray()
19155 return any(hasObservableArrayTypes(m) for m in desc.interface.members)
19157 bindingDeclareHeaders["mozilla/dom/RemoteObjectProxy.h"] = any(
19158 descriptorHasCrossOriginProperties(d) for d in descriptors
19160 bindingDeclareHeaders["jsapi.h"] = any(
19161 descriptorHasCrossOriginProperties(d)
19162 or descriptorHasObservableArrayTypes(d)
19163 for d in descriptors
19165 bindingDeclareHeaders["js/TypeDecls.h"] = not bindingDeclareHeaders["jsapi.h"]
19166 bindingDeclareHeaders["js/RootingAPI.h"] = not bindingDeclareHeaders["jsapi.h"]
19168 # JS::IsCallable
19169 bindingDeclareHeaders["js/CallAndConstruct.h"] = True
19171 def descriptorHasIteratorAlias(desc):
19172 def hasIteratorAlias(m):
19173 return m.isMethod() and (
19174 ("@@iterator" in m.aliases) or ("@@asyncIterator" in m.aliases)
19177 return any(hasIteratorAlias(m) for m in desc.interface.members)
19179 bindingHeaders["js/Symbol.h"] = any(
19180 descriptorHasIteratorAlias(d) for d in descriptors
19183 bindingHeaders["js/shadow/Object.h"] = any(
19184 d.interface.hasMembersInSlots() for d in descriptors
19187 # The symbols supplied by this header are used so ubiquitously it's not
19188 # worth the effort delineating the exact dependency, if it can't be done
19189 # *at* the places where their definitions are required.
19190 bindingHeaders["js/experimental/JitInfo.h"] = True
19192 # JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, and
19193 # JS::SetReservedSlot are also used too many places to restate
19194 # dependency logic.
19195 bindingHeaders["js/Object.h"] = True
19197 # JS::IsCallable, JS::Call, JS::Construct
19198 bindingHeaders["js/CallAndConstruct.h"] = True
19200 # JS_IsExceptionPending
19201 bindingHeaders["js/Exception.h"] = True
19203 # JS::Map{Clear, Delete, Has, Get, Set}
19204 bindingHeaders["js/MapAndSet.h"] = True
19206 # JS_DefineElement, JS_DefineProperty, JS_DefinePropertyById,
19207 # JS_DefineUCProperty, JS_ForwardGetPropertyTo, JS_GetProperty,
19208 # JS_GetPropertyById, JS_HasPropertyById, JS_SetProperty,
19209 # JS_SetPropertyById
19210 bindingHeaders["js/PropertyAndElement.h"] = True
19212 # JS_GetOwnPropertyDescriptorById
19213 bindingHeaders["js/PropertyDescriptor.h"] = True
19215 def descriptorDeprecated(desc):
19216 iface = desc.interface
19217 return any(
19218 m.getExtendedAttribute("Deprecated") for m in iface.members + [iface]
19221 bindingHeaders["mozilla/dom/Document.h"] = any(
19222 descriptorDeprecated(d) for d in descriptors
19225 bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any(
19226 d.concrete and d.proxy for d in descriptors
19229 bindingHeaders["mozilla/dom/ProxyHandlerUtils.h"] = any(
19230 d.concrete and d.proxy for d in descriptors
19233 bindingHeaders["js/String.h"] = any(
19234 d.needsMissingPropUseCounters for d in descriptors
19237 hasCrossOriginObjects = any(
19238 d.concrete and d.isMaybeCrossOriginObject() for d in descriptors
19240 bindingHeaders["mozilla/dom/MaybeCrossOriginObject.h"] = hasCrossOriginObjects
19241 bindingHeaders["AccessCheck.h"] = hasCrossOriginObjects
19242 hasCEReactions = any(d.hasCEReactions() for d in descriptors)
19243 bindingHeaders["mozilla/dom/CustomElementRegistry.h"] = hasCEReactions
19244 bindingHeaders["mozilla/dom/DocGroup.h"] = hasCEReactions
19246 def descriptorHasChromeOnly(desc):
19247 ctor = desc.interface.ctor()
19249 return (
19250 any(
19251 isChromeOnly(a) or needsCallerType(a)
19252 for a in desc.interface.members
19254 or desc.interface.getExtendedAttribute("ChromeOnly") is not None
19256 # JS-implemented interfaces with an interface object get a
19257 # chromeonly _create method. And interfaces with an
19258 # interface object might have a ChromeOnly constructor.
19260 desc.interface.hasInterfaceObject()
19261 and (
19262 desc.interface.isJSImplemented()
19263 or (ctor and isChromeOnly(ctor))
19268 # XXXkhuey ugly hack but this is going away soon.
19269 bindingHeaders["xpcprivate.h"] = webIDLFile.endswith("EventTarget.webidl")
19271 hasThreadChecks = any(d.hasThreadChecks() for d in descriptors)
19272 bindingHeaders["nsThreadUtils.h"] = hasThreadChecks
19274 dictionaries = config.getDictionaries(webIDLFile)
19276 def dictionaryHasChromeOnly(dictionary):
19277 while dictionary:
19278 if any(isChromeOnly(m) for m in dictionary.members):
19279 return True
19280 dictionary = dictionary.parent
19281 return False
19283 def needsNonSystemPrincipal(member):
19284 return (
19285 member.getExtendedAttribute("NeedsSubjectPrincipal") == ["NonSystem"]
19286 or member.getExtendedAttribute("SetterNeedsSubjectPrincipal")
19287 == ["NonSystem"]
19288 or member.getExtendedAttribute("GetterNeedsSubjectPrincipal")
19289 == ["NonSystem"]
19292 def descriptorNeedsNonSystemPrincipal(d):
19293 return any(needsNonSystemPrincipal(m) for m in d.interface.members)
19295 def descriptorHasPrefDisabler(desc):
19296 iface = desc.interface
19297 return any(
19298 PropertyDefiner.getControllingCondition(m, desc).hasDisablers()
19299 for m in iface.members
19300 if (m.isMethod() or m.isAttr() or m.isConst())
19303 def addPrefHeaderForObject(bindingHeaders, obj):
19305 obj might be a dictionary member or an interface.
19307 if obj is not None:
19308 pref = PropertyDefiner.getStringAttr(obj, "Pref")
19309 if pref:
19310 bindingHeaders[prefHeader(pref)] = True
19312 def addPrefHeadersForDictionary(bindingHeaders, dictionary):
19313 while dictionary:
19314 for m in dictionary.members:
19315 addPrefHeaderForObject(bindingHeaders, m)
19316 dictionary = dictionary.parent
19318 for d in dictionaries:
19319 addPrefHeadersForDictionary(bindingHeaders, d)
19320 for d in descriptors:
19321 interface = d.interface
19322 addPrefHeaderForObject(bindingHeaders, interface)
19323 addPrefHeaderForObject(bindingHeaders, interface.ctor())
19325 bindingHeaders["mozilla/dom/WebIDLPrefs.h"] = any(
19326 descriptorHasPrefDisabler(d) for d in descriptors
19328 bindingHeaders["nsContentUtils.h"] = (
19329 any(descriptorHasChromeOnly(d) for d in descriptors)
19330 or any(descriptorNeedsNonSystemPrincipal(d) for d in descriptors)
19331 or any(dictionaryHasChromeOnly(d) for d in dictionaries)
19333 hasNonEmptyDictionaries = any(len(dict.members) > 0 for dict in dictionaries)
19334 callbacks = config.getCallbacks(webIDLFile)
19335 callbackDescriptors = config.getDescriptors(
19336 webIDLFile=webIDLFile, isCallback=True
19338 jsImplemented = config.getDescriptors(
19339 webIDLFile=webIDLFile, isJSImplemented=True
19341 bindingDeclareHeaders["nsWeakReference.h"] = jsImplemented
19342 bindingDeclareHeaders["mozilla/dom/PrototypeList.h"] = descriptors
19343 bindingHeaders["nsIGlobalObject.h"] = jsImplemented
19344 bindingHeaders["AtomList.h"] = (
19345 hasNonEmptyDictionaries or jsImplemented or callbackDescriptors
19348 if callbackDescriptors:
19349 bindingDeclareHeaders["mozilla/ErrorResult.h"] = True
19351 def descriptorClearsPropsInSlots(descriptor):
19352 if not descriptor.wrapperCache:
19353 return False
19354 return any(
19355 m.isAttr() and m.getExtendedAttribute("StoreInSlot")
19356 for m in descriptor.interface.members
19359 bindingHeaders["nsJSUtils.h"] = any(
19360 descriptorClearsPropsInSlots(d) for d in descriptors
19363 # Make sure we can sanely use binding_detail in generated code.
19364 cgthings = [
19365 CGGeneric(
19366 dedent(
19368 namespace binding_detail {}; // Just to make sure it's known as a namespace
19369 using namespace mozilla::dom::binding_detail;
19375 # Do codegen for all the enums
19376 enums = config.getEnums(webIDLFile)
19377 cgthings.extend(CGEnum(e) for e in enums)
19378 maxEnumValues = CGList([CGMaxContiguousEnumValue(e) for e in enums], "\n")
19380 bindingDeclareHeaders["mozilla/Span.h"] = enums
19381 bindingDeclareHeaders["mozilla/ArrayUtils.h"] = enums
19382 bindingDeclareHeaders["mozilla/EnumTypeTraits.h"] = enums
19384 hasCode = descriptors or callbackDescriptors or dictionaries or callbacks
19385 bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode
19386 bindingHeaders["mozilla/OwningNonNull.h"] = hasCode
19387 bindingHeaders["<type_traits>"] = hasCode
19388 bindingHeaders["mozilla/dom/BindingDeclarations.h"] = not hasCode and enums
19390 bindingHeaders["WrapperFactory.h"] = descriptors
19391 bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors
19392 bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries # AutoJSAPI
19393 # Ensure we see our enums in the generated .cpp file, for the ToJSValue
19394 # method body. Also ensure that we see jsapi.h.
19395 if enums:
19396 bindingHeaders[CGHeaders.getDeclarationFilename(enums[0])] = True
19397 bindingHeaders["jsapi.h"] = True
19399 # For things that have [UseCounter] or [InstrumentedProps] or [Trial]
19400 for d in descriptors:
19401 if d.concrete:
19402 if d.instrumentedProps:
19403 bindingHeaders["mozilla/UseCounter.h"] = True
19404 if d.needsMissingPropUseCounters:
19405 bindingHeaders[prefHeader(MISSING_PROP_PREF)] = True
19406 if d.interface.isSerializable():
19407 bindingHeaders["mozilla/dom/StructuredCloneTags.h"] = True
19408 if d.wantsXrays:
19409 bindingHeaders["mozilla/Atomics.h"] = True
19410 bindingHeaders["mozilla/dom/XrayExpandoClass.h"] = True
19411 if d.wantsXrayExpandoClass:
19412 bindingHeaders["XrayWrapper.h"] = True
19413 for m in d.interface.members:
19414 if m.getExtendedAttribute("UseCounter"):
19415 bindingHeaders["mozilla/UseCounter.h"] = True
19416 if m.getExtendedAttribute("Trial"):
19417 bindingHeaders["mozilla/OriginTrials.h"] = True
19419 bindingHeaders["mozilla/dom/SimpleGlobalObject.h"] = any(
19420 CGDictionary.dictionarySafeToJSONify(d) for d in dictionaries
19423 for ancestor in (findAncestorWithInstrumentedProps(d) for d in descriptors):
19424 if not ancestor:
19425 continue
19426 bindingHeaders[CGHeaders.getDeclarationFilename(ancestor)] = True
19428 for d in descriptors:
19429 ancestor = d.interface.parent
19430 while ancestor:
19431 if ancestor.reflectedHTMLAttributesReturningFrozenArray:
19432 bindingHeaders[CGHeaders.getDeclarationFilename(ancestor)] = True
19433 break
19434 ancestor = ancestor.parent
19436 cgthings.extend(traverseMethods)
19437 cgthings.extend(unlinkMethods)
19439 # Do codegen for all the dictionaries. We have to be a bit careful
19440 # here, because we have to generate these in order from least derived
19441 # to most derived so that class inheritance works out. We also have to
19442 # generate members before the dictionary that contains them.
19444 for t in dependencySortDictionariesAndUnionsAndCallbacks(
19445 dictionaries + unionStructs + callbacks
19447 if t.isDictionary():
19448 cgthings.append(CGDictionary(t, config))
19449 elif t.isUnion():
19450 cgthings.append(CGUnionStruct(t, config))
19451 cgthings.append(CGUnionStruct(t, config, True))
19452 else:
19453 assert t.isCallback()
19454 cgthings.append(CGCallbackFunction(t, config))
19455 cgthings.append(CGNamespace("binding_detail", CGFastCallback(t)))
19457 # Do codegen for all the descriptors
19458 cgthings.extend(
19459 [CGDescriptor(x, config.attributeTemplates) for x in descriptors]
19462 # Do codegen for all the callback interfaces.
19463 cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors])
19465 cgthings.extend(
19467 CGNamespace("binding_detail", CGFastCallback(x.interface))
19468 for x in callbackDescriptors
19472 # Do codegen for JS implemented classes
19473 def getParentDescriptor(desc):
19474 if not desc.interface.parent:
19475 return set()
19476 return {desc.getDescriptor(desc.interface.parent.identifier.name)}
19478 for x in dependencySortObjects(
19479 jsImplemented, getParentDescriptor, lambda d: d.interface.identifier.name
19481 cgthings.append(
19482 CGCallbackInterface(x, spiderMonkeyInterfacesAreStructs=True)
19484 cgthings.append(CGJSImplClass(x))
19486 # And make sure we have the right number of newlines at the end
19487 curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
19489 # Wrap all of that in our namespaces.
19491 if len(maxEnumValues) > 0:
19492 curr = CGNamespace("dom", CGWrapper(curr, pre="\n"))
19493 curr = CGWrapper(CGList([curr, maxEnumValues], "\n\n"), post="\n\n")
19494 curr = CGNamespace("mozilla", CGWrapper(curr, pre="\n"))
19495 else:
19496 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, pre="\n"))
19498 curr = CGList(
19500 CGForwardDeclarations(
19501 config,
19502 descriptors,
19503 callbacks,
19504 dictionaries,
19505 callbackDescriptors + jsImplemented,
19506 additionalDeclarations=unionDeclarations,
19508 curr,
19510 "\n",
19513 # Add header includes.
19514 bindingHeaders = [
19515 header for header, include in bindingHeaders.items() if include
19517 bindingDeclareHeaders = [
19518 header for header, include in bindingDeclareHeaders.items() if include
19521 curr = CGHeaders(
19522 descriptors,
19523 dictionaries,
19524 callbacks,
19525 callbackDescriptors,
19526 bindingDeclareHeaders,
19527 bindingHeaders,
19528 prefix,
19529 curr,
19530 config,
19531 jsImplemented,
19534 # Add include guards.
19535 curr = CGIncludeGuard(prefix, curr)
19537 # Add the auto-generated comment.
19538 curr = CGWrapper(
19539 curr,
19540 pre=(
19541 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT % os.path.basename(webIDLFile)
19545 # Store the final result.
19546 self.root = curr
19548 def declare(self):
19549 return stripTrailingWhitespace(self.root.declare())
19551 def define(self):
19552 return stripTrailingWhitespace(self.root.define())
19554 def deps(self):
19555 return self.root.deps()
19558 class CGNativeMember(ClassMethod):
19559 def __init__(
19560 self,
19561 descriptorProvider,
19562 member,
19563 name,
19564 signature,
19565 extendedAttrs,
19566 breakAfter=True,
19567 passJSBitsAsNeeded=True,
19568 visibility="public",
19569 spiderMonkeyInterfacesAreStructs=True,
19570 variadicIsSequence=False,
19571 resultNotAddRefed=False,
19572 virtual=False,
19573 override=False,
19574 canRunScript=False,
19577 If spiderMonkeyInterfacesAreStructs is false, SpiderMonkey interfaces
19578 will be passed as JS::Handle<JSObject*>. If it's true they will be
19579 passed as one of the dom::SpiderMonkeyInterfaceObjectStorage subclasses.
19581 If passJSBitsAsNeeded is false, we don't automatically pass in a
19582 JSContext* or a JSObject* based on the return and argument types. We
19583 can still pass it based on 'implicitJSContext' annotations.
19585 self.descriptorProvider = descriptorProvider
19586 self.member = member
19587 self.extendedAttrs = extendedAttrs
19588 self.resultAlreadyAddRefed = not resultNotAddRefed
19589 self.passJSBitsAsNeeded = passJSBitsAsNeeded
19590 self.spiderMonkeyInterfacesAreStructs = spiderMonkeyInterfacesAreStructs
19591 self.variadicIsSequence = variadicIsSequence
19592 breakAfterSelf = "\n" if breakAfter else ""
19593 ClassMethod.__init__(
19594 self,
19595 name,
19596 self.getReturnType(signature[0], False),
19597 self.getArgs(signature[0], signature[1]),
19598 static=member.isStatic(),
19599 # Mark our getters, which are attrs that
19600 # have a non-void return type, as const.
19601 const=(
19602 not member.isStatic()
19603 and member.isAttr()
19604 and not signature[0].isUndefined()
19606 breakAfterReturnDecl=" ",
19607 breakAfterSelf=breakAfterSelf,
19608 visibility=visibility,
19609 virtual=virtual,
19610 override=override,
19611 canRunScript=canRunScript,
19614 def getReturnType(self, type, isMember):
19615 return self.getRetvalInfo(type, isMember)[0]
19617 def getRetvalInfo(self, type, isMember):
19619 Returns a tuple:
19621 The first element is the type declaration for the retval
19623 The second element is a default value that can be used on error returns.
19624 For cases whose behavior depends on isMember, the second element will be
19625 None if isMember is true.
19627 The third element is a template for actually returning a value stored in
19628 "${declName}" and "${holderName}". This means actually returning it if
19629 we're not outparam, else assigning to the "retval" outparam. If
19630 isMember is true, this can be None, since in that case the caller will
19631 never examine this value.
19633 if type.isUndefined():
19634 return "void", "", ""
19635 if type.isPrimitive() and type.tag() in builtinNames:
19636 result = CGGeneric(builtinNames[type.tag()])
19637 defaultReturnArg = "0"
19638 if type.nullable():
19639 result = CGTemplatedType("Nullable", result)
19640 defaultReturnArg = ""
19641 return (
19642 result.define(),
19643 "%s(%s)" % (result.define(), defaultReturnArg),
19644 "return ${declName};\n",
19646 if type.isJSString():
19647 if isMember:
19648 raise TypeError("JSString not supported as return type member")
19649 # Outparam
19650 return "void", "", "aRetVal.set(${declName});\n"
19651 if type.isDOMString() or type.isUSVString():
19652 if isMember:
19653 # No need for a third element in the isMember case
19654 return "nsString", None, None
19655 # Outparam
19656 return "void", "", "aRetVal = ${declName};\n"
19657 if type.isByteString() or type.isUTF8String():
19658 if isMember:
19659 # No need for a third element in the isMember case
19660 return "nsCString", None, None
19661 # Outparam
19662 return "void", "", "aRetVal = ${declName};\n"
19663 if type.isEnum():
19664 enumName = type.unroll().inner.identifier.name
19665 if type.nullable():
19666 enumName = CGTemplatedType("Nullable", CGGeneric(enumName)).define()
19667 defaultValue = "%s()" % enumName
19668 else:
19669 defaultValue = "%s(0)" % enumName
19670 return enumName, defaultValue, "return ${declName};\n"
19671 if type.isGeckoInterface() or type.isPromise():
19672 if type.isGeckoInterface():
19673 iface = type.unroll().inner
19674 result = CGGeneric(
19675 self.descriptorProvider.getDescriptor(
19676 iface.identifier.name
19677 ).prettyNativeType
19679 else:
19680 result = CGGeneric("Promise")
19681 if self.resultAlreadyAddRefed:
19682 if isMember:
19683 holder = "RefPtr"
19684 else:
19685 holder = "already_AddRefed"
19686 if memberReturnsNewObject(self.member) or isMember:
19687 warning = ""
19688 else:
19689 warning = "// Return a raw pointer here to avoid refcounting, but make sure it's safe (the object should be kept alive by the callee).\n"
19690 result = CGWrapper(result, pre=("%s%s<" % (warning, holder)), post=">")
19691 else:
19692 result = CGWrapper(result, post="*")
19693 # Since we always force an owning type for callback return values,
19694 # our ${declName} is an OwningNonNull or RefPtr. So we can just
19695 # .forget() to get our already_AddRefed.
19696 return result.define(), "nullptr", "return ${declName}.forget();\n"
19697 if type.isCallback():
19698 return (
19699 "already_AddRefed<%s>" % type.unroll().callback.identifier.name,
19700 "nullptr",
19701 "return ${declName}.forget();\n",
19703 if type.isAny():
19704 if isMember:
19705 # No need for a third element in the isMember case
19706 return "JS::Value", None, None
19707 # Outparam
19708 return "void", "", "aRetVal.set(${declName});\n"
19710 if type.isObject():
19711 if isMember:
19712 # No need for a third element in the isMember case
19713 return "JSObject*", None, None
19714 return "void", "", "aRetVal.set(${declName});\n"
19715 if type.isSpiderMonkeyInterface():
19716 if isMember:
19717 # No need for a third element in the isMember case
19718 return "JSObject*", None, None
19719 if type.nullable():
19720 returnCode = (
19721 "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj()"
19723 else:
19724 returnCode = "${declName}.Obj()"
19725 return "void", "", "aRetVal.set(%s);\n" % returnCode
19726 if type.isSequence():
19727 # If we want to handle sequence-of-sequences return values, we're
19728 # going to need to fix example codegen to not produce nsTArray<void>
19729 # for the relevant argument...
19730 assert not isMember
19731 # Outparam.
19732 if type.nullable():
19733 returnCode = dedent(
19735 if (${declName}.IsNull()) {
19736 aRetVal.SetNull();
19737 } else {
19738 aRetVal.SetValue() = std::move(${declName}.Value());
19742 else:
19743 returnCode = "aRetVal = std::move(${declName});\n"
19744 return "void", "", returnCode
19745 if type.isRecord():
19746 # If we want to handle record-of-record return values, we're
19747 # going to need to fix example codegen to not produce record<void>
19748 # for the relevant argument...
19749 assert not isMember
19750 # In this case we convert directly into our outparam to start with
19751 return "void", "", ""
19752 if type.isDictionary():
19753 if isMember:
19754 # Only the first member of the tuple matters here, but return
19755 # bogus values for the others in case someone decides to use
19756 # them.
19757 return CGDictionary.makeDictionaryName(type.inner), None, None
19758 # In this case we convert directly into our outparam to start with
19759 return "void", "", ""
19760 if type.isUnion():
19761 if isMember:
19762 # Only the first member of the tuple matters here, but return
19763 # bogus values for the others in case someone decides to use
19764 # them.
19765 return CGUnionStruct.unionTypeDecl(type, True), None, None
19766 # In this case we convert directly into our outparam to start with
19767 return "void", "", ""
19769 raise TypeError("Don't know how to declare return value for %s" % type)
19771 def getArgs(self, returnType, argList):
19772 args = [self.getArg(arg) for arg in argList]
19773 # Now the outparams
19774 if returnType.isJSString():
19775 args.append(Argument("JS::MutableHandle<JSString*>", "aRetVal"))
19776 elif returnType.isDOMString() or returnType.isUSVString():
19777 args.append(Argument("nsString&", "aRetVal"))
19778 elif returnType.isByteString() or returnType.isUTF8String():
19779 args.append(Argument("nsCString&", "aRetVal"))
19780 elif returnType.isSequence():
19781 nullable = returnType.nullable()
19782 if nullable:
19783 returnType = returnType.inner
19784 # And now the actual underlying type
19785 elementDecl = self.getReturnType(returnType.inner, True)
19786 type = CGTemplatedType("nsTArray", CGGeneric(elementDecl))
19787 if nullable:
19788 type = CGTemplatedType("Nullable", type)
19789 args.append(Argument("%s&" % type.define(), "aRetVal"))
19790 elif returnType.isRecord():
19791 nullable = returnType.nullable()
19792 if nullable:
19793 returnType = returnType.inner
19794 # And now the actual underlying type
19795 elementDecl = self.getReturnType(returnType.inner, True)
19796 type = CGTemplatedType(
19797 "Record", [recordKeyDeclType(returnType), CGGeneric(elementDecl)]
19799 if nullable:
19800 type = CGTemplatedType("Nullable", type)
19801 args.append(Argument("%s&" % type.define(), "aRetVal"))
19802 elif returnType.isDictionary():
19803 nullable = returnType.nullable()
19804 if nullable:
19805 returnType = returnType.inner
19806 dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner))
19807 if nullable:
19808 dictType = CGTemplatedType("Nullable", dictType)
19809 args.append(Argument("%s&" % dictType.define(), "aRetVal"))
19810 elif returnType.isUnion():
19811 args.append(
19812 Argument(
19813 "%s&" % CGUnionStruct.unionTypeDecl(returnType, True), "aRetVal"
19816 elif returnType.isAny():
19817 args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal"))
19818 elif returnType.isObject() or returnType.isSpiderMonkeyInterface():
19819 args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal"))
19821 # And the nsIPrincipal
19822 if "needsSubjectPrincipal" in self.extendedAttrs:
19823 if "needsNonSystemSubjectPrincipal" in self.extendedAttrs:
19824 args.append(Argument("nsIPrincipal*", "aPrincipal"))
19825 else:
19826 args.append(Argument("nsIPrincipal&", "aPrincipal"))
19827 # And the caller type, if desired.
19828 if needsCallerType(self.member):
19829 args.append(Argument("CallerType", "aCallerType"))
19830 # And the ErrorResult or OOMReporter
19831 if "needsErrorResult" in self.extendedAttrs:
19832 # Use aRv so it won't conflict with local vars named "rv"
19833 args.append(Argument("ErrorResult&", "aRv"))
19834 elif "canOOM" in self.extendedAttrs:
19835 args.append(Argument("OOMReporter&", "aRv"))
19837 # The legacycaller thisval
19838 if self.member.isMethod() and self.member.isLegacycaller():
19839 # If it has an identifier, we can't deal with it yet
19840 assert self.member.isIdentifierLess()
19841 args.insert(0, Argument("const JS::Value&", "aThisVal"))
19842 # And jscontext bits.
19843 if needCx(
19844 returnType,
19845 argList,
19846 self.extendedAttrs,
19847 self.passJSBitsAsNeeded,
19848 self.member.isStatic(),
19850 args.insert(0, Argument("JSContext*", "cx"))
19851 if needScopeObject(
19852 returnType,
19853 argList,
19854 self.extendedAttrs,
19855 self.descriptorProvider.wrapperCache,
19856 self.passJSBitsAsNeeded,
19857 self.member.getExtendedAttribute("StoreInSlot"),
19859 args.insert(1, Argument("JS::Handle<JSObject*>", "obj"))
19860 # And if we're static, a global
19861 if self.member.isStatic():
19862 args.insert(0, Argument("const GlobalObject&", "global"))
19863 return args
19865 def doGetArgType(self, type, optional, isMember):
19867 The main work of getArgType. Returns a string type decl, whether this
19868 is a const ref, as well as whether the type should be wrapped in
19869 Nullable as needed.
19871 isMember can be false or one of the strings "Sequence", "Variadic",
19872 "Record"
19874 if type.isSequence():
19875 nullable = type.nullable()
19876 if nullable:
19877 type = type.inner
19878 elementType = type.inner
19879 argType = self.getArgType(elementType, False, "Sequence")[0]
19880 decl = CGTemplatedType("Sequence", argType)
19881 return decl.define(), True, True
19883 if type.isRecord():
19884 nullable = type.nullable()
19885 if nullable:
19886 type = type.inner
19887 elementType = type.inner
19888 argType = self.getArgType(elementType, False, "Record")[0]
19889 decl = CGTemplatedType("Record", [recordKeyDeclType(type), argType])
19890 return decl.define(), True, True
19892 if type.isUnion():
19893 # unionTypeDecl will handle nullable types, so return False for
19894 # auto-wrapping in Nullable
19895 return CGUnionStruct.unionTypeDecl(type, isMember), True, False
19897 if type.isPromise():
19898 assert not type.nullable()
19899 if optional or isMember:
19900 typeDecl = "OwningNonNull<Promise>"
19901 else:
19902 typeDecl = "Promise&"
19903 return (typeDecl, False, False)
19905 if type.isGeckoInterface() and not type.isCallbackInterface():
19906 iface = type.unroll().inner
19907 if iface.identifier.name == "WindowProxy":
19908 return "WindowProxyHolder", True, False
19910 argIsPointer = type.nullable() or iface.isExternal()
19911 forceOwningType = iface.isCallback() or isMember
19912 if argIsPointer:
19913 if (optional or isMember) and forceOwningType:
19914 typeDecl = "RefPtr<%s>"
19915 else:
19916 typeDecl = "%s*"
19917 else:
19918 if optional or isMember:
19919 if forceOwningType:
19920 typeDecl = "OwningNonNull<%s>"
19921 else:
19922 typeDecl = "NonNull<%s>"
19923 else:
19924 typeDecl = "%s&"
19925 return (
19927 typeDecl
19928 % self.descriptorProvider.getDescriptor(
19929 iface.identifier.name
19930 ).prettyNativeType
19932 False,
19933 False,
19936 if type.isSpiderMonkeyInterface():
19937 if not self.spiderMonkeyInterfacesAreStructs:
19938 return "JS::Handle<JSObject*>", False, False
19940 # Unroll for the name, in case we're nullable.
19941 return type.unroll().name, True, True
19943 if type.isJSString():
19944 if isMember:
19945 raise TypeError("JSString not supported as member")
19946 return "JS::Handle<JSString*>", False, False
19948 if type.isDOMString() or type.isUSVString():
19949 if isMember:
19950 declType = "nsString"
19951 else:
19952 declType = "nsAString"
19953 return declType, True, False
19955 if type.isByteString() or type.isUTF8String():
19956 # TODO(emilio): Maybe bytestrings could benefit from nsAutoCString
19957 # or such too.
19958 if type.isUTF8String() and not isMember:
19959 declType = "nsACString"
19960 else:
19961 declType = "nsCString"
19962 return declType, True, False
19964 if type.isEnum():
19965 return type.unroll().inner.identifier.name, False, True
19967 if type.isCallback() or type.isCallbackInterface():
19968 forceOwningType = optional or isMember
19969 if type.nullable():
19970 if forceOwningType:
19971 declType = "RefPtr<%s>"
19972 else:
19973 declType = "%s*"
19974 else:
19975 if forceOwningType:
19976 declType = "OwningNonNull<%s>"
19977 else:
19978 declType = "%s&"
19979 if type.isCallback():
19980 name = type.unroll().callback.identifier.name
19981 else:
19982 name = type.unroll().inner.identifier.name
19983 return declType % name, False, False
19985 if type.isAny():
19986 # Don't do the rooting stuff for variadics for now
19987 if isMember:
19988 declType = "JS::Value"
19989 else:
19990 declType = "JS::Handle<JS::Value>"
19991 return declType, False, False
19993 if type.isObject():
19994 if isMember:
19995 declType = "JSObject*"
19996 else:
19997 declType = "JS::Handle<JSObject*>"
19998 return declType, False, False
20000 if type.isDictionary():
20001 typeName = CGDictionary.makeDictionaryName(type.inner)
20002 return typeName, True, True
20004 assert type.isPrimitive()
20006 return builtinNames[type.tag()], False, True
20008 def getArgType(self, type, optional, isMember):
20010 Get the type of an argument declaration. Returns the type CGThing, and
20011 whether this should be a const ref.
20013 isMember can be False, "Sequence", or "Variadic"
20015 decl, ref, handleNullable = self.doGetArgType(type, optional, isMember)
20016 decl = CGGeneric(decl)
20017 if handleNullable and type.nullable():
20018 decl = CGTemplatedType("Nullable", decl)
20019 ref = True
20020 if isMember == "Variadic":
20021 arrayType = "Sequence" if self.variadicIsSequence else "nsTArray"
20022 decl = CGTemplatedType(arrayType, decl)
20023 ref = True
20024 elif optional:
20025 # Note: All variadic args claim to be optional, but we can just use
20026 # empty arrays to represent them not being present.
20027 decl = CGTemplatedType("Optional", decl)
20028 ref = True
20029 return (decl, ref)
20031 def getArg(self, arg):
20033 Get the full argument declaration for an argument
20035 decl, ref = self.getArgType(
20036 arg.type, arg.canHaveMissingValue(), "Variadic" if arg.variadic else False
20038 if ref:
20039 decl = CGWrapper(decl, pre="const ", post="&")
20041 return Argument(decl.define(), arg.identifier.name)
20043 def arguments(self):
20044 return self.member.signatures()[0][1]
20047 class CGExampleMethod(CGNativeMember):
20048 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
20049 CGNativeMember.__init__(
20050 self,
20051 descriptor,
20052 method,
20053 CGSpecializedMethod.makeNativeName(descriptor, method),
20054 signature,
20055 descriptor.getExtendedAttributes(method),
20056 breakAfter=breakAfter,
20057 variadicIsSequence=True,
20060 def declare(self, cgClass):
20061 assert self.member.isMethod()
20062 # We skip declaring ourselves if this is a maplike/setlike/iterable
20063 # method, because those get implemented automatically by the binding
20064 # machinery, so the implementor of the interface doesn't have to worry
20065 # about it.
20066 if self.member.isMaplikeOrSetlikeOrIterableMethod():
20067 return ""
20068 return CGNativeMember.declare(self, cgClass)
20070 def define(self, cgClass):
20071 return ""
20074 class CGExampleGetter(CGNativeMember):
20075 def __init__(self, descriptor, attr):
20076 CGNativeMember.__init__(
20077 self,
20078 descriptor,
20079 attr,
20080 CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
20081 (attr.type, []),
20082 descriptor.getExtendedAttributes(attr, getter=True),
20085 def getArgs(self, returnType, argList):
20086 args = CGNativeMember.getArgs(self, returnType, argList)
20087 if self.member.getExtendedAttribute(
20088 "ReflectedHTMLAttributeReturningFrozenArray"
20090 args.insert(0, Argument("bool*", "aUseCachedValue"))
20091 return args
20093 def declare(self, cgClass):
20094 assert self.member.isAttr()
20095 # We skip declaring ourselves if this is a maplike/setlike attr (in
20096 # practice, "size"), because those get implemented automatically by the
20097 # binding machinery, so the implementor of the interface doesn't have to
20098 # worry about it.
20099 if self.member.isMaplikeOrSetlikeAttr():
20100 return ""
20101 return CGNativeMember.declare(self, cgClass)
20103 def define(self, cgClass):
20104 return ""
20107 class CGExampleSetter(CGNativeMember):
20108 def __init__(self, descriptor, attr):
20109 CGNativeMember.__init__(
20110 self,
20111 descriptor,
20112 attr,
20113 CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
20115 BuiltinTypes[IDLBuiltinType.Types.undefined],
20116 [FakeArgument(attr.type)],
20118 descriptor.getExtendedAttributes(attr, setter=True),
20121 def define(self, cgClass):
20122 return ""
20125 class CGBindingImplClass(CGClass):
20127 Common codegen for generating a C++ implementation of a WebIDL interface
20130 def __init__(
20131 self,
20132 descriptor,
20133 cgMethod,
20134 cgGetter,
20135 cgSetter,
20136 wantGetParent=True,
20137 wrapMethodName="WrapObject",
20138 skipStaticMethods=False,
20141 cgMethod, cgGetter and cgSetter are classes used to codegen methods,
20142 getters and setters.
20144 self.descriptor = descriptor
20145 self._deps = descriptor.interface.getDeps()
20147 iface = descriptor.interface
20149 self.methodDecls = []
20151 def appendMethod(m, isConstructor=False):
20152 sigs = m.signatures()
20153 for s in sigs[:-1]:
20154 # Don't put a blank line after overloads, until we
20155 # get to the last one.
20156 self.methodDecls.append(
20157 cgMethod(descriptor, m, s, isConstructor, breakAfter=False)
20159 self.methodDecls.append(cgMethod(descriptor, m, sigs[-1], isConstructor))
20161 if iface.ctor():
20162 appendMethod(iface.ctor(), isConstructor=True)
20163 for n in iface.legacyFactoryFunctions:
20164 appendMethod(n, isConstructor=True)
20165 for m in iface.members:
20166 if m.isMethod():
20167 if m.isIdentifierLess():
20168 continue
20169 if m.isMaplikeOrSetlikeOrIterableMethod():
20170 # Handled by generated code already
20171 continue
20172 if not m.isStatic() or not skipStaticMethods:
20173 appendMethod(m)
20174 elif m.isAttr():
20175 if m.isMaplikeOrSetlikeAttr() or m.type.isObservableArray():
20176 # Handled by generated code already
20177 continue
20178 self.methodDecls.append(cgGetter(descriptor, m))
20179 if not m.readonly:
20180 self.methodDecls.append(cgSetter(descriptor, m))
20182 # Now do the special operations
20183 def appendSpecialOperation(name, op):
20184 if op is None:
20185 return
20186 assert len(op.signatures()) == 1
20187 returnType, args = op.signatures()[0]
20188 # Make a copy of the args, since we plan to modify them.
20189 args = list(args)
20190 if op.isGetter() or op.isDeleter():
20191 # This is a total hack. The '&' belongs with the
20192 # type, not the name! But it works, and is simpler
20193 # than trying to somehow make this pretty.
20194 args.append(
20195 FakeArgument(
20196 BuiltinTypes[IDLBuiltinType.Types.boolean], name="&found"
20199 if name == "Stringifier":
20200 if op.isIdentifierLess():
20201 # XXXbz I wish we were consistent about our renaming here.
20202 name = "Stringify"
20203 else:
20204 # We already added this method
20205 return
20206 if name == "LegacyCaller":
20207 if op.isIdentifierLess():
20208 # XXXbz I wish we were consistent about our renaming here.
20209 name = "LegacyCall"
20210 else:
20211 # We already added this method
20212 return
20213 self.methodDecls.append(
20214 CGNativeMember(
20215 descriptor,
20217 name,
20218 (returnType, args),
20219 descriptor.getExtendedAttributes(op),
20223 # Sort things by name so we get stable ordering in the output.
20224 ops = sorted(descriptor.operations.items(), key=lambda x: x[0])
20225 for name, op in ops:
20226 appendSpecialOperation(name, op)
20227 # If we support indexed properties, then we need a Length()
20228 # method so we know which indices are supported.
20229 if descriptor.supportsIndexedProperties():
20230 # But we don't need it if we already have an infallible
20231 # "length" attribute, which we often do.
20232 haveLengthAttr = any(
20234 for m in iface.members
20235 if m.isAttr()
20236 and CGSpecializedGetterCommon.makeNativeName(descriptor, m) == "Length"
20238 if not haveLengthAttr:
20239 self.methodDecls.append(
20240 CGNativeMember(
20241 descriptor,
20242 FakeMember(),
20243 "Length",
20244 (BuiltinTypes[IDLBuiltinType.Types.unsigned_long], []),
20248 # And if we support named properties we need to be able to
20249 # enumerate the supported names.
20250 if descriptor.supportsNamedProperties():
20251 self.methodDecls.append(
20252 CGNativeMember(
20253 descriptor,
20254 FakeMember(),
20255 "GetSupportedNames",
20257 IDLSequenceType(
20258 None, BuiltinTypes[IDLBuiltinType.Types.domstring]
20266 if descriptor.concrete:
20267 wrapArgs = [
20268 Argument("JSContext*", "aCx"),
20269 Argument("JS::Handle<JSObject*>", "aGivenProto"),
20271 if not descriptor.wrapperCache:
20272 wrapReturnType = "bool"
20273 wrapArgs.append(Argument("JS::MutableHandle<JSObject*>", "aReflector"))
20274 else:
20275 wrapReturnType = "JSObject*"
20276 self.methodDecls.insert(
20278 ClassMethod(
20279 wrapMethodName,
20280 wrapReturnType,
20281 wrapArgs,
20282 virtual=descriptor.wrapperCache,
20283 breakAfterReturnDecl=" ",
20284 override=descriptor.wrapperCache,
20285 body=self.getWrapObjectBody(),
20288 if descriptor.hasCEReactions():
20289 self.methodDecls.insert(
20291 ClassMethod(
20292 "GetDocGroup",
20293 "DocGroup*",
20295 const=True,
20296 breakAfterReturnDecl=" ",
20297 body=self.getGetDocGroupBody(),
20300 if wantGetParent:
20301 self.methodDecls.insert(
20303 ClassMethod(
20304 "GetParentObject",
20305 self.getGetParentObjectReturnType(),
20307 const=True,
20308 breakAfterReturnDecl=" ",
20309 body=self.getGetParentObjectBody(),
20313 # Invoke CGClass.__init__ in any subclasses afterwards to do the actual codegen.
20315 def getWrapObjectBody(self):
20316 return None
20318 def getGetParentObjectReturnType(self):
20319 # The lack of newline before the end of the string is on purpose.
20320 return dedent(
20322 // This should return something that eventually allows finding a
20323 // path to the global this object is associated with. Most simply,
20324 // returning an actual global works.
20325 nsIGlobalObject*"""
20328 def getGetParentObjectBody(self):
20329 return None
20331 def getGetDocGroupBody(self):
20332 return None
20334 def deps(self):
20335 return self._deps
20338 class CGExampleObservableArrayCallback(CGNativeMember):
20339 def __init__(self, descriptor, attr, callbackName):
20340 assert attr.isAttr()
20341 assert attr.type.isObservableArray()
20342 CGNativeMember.__init__(
20343 self,
20344 descriptor,
20345 attr,
20346 self.makeNativeName(attr, callbackName),
20348 BuiltinTypes[IDLBuiltinType.Types.undefined],
20350 FakeArgument(attr.type.inner, "aValue"),
20351 FakeArgument(
20352 BuiltinTypes[IDLBuiltinType.Types.unsigned_long], "aIndex"
20356 ["needsErrorResult"],
20359 def declare(self, cgClass):
20360 assert self.member.isAttr()
20361 assert self.member.type.isObservableArray()
20362 return CGNativeMember.declare(self, cgClass)
20364 def define(self, cgClass):
20365 return ""
20367 @staticmethod
20368 def makeNativeName(attr, callbackName):
20369 assert attr.isAttr()
20370 nativeName = MakeNativeName(attr.identifier.name)
20371 return "On" + callbackName + nativeName
20374 class CGExampleClass(CGBindingImplClass):
20376 Codegen for the actual example class implementation for this descriptor
20379 def __init__(self, descriptor):
20380 CGBindingImplClass.__init__(
20381 self,
20382 descriptor,
20383 CGExampleMethod,
20384 CGExampleGetter,
20385 CGExampleSetter,
20386 wantGetParent=descriptor.wrapperCache,
20389 self.parentIface = descriptor.interface.parent
20390 if self.parentIface:
20391 self.parentDesc = descriptor.getDescriptor(self.parentIface.identifier.name)
20392 bases = [ClassBase(self.nativeLeafName(self.parentDesc))]
20393 else:
20394 bases = [
20395 ClassBase(
20396 "nsISupports /* or NonRefcountedDOMObject if this is a non-refcounted object */"
20399 if descriptor.wrapperCache:
20400 bases.append(
20401 ClassBase(
20402 "nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */"
20406 destructorVisibility = "protected"
20407 if self.parentIface:
20408 extradeclarations = (
20409 "public:\n"
20410 " NS_DECL_ISUPPORTS_INHERITED\n"
20411 " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n"
20412 "\n"
20414 self.nativeLeafName(descriptor),
20415 self.nativeLeafName(self.parentDesc),
20418 else:
20419 extradeclarations = (
20420 "public:\n"
20421 " NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
20422 " NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(%s)\n"
20423 "\n" % self.nativeLeafName(descriptor)
20426 if descriptor.interface.hasChildInterfaces():
20427 decorators = ""
20428 else:
20429 decorators = "final"
20431 for m in descriptor.interface.members:
20432 if m.isAttr() and m.type.isObservableArray():
20433 self.methodDecls.append(
20434 CGExampleObservableArrayCallback(descriptor, m, "Set")
20436 self.methodDecls.append(
20437 CGExampleObservableArrayCallback(descriptor, m, "Delete")
20440 CGClass.__init__(
20441 self,
20442 self.nativeLeafName(descriptor),
20443 bases=bases,
20444 constructors=[ClassConstructor([], visibility="public")],
20445 destructor=ClassDestructor(visibility=destructorVisibility),
20446 methods=self.methodDecls,
20447 decorators=decorators,
20448 extradeclarations=extradeclarations,
20451 def define(self):
20452 # Just override CGClass and do our own thing
20453 nativeType = self.nativeLeafName(self.descriptor)
20455 ctordtor = fill(
20457 ${nativeType}::${nativeType}()
20459 // Add |MOZ_COUNT_CTOR(${nativeType});| for a non-refcounted object.
20462 ${nativeType}::~${nativeType}()
20464 // Add |MOZ_COUNT_DTOR(${nativeType});| for a non-refcounted object.
20466 """,
20467 nativeType=nativeType,
20470 if self.parentIface:
20471 ccImpl = fill(
20474 // Only needed for refcounted objects.
20475 # error "If you don't have members that need cycle collection,
20476 # then remove all the cycle collection bits from this
20477 # implementation and the corresponding header. If you do, you
20478 # want NS_IMPL_CYCLE_COLLECTION_INHERITED(${nativeType},
20479 # ${parentType}, your, members, here)"
20480 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
20481 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
20482 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
20483 NS_INTERFACE_MAP_END_INHERITING(${parentType})
20485 """,
20486 nativeType=nativeType,
20487 parentType=self.nativeLeafName(self.parentDesc),
20489 else:
20490 ccImpl = fill(
20493 // Only needed for refcounted objects.
20494 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType})
20495 NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType})
20496 NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType})
20497 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
20498 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
20499 NS_INTERFACE_MAP_ENTRY(nsISupports)
20500 NS_INTERFACE_MAP_END
20502 """,
20503 nativeType=nativeType,
20506 classImpl = ccImpl + ctordtor + "\n"
20507 if self.descriptor.concrete:
20508 if self.descriptor.wrapperCache:
20509 reflectorArg = ""
20510 reflectorPassArg = ""
20511 returnType = "JSObject*"
20512 else:
20513 reflectorArg = ", JS::MutableHandle<JSObject*> aReflector"
20514 reflectorPassArg = ", aReflector"
20515 returnType = "bool"
20516 classImpl += fill(
20518 ${returnType}
20519 ${nativeType}::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto${reflectorArg})
20521 return ${ifaceName}_Binding::Wrap(aCx, this, aGivenProto${reflectorPassArg});
20524 """,
20525 returnType=returnType,
20526 nativeType=nativeType,
20527 reflectorArg=reflectorArg,
20528 ifaceName=self.descriptor.name,
20529 reflectorPassArg=reflectorPassArg,
20532 return classImpl
20534 @staticmethod
20535 def nativeLeafName(descriptor):
20536 return descriptor.nativeType.split("::")[-1]
20539 class CGExampleRoot(CGThing):
20541 Root codegen class for example implementation generation. Instantiate the
20542 class and call declare or define to generate header or cpp code,
20543 respectively.
20546 def __init__(self, config, interfaceName):
20547 descriptor = config.getDescriptor(interfaceName)
20549 self.root = CGWrapper(CGExampleClass(descriptor), pre="\n", post="\n")
20551 self.root = CGNamespace.build(["mozilla", "dom"], self.root)
20553 builder = ForwardDeclarationBuilder()
20554 if descriptor.hasCEReactions():
20555 builder.addInMozillaDom("DocGroup")
20556 for member in descriptor.interface.members:
20557 if not member.isAttr() and not member.isMethod():
20558 continue
20559 if member.isStatic():
20560 builder.addInMozillaDom("GlobalObject")
20561 if member.isAttr():
20562 if not member.isMaplikeOrSetlikeAttr():
20563 builder.forwardDeclareForType(member.type, config)
20564 else:
20565 assert member.isMethod()
20566 if not member.isMaplikeOrSetlikeOrIterableMethod():
20567 for sig in member.signatures():
20568 builder.forwardDeclareForType(sig[0], config)
20569 for arg in sig[1]:
20570 builder.forwardDeclareForType(arg.type, config)
20572 self.root = CGList([builder.build(), self.root], "\n")
20574 # Throw in our #includes
20575 self.root = CGHeaders(
20581 "nsWrapperCache.h",
20582 "nsCycleCollectionParticipant.h",
20583 "mozilla/Attributes.h",
20584 "mozilla/ErrorResult.h",
20585 "mozilla/dom/BindingDeclarations.h",
20586 "js/TypeDecls.h",
20589 "mozilla/dom/%s.h" % interfaceName,
20591 "mozilla/dom/%s"
20592 % CGHeaders.getDeclarationFilename(descriptor.interface)
20596 self.root,
20599 # And now some include guards
20600 self.root = CGIncludeGuard(interfaceName, self.root)
20602 # And our license block comes before everything else
20603 self.root = CGWrapper(
20604 self.root,
20605 pre=dedent(
20607 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
20608 /* vim:set ts=2 sw=2 sts=2 et cindent: */
20609 /* This Source Code Form is subject to the terms of the Mozilla Public
20610 * License, v. 2.0. If a copy of the MPL was not distributed with this
20611 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
20617 def declare(self):
20618 return self.root.declare()
20620 def define(self):
20621 return self.root.define()
20624 def jsImplName(name):
20625 return name + "JSImpl"
20628 class CGJSImplMember(CGNativeMember):
20630 Base class for generating code for the members of the implementation class
20631 for a JS-implemented WebIDL interface.
20634 def __init__(
20635 self,
20636 descriptorProvider,
20637 member,
20638 name,
20639 signature,
20640 extendedAttrs,
20641 breakAfter=True,
20642 passJSBitsAsNeeded=True,
20643 visibility="public",
20644 variadicIsSequence=False,
20645 virtual=False,
20646 override=False,
20648 CGNativeMember.__init__(
20649 self,
20650 descriptorProvider,
20651 member,
20652 name,
20653 signature,
20654 extendedAttrs,
20655 breakAfter=breakAfter,
20656 passJSBitsAsNeeded=passJSBitsAsNeeded,
20657 visibility=visibility,
20658 variadicIsSequence=variadicIsSequence,
20659 virtual=virtual,
20660 override=override,
20662 self.body = self.getImpl()
20664 def getArgs(self, returnType, argList):
20665 args = CGNativeMember.getArgs(self, returnType, argList)
20666 args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
20667 return args
20670 class CGJSImplMethod(CGJSImplMember):
20672 Class for generating code for the methods for a JS-implemented WebIDL
20673 interface.
20676 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
20677 self.signature = signature
20678 self.descriptor = descriptor
20679 self.isConstructor = isConstructor
20680 CGJSImplMember.__init__(
20681 self,
20682 descriptor,
20683 method,
20684 CGSpecializedMethod.makeNativeName(descriptor, method),
20685 signature,
20686 descriptor.getExtendedAttributes(method),
20687 breakAfter=breakAfter,
20688 variadicIsSequence=True,
20689 passJSBitsAsNeeded=False,
20692 def getArgs(self, returnType, argList):
20693 if self.isConstructor:
20694 # Skip the JS::Compartment bits for constructors; it's handled
20695 # manually in getImpl. But we do need our aGivenProto argument. We
20696 # allow it to be omitted if the default proto is desired.
20697 return CGNativeMember.getArgs(self, returnType, argList) + [
20698 Argument("JS::Handle<JSObject*>", "aGivenProto", "nullptr")
20700 return CGJSImplMember.getArgs(self, returnType, argList)
20702 def getImpl(self):
20703 args = self.getArgs(self.signature[0], self.signature[1])
20704 if not self.isConstructor:
20705 return "return mImpl->%s(%s);\n" % (
20706 self.name,
20707 ", ".join(arg.name for arg in args),
20710 assert self.descriptor.interface.isJSImplemented()
20711 if self.name != "Constructor":
20712 raise TypeError(
20713 "Legacy factory functions are not supported for JS implemented WebIDL."
20715 if len(self.signature[1]) != 0:
20716 # The first two arguments to the constructor implementation are not
20717 # arguments to the WebIDL constructor, so don't pass them to
20718 # __Init(). The last argument is the prototype we're supposed to
20719 # use, and shouldn't get passed to __Init() either.
20720 assert args[0].argType == "const GlobalObject&"
20721 assert args[1].argType == "JSContext*"
20722 assert args[-1].argType == "JS::Handle<JSObject*>"
20723 assert args[-1].name == "aGivenProto"
20724 constructorArgs = [arg.name for arg in args[2:-1]]
20725 constructorArgs.append("js::GetNonCCWObjectRealm(scopeObj)")
20726 initCall = fill(
20728 // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
20729 JS::Rooted<JSObject*> scopeObj(cx, global.Get());
20730 MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx));
20731 JS::Rooted<JS::Value> wrappedVal(cx);
20732 if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal, aGivenProto)) {
20733 MOZ_ASSERT(JS_IsExceptionPending(cx));
20734 aRv.Throw(NS_ERROR_UNEXPECTED);
20735 return nullptr;
20737 // Initialize the object with the constructor arguments.
20738 impl->mImpl->__Init(${args});
20739 if (aRv.Failed()) {
20740 return nullptr;
20742 """,
20743 args=", ".join(constructorArgs),
20745 else:
20746 initCall = ""
20747 return fill(
20749 RefPtr<${implClass}> impl =
20750 ConstructJSImplementation<${implClass}>("${contractId}", global, aRv);
20751 if (aRv.Failed()) {
20752 return nullptr;
20754 $*{initCall}
20755 return impl.forget();
20756 """,
20757 contractId=self.descriptor.interface.getJSImplementation(),
20758 implClass=self.descriptor.name,
20759 initCall=initCall,
20763 # We're always fallible
20764 def callbackGetterName(attr, descriptor):
20765 return "Get" + MakeNativeName(
20766 descriptor.binaryNameFor(attr.identifier.name, attr.isStatic())
20770 def callbackSetterName(attr, descriptor):
20771 return "Set" + MakeNativeName(
20772 descriptor.binaryNameFor(attr.identifier.name, attr.isStatic())
20776 class CGJSImplGetter(CGJSImplMember):
20778 Class for generating code for the getters of attributes for a JS-implemented
20779 WebIDL interface.
20782 def __init__(self, descriptor, attr):
20783 CGJSImplMember.__init__(
20784 self,
20785 descriptor,
20786 attr,
20787 CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
20788 (attr.type, []),
20789 descriptor.getExtendedAttributes(attr, getter=True),
20790 passJSBitsAsNeeded=False,
20793 def getImpl(self):
20794 callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])]
20795 return "return mImpl->%s(%s);\n" % (
20796 callbackGetterName(self.member, self.descriptorProvider),
20797 ", ".join(callbackArgs),
20801 class CGJSImplSetter(CGJSImplMember):
20803 Class for generating code for the setters of attributes for a JS-implemented
20804 WebIDL interface.
20807 def __init__(self, descriptor, attr):
20808 CGJSImplMember.__init__(
20809 self,
20810 descriptor,
20811 attr,
20812 CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
20814 BuiltinTypes[IDLBuiltinType.Types.undefined],
20815 [FakeArgument(attr.type)],
20817 descriptor.getExtendedAttributes(attr, setter=True),
20818 passJSBitsAsNeeded=False,
20821 def getImpl(self):
20822 callbackArgs = [
20823 arg.name
20824 for arg in self.getArgs(
20825 BuiltinTypes[IDLBuiltinType.Types.undefined],
20826 [FakeArgument(self.member.type)],
20829 return "mImpl->%s(%s);\n" % (
20830 callbackSetterName(self.member, self.descriptorProvider),
20831 ", ".join(callbackArgs),
20835 class CGJSImplClass(CGBindingImplClass):
20836 def __init__(self, descriptor):
20837 CGBindingImplClass.__init__(
20838 self,
20839 descriptor,
20840 CGJSImplMethod,
20841 CGJSImplGetter,
20842 CGJSImplSetter,
20843 skipStaticMethods=True,
20846 if descriptor.interface.parent:
20847 parentClass = descriptor.getDescriptor(
20848 descriptor.interface.parent.identifier.name
20849 ).jsImplParent
20850 baseClasses = [ClassBase(parentClass)]
20851 isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n"
20852 ccDecl = "NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" % (
20853 descriptor.name,
20854 parentClass,
20856 extradefinitions = fill(
20858 NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent)
20859 NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass})
20860 NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass})
20861 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
20862 NS_INTERFACE_MAP_END_INHERITING(${parentClass})
20863 """,
20864 ifaceName=self.descriptor.name,
20865 parentClass=parentClass,
20867 else:
20868 baseClasses = [
20869 ClassBase("nsSupportsWeakReference"),
20870 ClassBase("nsWrapperCache"),
20872 isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
20873 ccDecl = (
20874 "NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(%s)\n" % descriptor.name
20876 extradefinitions = fill(
20878 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(${ifaceName})
20879 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(${ifaceName})
20880 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl)
20881 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
20882 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
20883 tmp->ClearWeakReferences();
20884 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
20885 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(${ifaceName})
20886 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl)
20887 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
20888 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
20889 NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName})
20890 NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName})
20891 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
20892 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
20893 NS_INTERFACE_MAP_ENTRY(nsISupports)
20894 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
20895 NS_INTERFACE_MAP_END
20896 """,
20897 ifaceName=self.descriptor.name,
20900 extradeclarations = fill(
20902 public:
20903 $*{isupportsDecl}
20904 $*{ccDecl}
20906 private:
20907 RefPtr<${jsImplName}> mImpl;
20908 nsCOMPtr<nsIGlobalObject> mParent;
20910 """,
20911 isupportsDecl=isupportsDecl,
20912 ccDecl=ccDecl,
20913 jsImplName=jsImplName(descriptor.name),
20916 if descriptor.interface.getExtendedAttribute("WantsEventListenerHooks"):
20917 # No need to do too much sanity checking here; the
20918 # generated code will fail to compile if the methods we
20919 # try to overrid aren't on a superclass.
20920 self.methodDecls.extend(
20921 self.getEventHookMethod(parentClass, "EventListenerAdded")
20923 self.methodDecls.extend(
20924 self.getEventHookMethod(parentClass, "EventListenerRemoved")
20927 if descriptor.interface.hasChildInterfaces():
20928 decorators = ""
20929 # We need a protected virtual destructor our subclasses can use
20930 destructor = ClassDestructor(virtual=True, visibility="protected")
20931 else:
20932 decorators = "final"
20933 destructor = ClassDestructor(virtual=False, visibility="private")
20935 baseConstructors = [
20937 "mImpl(new %s(nullptr, aJSImplObject, aJSImplGlobal, /* aIncumbentGlobal = */ nullptr))"
20938 % jsImplName(descriptor.name)
20940 "mParent(aParent)",
20942 parentInterface = descriptor.interface.parent
20943 while parentInterface:
20944 if parentInterface.isJSImplemented():
20945 baseConstructors.insert(
20946 0, "%s(aJSImplObject, aJSImplGlobal, aParent)" % parentClass
20948 break
20949 parentInterface = parentInterface.parent
20950 if not parentInterface and descriptor.interface.parent:
20951 # We only have C++ ancestors, so only pass along the window
20952 baseConstructors.insert(0, "%s(aParent)" % parentClass)
20954 constructor = ClassConstructor(
20956 Argument("JS::Handle<JSObject*>", "aJSImplObject"),
20957 Argument("JS::Handle<JSObject*>", "aJSImplGlobal"),
20958 Argument("nsIGlobalObject*", "aParent"),
20960 visibility="public",
20961 baseConstructors=baseConstructors,
20964 self.methodDecls.append(
20965 ClassMethod(
20966 "_Create",
20967 "bool",
20968 JSNativeArguments(),
20969 static=True,
20970 body=self.getCreateFromExistingBody(),
20974 CGClass.__init__(
20975 self,
20976 descriptor.name,
20977 bases=baseClasses,
20978 constructors=[constructor],
20979 destructor=destructor,
20980 methods=self.methodDecls,
20981 decorators=decorators,
20982 extradeclarations=extradeclarations,
20983 extradefinitions=extradefinitions,
20986 def getWrapObjectBody(self):
20987 return fill(
20989 JS::Rooted<JSObject*> obj(aCx, ${name}_Binding::Wrap(aCx, this, aGivenProto));
20990 if (!obj) {
20991 return nullptr;
20994 // Now define it on our chrome object
20995 JSAutoRealm ar(aCx, mImpl->CallbackGlobalOrNull());
20996 if (!JS_WrapObject(aCx, &obj)) {
20997 return nullptr;
20999 JS::Rooted<JSObject*> callback(aCx, mImpl->CallbackOrNull());
21000 if (!JS_DefineProperty(aCx, callback, "__DOM_IMPL__", obj, 0)) {
21001 return nullptr;
21003 return obj;
21004 """,
21005 name=self.descriptor.name,
21008 def getGetParentObjectReturnType(self):
21009 return "nsISupports*"
21011 def getGetParentObjectBody(self):
21012 return "return mParent;\n"
21014 def getGetDocGroupBody(self):
21015 return dedent(
21017 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent);
21018 if (!window) {
21019 return nullptr;
21021 return window->GetDocGroup();
21025 def getCreateFromExistingBody(self):
21026 # XXXbz we could try to get parts of this (e.g. the argument
21027 # conversions) auto-generated by somehow creating an IDLMethod and
21028 # adding it to our interface, but we'd still need to special-case the
21029 # implementation slightly to have it not try to forward to the JS
21030 # object...
21031 return fill(
21033 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
21034 if (!args.requireAtLeast(cx, "${ifaceName}._create", 2)) {
21035 return false;
21037 BindingCallContext callCx(cx, "${ifaceName}._create");
21038 if (!args[0].isObject()) {
21039 return callCx.ThrowErrorMessage<MSG_NOT_OBJECT>("Argument 1");
21041 if (!args[1].isObject()) {
21042 return callCx.ThrowErrorMessage<MSG_NOT_OBJECT>("Argument 2");
21045 // GlobalObject will go through wrappers as needed for us, and
21046 // is simpler than the right UnwrapArg incantation.
21047 GlobalObject global(cx, &args[0].toObject());
21048 if (global.Failed()) {
21049 return false;
21051 nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(global.GetAsSupports());
21052 MOZ_ASSERT(globalHolder);
21053 JS::Rooted<JSObject*> arg(cx, &args[1].toObject());
21054 JS::Rooted<JSObject*> argGlobal(cx, JS::CurrentGlobalOrNull(cx));
21055 RefPtr<${implName}> impl = new ${implName}(arg, argGlobal, globalHolder);
21056 MOZ_ASSERT(js::IsObjectInContextCompartment(arg, cx));
21057 return GetOrCreateDOMReflector(cx, impl, args.rval());
21058 """,
21059 ifaceName=self.descriptor.interface.identifier.name,
21060 implName=self.descriptor.name,
21063 def getEventHookMethod(self, parentClass, methodName):
21064 body = fill(
21066 ${parentClass}::${methodName}(aType);
21067 mImpl->${methodName}(Substring(nsDependentAtomString(aType), 2), IgnoreErrors());
21068 """,
21069 parentClass=parentClass,
21070 methodName=methodName,
21072 return [
21073 ClassMethod(
21074 methodName,
21075 "void",
21076 [Argument("nsAtom*", "aType")],
21077 virtual=True,
21078 override=True,
21079 body=body,
21081 ClassUsingFromBaseDeclaration(parentClass, methodName),
21085 def isJSImplementedDescriptor(descriptorProvider):
21086 return (
21087 isinstance(descriptorProvider, Descriptor)
21088 and descriptorProvider.interface.isJSImplemented()
21092 class CGCallback(CGClass):
21093 def __init__(
21094 self, idlObject, descriptorProvider, baseName, methods, getters=[], setters=[]
21096 self.baseName = baseName
21097 self._deps = idlObject.getDeps()
21098 self.idlObject = idlObject
21099 self.name = idlObject.identifier.name
21100 if isJSImplementedDescriptor(descriptorProvider):
21101 self.name = jsImplName(self.name)
21102 # For our public methods that needThisHandling we want most of the
21103 # same args and the same return type as what CallbackMember
21104 # generates. So we want to take advantage of all its
21105 # CGNativeMember infrastructure, but that infrastructure can't deal
21106 # with templates and most especially template arguments. So just
21107 # cheat and have CallbackMember compute all those things for us.
21108 realMethods = []
21109 for method in methods:
21110 if not isinstance(method, CallbackMember) or not method.needThisHandling:
21111 realMethods.append(method)
21112 else:
21113 realMethods.extend(self.getMethodImpls(method))
21114 realMethods.append(
21115 ClassMethod(
21116 "operator==",
21117 "bool",
21118 [Argument("const %s&" % self.name, "aOther")],
21119 inline=True,
21120 bodyInHeader=True,
21121 const=True,
21122 body=("return %s::operator==(aOther);\n" % baseName),
21125 CGClass.__init__(
21126 self,
21127 self.name,
21128 bases=[ClassBase(baseName)],
21129 constructors=self.getConstructors(),
21130 methods=realMethods + getters + setters,
21133 def getConstructors(self):
21134 if (
21135 not self.idlObject.isInterface()
21136 and not self.idlObject._treatNonObjectAsNull
21138 body = "MOZ_ASSERT(JS::IsCallable(mCallback));\n"
21139 else:
21140 # Not much we can assert about it, other than not being null, and
21141 # CallbackObject does that already.
21142 body = ""
21143 return [
21144 ClassConstructor(
21146 Argument("JSContext*", "aCx"),
21147 Argument("JS::Handle<JSObject*>", "aCallback"),
21148 Argument("JS::Handle<JSObject*>", "aCallbackGlobal"),
21149 Argument("nsIGlobalObject*", "aIncumbentGlobal"),
21151 bodyInHeader=True,
21152 visibility="public",
21153 explicit=True,
21154 baseConstructors=[
21155 "%s(aCx, aCallback, aCallbackGlobal, aIncumbentGlobal)"
21156 % self.baseName,
21158 body=body,
21160 ClassConstructor(
21162 Argument("JSObject*", "aCallback"),
21163 Argument("JSObject*", "aCallbackGlobal"),
21164 Argument("const FastCallbackConstructor&", ""),
21166 bodyInHeader=True,
21167 visibility="public",
21168 explicit=True,
21169 baseConstructors=[
21170 "%s(aCallback, aCallbackGlobal, FastCallbackConstructor())"
21171 % self.baseName,
21173 body=body,
21175 ClassConstructor(
21177 Argument("JSObject*", "aCallback"),
21178 Argument("JSObject*", "aCallbackGlobal"),
21179 Argument("JSObject*", "aAsyncStack"),
21180 Argument("nsIGlobalObject*", "aIncumbentGlobal"),
21182 bodyInHeader=True,
21183 visibility="public",
21184 explicit=True,
21185 baseConstructors=[
21186 "%s(aCallback, aCallbackGlobal, aAsyncStack, aIncumbentGlobal)"
21187 % self.baseName,
21189 body=body,
21193 def getMethodImpls(self, method):
21194 assert method.needThisHandling
21195 args = list(method.args)
21196 # Strip out the BindingCallContext&/JSObject* args
21197 # that got added.
21198 assert args[0].name == "cx" and args[0].argType == "BindingCallContext&"
21199 assert args[1].name == "aThisVal" and args[1].argType == "JS::Handle<JS::Value>"
21200 args = args[2:]
21202 # Now remember which index the ErrorResult argument is at;
21203 # we'll need this below.
21204 assert args[-1].name == "aRv" and args[-1].argType == "ErrorResult&"
21205 rvIndex = len(args) - 1
21206 assert rvIndex >= 0
21208 # Record the names of all the arguments, so we can use them when we call
21209 # the private method.
21210 argnames = [arg.name for arg in args]
21211 argnamesWithThis = ["s.GetCallContext()", "thisValJS"] + argnames
21212 argnamesWithoutThis = [
21213 "s.GetCallContext()",
21214 "JS::UndefinedHandleValue",
21215 ] + argnames
21216 # Now that we've recorded the argnames for our call to our private
21217 # method, insert our optional argument for the execution reason.
21218 args.append(Argument("const char*", "aExecutionReason", "nullptr"))
21220 # Make copies of the arg list for the two "without rv" overloads. Note
21221 # that those don't need aExceptionHandling or aRealm arguments because
21222 # those would make not sense anyway: the only sane thing to do with
21223 # exceptions in the "without rv" cases is to report them.
21224 argsWithoutRv = list(args)
21225 argsWithoutRv.pop(rvIndex)
21226 argsWithoutThisAndRv = list(argsWithoutRv)
21228 # Add the potional argument for deciding whether the CallSetup should
21229 # re-throw exceptions on aRv.
21230 args.append(
21231 Argument("ExceptionHandling", "aExceptionHandling", "eReportExceptions")
21233 # And the argument for communicating when exceptions should really be
21234 # rethrown. In particular, even when aExceptionHandling is
21235 # eRethrowExceptions they won't get rethrown if aRealm is provided
21236 # and its principal doesn't subsume either the callback or the
21237 # exception.
21238 args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
21239 # And now insert our template argument.
21240 argsWithoutThis = list(args)
21241 args.insert(0, Argument("const T&", "thisVal"))
21242 argsWithoutRv.insert(0, Argument("const T&", "thisVal"))
21244 argnamesWithoutThisAndRv = [arg.name for arg in argsWithoutThisAndRv]
21245 argnamesWithoutThisAndRv.insert(rvIndex, "IgnoreErrors()")
21246 # If we just leave things like that, and have no actual arguments in the
21247 # IDL, we will end up trying to call the templated "without rv" overload
21248 # with "rv" as the thisVal. That's no good. So explicitly append the
21249 # aExceptionHandling and aRealm values we need to end up matching the
21250 # signature of our non-templated "with rv" overload.
21251 argnamesWithoutThisAndRv.extend(["eReportExceptions", "nullptr"])
21253 argnamesWithoutRv = [arg.name for arg in argsWithoutRv]
21254 # Note that we need to insert at rvIndex + 1, since we inserted a
21255 # thisVal arg at the start.
21256 argnamesWithoutRv.insert(rvIndex + 1, "IgnoreErrors()")
21258 errorReturn = method.getDefaultRetval()
21260 setupCall = fill(
21262 MOZ_ASSERT(!aRv.Failed(), "Don't pass an already-failed ErrorResult to a callback!");
21263 if (!aExecutionReason) {
21264 aExecutionReason = "${executionReason}";
21266 CallSetup s(this, aRv, aExecutionReason, aExceptionHandling, aRealm);
21267 if (!s.GetContext()) {
21268 MOZ_ASSERT(aRv.Failed());
21269 return${errorReturn};
21271 """,
21272 errorReturn=errorReturn,
21273 executionReason=method.getPrettyName(),
21276 bodyWithThis = fill(
21278 $*{setupCall}
21279 JS::Rooted<JS::Value> thisValJS(s.GetContext());
21280 if (!ToJSValue(s.GetContext(), thisVal, &thisValJS)) {
21281 aRv.Throw(NS_ERROR_FAILURE);
21282 return${errorReturn};
21284 return ${methodName}(${callArgs});
21285 """,
21286 setupCall=setupCall,
21287 errorReturn=errorReturn,
21288 methodName=method.name,
21289 callArgs=", ".join(argnamesWithThis),
21291 bodyWithoutThis = fill(
21293 $*{setupCall}
21294 return ${methodName}(${callArgs});
21295 """,
21296 setupCall=setupCall,
21297 errorReturn=errorReturn,
21298 methodName=method.name,
21299 callArgs=", ".join(argnamesWithoutThis),
21301 bodyWithThisWithoutRv = fill(
21303 return ${methodName}(${callArgs});
21304 """,
21305 methodName=method.name,
21306 callArgs=", ".join(argnamesWithoutRv),
21308 bodyWithoutThisAndRv = fill(
21310 return ${methodName}(${callArgs});
21311 """,
21312 methodName=method.name,
21313 callArgs=", ".join(argnamesWithoutThisAndRv),
21316 return [
21317 ClassMethod(
21318 method.name,
21319 method.returnType,
21320 args,
21321 bodyInHeader=True,
21322 templateArgs=["typename T"],
21323 body=bodyWithThis,
21324 canRunScript=method.canRunScript,
21326 ClassMethod(
21327 method.name,
21328 method.returnType,
21329 argsWithoutThis,
21330 bodyInHeader=True,
21331 body=bodyWithoutThis,
21332 canRunScript=method.canRunScript,
21334 ClassMethod(
21335 method.name,
21336 method.returnType,
21337 argsWithoutRv,
21338 bodyInHeader=True,
21339 templateArgs=["typename T"],
21340 body=bodyWithThisWithoutRv,
21341 canRunScript=method.canRunScript,
21343 ClassMethod(
21344 method.name,
21345 method.returnType,
21346 argsWithoutThisAndRv,
21347 bodyInHeader=True,
21348 body=bodyWithoutThisAndRv,
21349 canRunScript=method.canRunScript,
21351 method,
21354 def deps(self):
21355 return self._deps
21358 class CGCallbackFunction(CGCallback):
21359 def __init__(self, callback, descriptorProvider):
21360 self.callback = callback
21361 if callback.isConstructor():
21362 methods = [ConstructCallback(callback, descriptorProvider)]
21363 else:
21364 methods = [CallCallback(callback, descriptorProvider)]
21365 CGCallback.__init__(
21366 self, callback, descriptorProvider, "CallbackFunction", methods
21369 def getConstructors(self):
21370 return CGCallback.getConstructors(self) + [
21371 ClassConstructor(
21372 [Argument("CallbackFunction*", "aOther")],
21373 bodyInHeader=True,
21374 visibility="public",
21375 explicit=True,
21376 baseConstructors=["CallbackFunction(aOther)"],
21381 class CGFastCallback(CGClass):
21382 def __init__(self, idlObject):
21383 self._deps = idlObject.getDeps()
21384 baseName = idlObject.identifier.name
21385 constructor = ClassConstructor(
21387 Argument("JSObject*", "aCallback"),
21388 Argument("JSObject*", "aCallbackGlobal"),
21390 bodyInHeader=True,
21391 visibility="public",
21392 explicit=True,
21393 baseConstructors=[
21394 "%s(aCallback, aCallbackGlobal, FastCallbackConstructor())" % baseName,
21396 body="",
21399 traceMethod = ClassMethod(
21400 "Trace",
21401 "void",
21402 [Argument("JSTracer*", "aTracer")],
21403 inline=True,
21404 bodyInHeader=True,
21405 visibility="public",
21406 body="%s::Trace(aTracer);\n" % baseName,
21408 holdMethod = ClassMethod(
21409 "FinishSlowJSInitIfMoreThanOneOwner",
21410 "void",
21411 [Argument("JSContext*", "aCx")],
21412 inline=True,
21413 bodyInHeader=True,
21414 visibility="public",
21415 body=("%s::FinishSlowJSInitIfMoreThanOneOwner(aCx);\n" % baseName),
21418 CGClass.__init__(
21419 self,
21420 "Fast%s" % baseName,
21421 bases=[ClassBase(baseName)],
21422 constructors=[constructor],
21423 methods=[traceMethod, holdMethod],
21426 def deps(self):
21427 return self._deps
21430 class CGCallbackInterface(CGCallback):
21431 def __init__(self, descriptor, spiderMonkeyInterfacesAreStructs=False):
21432 iface = descriptor.interface
21433 attrs = [
21435 for m in iface.members
21436 if (
21437 m.isAttr()
21438 and not m.isStatic()
21439 and (not m.isMaplikeOrSetlikeAttr() or not iface.isJSImplemented())
21442 getters = [
21443 CallbackGetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
21444 for a in attrs
21446 setters = [
21447 CallbackSetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
21448 for a in attrs
21449 if not a.readonly
21451 methods = [
21453 for m in iface.members
21454 if (
21455 m.isMethod()
21456 and not m.isStatic()
21457 and not m.isIdentifierLess()
21458 and (
21459 not m.isMaplikeOrSetlikeOrIterableMethod()
21460 or not iface.isJSImplemented()
21464 methods = [
21465 CallbackOperation(m, sig, descriptor, spiderMonkeyInterfacesAreStructs)
21466 for m in methods
21467 for sig in m.signatures()
21470 needInitId = False
21471 if iface.isJSImplemented() and iface.ctor():
21472 sigs = descriptor.interface.ctor().signatures()
21473 if len(sigs) != 1:
21474 raise TypeError("We only handle one constructor. See bug 869268.")
21475 methods.append(CGJSImplInitOperation(sigs[0], descriptor))
21476 needInitId = True
21478 idlist = [
21479 descriptor.binaryNameFor(m.identifier.name, m.isStatic())
21480 for m in iface.members
21481 if m.isAttr() or m.isMethod()
21483 if needInitId:
21484 idlist.append("__init")
21486 if iface.isJSImplemented() and iface.getExtendedAttribute(
21487 "WantsEventListenerHooks"
21489 methods.append(CGJSImplEventHookOperation(descriptor, "eventListenerAdded"))
21490 methods.append(
21491 CGJSImplEventHookOperation(descriptor, "eventListenerRemoved")
21493 idlist.append("eventListenerAdded")
21494 idlist.append("eventListenerRemoved")
21496 if len(idlist) != 0:
21497 methods.append(initIdsClassMethod(idlist, iface.identifier.name + "Atoms"))
21498 CGCallback.__init__(
21499 self,
21500 iface,
21501 descriptor,
21502 "CallbackInterface",
21503 methods,
21504 getters=getters,
21505 setters=setters,
21509 class FakeMember:
21510 def __init__(self, name=None):
21511 if name is not None:
21512 self.identifier = FakeIdentifier(name)
21514 def isStatic(self):
21515 return False
21517 def isAttr(self):
21518 return False
21520 def isMethod(self):
21521 return False
21523 def getExtendedAttribute(self, name):
21524 # Claim to be a [NewObject] so we can avoid the "return a raw pointer"
21525 # comments CGNativeMember codegen would otherwise stick in.
21526 if name == "NewObject":
21527 return True
21528 return None
21531 class CallbackMember(CGNativeMember):
21532 # XXXbz It's OK to use CallbackKnownNotGray for wrapScope because
21533 # CallSetup already handled the unmark-gray bits for us. we don't have
21534 # anything better to use for 'obj', really...
21535 def __init__(
21536 self,
21537 sig,
21538 name,
21539 descriptorProvider,
21540 needThisHandling,
21541 rethrowContentException=False,
21542 spiderMonkeyInterfacesAreStructs=False,
21543 wrapScope=None,
21544 canRunScript=False,
21545 passJSBitsAsNeeded=False,
21548 needThisHandling is True if we need to be able to accept a specified
21549 thisObj, False otherwise.
21551 assert not rethrowContentException or not needThisHandling
21553 self.retvalType = sig[0]
21554 self.originalSig = sig
21555 args = sig[1]
21556 self.argCount = len(args)
21557 if self.argCount > 0:
21558 # Check for variadic arguments
21559 lastArg = args[self.argCount - 1]
21560 if lastArg.variadic:
21561 self.argCountStr = "(%d - 1) + %s.Length()" % (
21562 self.argCount,
21563 lastArg.identifier.name,
21565 else:
21566 self.argCountStr = "%d" % self.argCount
21567 self.needThisHandling = needThisHandling
21568 # If needThisHandling, we generate ourselves as private and the caller
21569 # will handle generating public versions that handle the "this" stuff.
21570 visibility = "private" if needThisHandling else "public"
21571 self.rethrowContentException = rethrowContentException
21573 self.wrapScope = wrapScope
21574 # We don't care, for callback codegen, whether our original member was
21575 # a method or attribute or whatnot. Just always pass FakeMember()
21576 # here.
21577 CGNativeMember.__init__(
21578 self,
21579 descriptorProvider,
21580 FakeMember(),
21581 name,
21582 (self.retvalType, args),
21583 extendedAttrs=["needsErrorResult"],
21584 passJSBitsAsNeeded=passJSBitsAsNeeded,
21585 visibility=visibility,
21586 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
21587 canRunScript=canRunScript,
21589 # We have to do all the generation of our body now, because
21590 # the caller relies on us throwing if we can't manage it.
21591 self.body = self.getImpl()
21593 def getImpl(self):
21594 setupCall = self.getCallSetup()
21595 declRval = self.getRvalDecl()
21596 if self.argCount > 0:
21597 argvDecl = fill(
21599 JS::RootedVector<JS::Value> argv(cx);
21600 if (!argv.resize(${argCount})) {
21601 $*{failureCode}
21602 return${errorReturn};
21604 """,
21605 argCount=self.argCountStr,
21606 failureCode=self.getArgvDeclFailureCode(),
21607 errorReturn=self.getDefaultRetval(),
21609 else:
21610 # Avoid weird 0-sized arrays
21611 argvDecl = ""
21612 convertArgs = self.getArgConversions()
21613 doCall = self.getCall()
21614 returnResult = self.getResultConversion()
21616 body = declRval + argvDecl + convertArgs + doCall
21617 if self.needsScopeBody():
21618 body = "{\n" + indent(body) + "}\n"
21619 return setupCall + body + returnResult
21621 def needsScopeBody(self):
21622 return False
21624 def getArgvDeclFailureCode(self):
21625 return dedent(
21627 // That threw an exception on the JSContext, and our CallSetup will do
21628 // the right thing with that.
21632 def getExceptionCode(self, forResult):
21633 return fill(
21635 aRv.Throw(NS_ERROR_UNEXPECTED);
21636 return${defaultRetval};
21637 """,
21638 defaultRetval=self.getDefaultRetval(),
21641 def getResultConversion(
21642 self, val="rval", failureCode=None, isDefinitelyObject=False, exceptionCode=None
21644 replacements = {
21645 "val": val,
21646 "holderName": "rvalHolder",
21647 "declName": "rvalDecl",
21648 # We actually want to pass in a null scope object here, because
21649 # wrapping things into our current compartment (that of mCallback)
21650 # is what we want.
21651 "obj": "nullptr",
21652 "passedToJSImpl": "false",
21655 if isJSImplementedDescriptor(self.descriptorProvider):
21656 isCallbackReturnValue = "JSImpl"
21657 else:
21658 isCallbackReturnValue = "Callback"
21659 sourceDescription = "return value of %s" % self.getPrettyName()
21660 convertType = instantiateJSToNativeConversion(
21661 getJSToNativeConversionInfo(
21662 self.retvalType,
21663 self.descriptorProvider,
21664 failureCode=failureCode,
21665 isDefinitelyObject=isDefinitelyObject,
21666 exceptionCode=exceptionCode or self.getExceptionCode(forResult=True),
21667 isCallbackReturnValue=isCallbackReturnValue,
21668 # Allow returning a callback type that
21669 # allows non-callable objects.
21670 allowTreatNonCallableAsNull=True,
21671 sourceDescription=sourceDescription,
21673 replacements,
21675 assignRetval = string.Template(
21676 self.getRetvalInfo(self.retvalType, False)[2]
21677 ).substitute(replacements)
21678 type = convertType.define()
21679 return type + assignRetval
21681 def getArgConversions(self):
21682 # Just reget the arglist from self.originalSig, because our superclasses
21683 # just have way to many members they like to clobber, so I can't find a
21684 # safe member name to store it in.
21685 argConversions = [
21686 self.getArgConversion(i, arg) for i, arg in enumerate(self.originalSig[1])
21688 if not argConversions:
21689 return "\n"
21691 # Do them back to front, so our argc modifications will work
21692 # correctly, because we examine trailing arguments first.
21693 argConversions.reverse()
21694 # Wrap each one in a scope so that any locals it has don't leak out, and
21695 # also so that we can just "break;" for our successCode.
21696 argConversions = [
21697 CGWrapper(CGIndenter(CGGeneric(c)), pre="do {\n", post="} while (false);\n")
21698 for c in argConversions
21700 if self.argCount > 0:
21701 argConversions.insert(0, self.getArgcDecl())
21702 # And slap them together.
21703 return CGList(argConversions, "\n").define() + "\n"
21705 def getArgConversion(self, i, arg):
21706 argval = arg.identifier.name
21708 if arg.variadic:
21709 argval = argval + "[idx]"
21710 jsvalIndex = "%d + idx" % i
21711 else:
21712 jsvalIndex = "%d" % i
21713 if arg.canHaveMissingValue():
21714 argval += ".Value()"
21716 prepend = ""
21718 wrapScope = self.wrapScope
21719 if arg.type.isUnion() and wrapScope is None:
21720 prepend += (
21721 "JS::Rooted<JSObject*> callbackObj(cx, CallbackKnownNotGray());\n"
21723 wrapScope = "callbackObj"
21725 conversion = prepend + wrapForType(
21726 arg.type,
21727 self.descriptorProvider,
21729 "result": argval,
21730 "successCode": "continue;\n" if arg.variadic else "break;\n",
21731 "jsvalRef": "argv[%s]" % jsvalIndex,
21732 "jsvalHandle": "argv[%s]" % jsvalIndex,
21733 "obj": wrapScope,
21734 "returnsNewObject": False,
21735 "exceptionCode": self.getExceptionCode(forResult=False),
21736 "spiderMonkeyInterfacesAreStructs": self.spiderMonkeyInterfacesAreStructs,
21740 if arg.variadic:
21741 conversion = fill(
21743 for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) {
21744 $*{conversion}
21746 break;
21747 """,
21748 arg=arg.identifier.name,
21749 conversion=conversion,
21751 elif arg.canHaveMissingValue():
21752 conversion = fill(
21754 if (${argName}.WasPassed()) {
21755 $*{conversion}
21756 } else if (argc == ${iPlus1}) {
21757 // This is our current trailing argument; reduce argc
21758 --argc;
21759 } else {
21760 argv[${i}].setUndefined();
21762 """,
21763 argName=arg.identifier.name,
21764 conversion=conversion,
21765 iPlus1=i + 1,
21766 i=i,
21768 return conversion
21770 def getDefaultRetval(self):
21771 default = self.getRetvalInfo(self.retvalType, False)[1]
21772 if len(default) != 0:
21773 default = " " + default
21774 return default
21776 def getArgs(self, returnType, argList):
21777 args = CGNativeMember.getArgs(self, returnType, argList)
21778 if not self.needThisHandling:
21779 # Since we don't need this handling, we're the actual method that
21780 # will be called, so we need an aRethrowExceptions argument.
21781 if not self.rethrowContentException:
21782 args.append(Argument("const char*", "aExecutionReason", "nullptr"))
21783 args.append(
21784 Argument(
21785 "ExceptionHandling", "aExceptionHandling", "eReportExceptions"
21788 args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
21789 return args
21790 # We want to allow the caller to pass in a "this" value, as
21791 # well as a BindingCallContext.
21792 return [
21793 Argument("BindingCallContext&", "cx"),
21794 Argument("JS::Handle<JS::Value>", "aThisVal"),
21795 ] + args
21797 def getCallSetup(self):
21798 if self.needThisHandling:
21799 # It's been done for us already
21800 return ""
21801 callSetup = "CallSetup s(this, aRv"
21802 if self.rethrowContentException:
21803 # getArgs doesn't add the aExceptionHandling argument but does add
21804 # aRealm for us.
21805 callSetup += (
21806 ', "%s", eRethrowContentExceptions, aRealm, /* aIsJSImplementedWebIDL = */ '
21807 % self.getPrettyName()
21809 callSetup += toStringBool(
21810 isJSImplementedDescriptor(self.descriptorProvider)
21812 else:
21813 callSetup += ', "%s", aExceptionHandling, aRealm' % self.getPrettyName()
21814 callSetup += ");\n"
21815 return fill(
21817 $*{callSetup}
21818 if (aRv.Failed()) {
21819 return${errorReturn};
21821 MOZ_ASSERT(s.GetContext());
21822 BindingCallContext& cx = s.GetCallContext();
21824 """,
21825 callSetup=callSetup,
21826 errorReturn=self.getDefaultRetval(),
21829 def getArgcDecl(self):
21830 return CGGeneric("unsigned argc = %s;\n" % self.argCountStr)
21832 @staticmethod
21833 def ensureASCIIName(idlObject):
21834 type = "attribute" if idlObject.isAttr() else "operation"
21835 if re.match("[^\x20-\x7E]", idlObject.identifier.name):
21836 raise SyntaxError(
21837 'Callback %s name "%s" contains non-ASCII '
21838 "characters. We can't handle that. %s"
21839 % (type, idlObject.identifier.name, idlObject.location)
21841 if re.match('"', idlObject.identifier.name):
21842 raise SyntaxError(
21843 "Callback %s name '%s' contains "
21844 "double-quote character. We can't handle "
21845 "that. %s" % (type, idlObject.identifier.name, idlObject.location)
21849 class ConstructCallback(CallbackMember):
21850 def __init__(self, callback, descriptorProvider):
21851 self.callback = callback
21852 CallbackMember.__init__(
21853 self,
21854 callback.signatures()[0],
21855 "Construct",
21856 descriptorProvider,
21857 needThisHandling=False,
21858 canRunScript=True,
21861 def getRvalDecl(self):
21862 # Box constructedObj for getJSToNativeConversionInfo().
21863 return "JS::Rooted<JS::Value> rval(cx);\n"
21865 def getCall(self):
21866 if self.argCount > 0:
21867 args = "JS::HandleValueArray::subarray(argv, 0, argc)"
21868 else:
21869 args = "JS::HandleValueArray::empty()"
21871 return fill(
21873 JS::Rooted<JS::Value> constructor(cx, JS::ObjectValue(*mCallback));
21874 JS::Rooted<JSObject*> constructedObj(cx);
21875 if (!JS::Construct(cx, constructor,
21876 ${args}, &constructedObj)) {
21877 aRv.NoteJSContextException(cx);
21878 return${errorReturn};
21880 rval.setObject(*constructedObj);
21881 """,
21882 args=args,
21883 errorReturn=self.getDefaultRetval(),
21886 def getResultConversion(self):
21887 return CallbackMember.getResultConversion(self, isDefinitelyObject=True)
21889 def getPrettyName(self):
21890 return self.callback.identifier.name
21893 class CallbackMethod(CallbackMember):
21894 def __init__(
21895 self,
21896 sig,
21897 name,
21898 descriptorProvider,
21899 needThisHandling,
21900 rethrowContentException=False,
21901 spiderMonkeyInterfacesAreStructs=False,
21902 canRunScript=False,
21904 CallbackMember.__init__(
21905 self,
21906 sig,
21907 name,
21908 descriptorProvider,
21909 needThisHandling,
21910 rethrowContentException,
21911 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
21912 canRunScript=canRunScript,
21915 def getRvalDecl(self):
21916 return "JS::Rooted<JS::Value> rval(cx);\n"
21918 def getNoteCallFailed(self):
21919 return fill(
21921 aRv.NoteJSContextException(cx);
21922 return${errorReturn};
21923 """,
21924 errorReturn=self.getDefaultRetval(),
21927 def getCall(self):
21928 if self.argCount > 0:
21929 args = "JS::HandleValueArray::subarray(argv, 0, argc)"
21930 else:
21931 args = "JS::HandleValueArray::empty()"
21933 return fill(
21935 $*{declCallable}
21936 $*{declThis}
21937 if (${callGuard}!JS::Call(cx, ${thisVal}, callable,
21938 ${args}, &rval)) {
21939 $*{noteError}
21941 """,
21942 declCallable=self.getCallableDecl(),
21943 declThis=self.getThisDecl(),
21944 callGuard=self.getCallGuard(),
21945 thisVal=self.getThisVal(),
21946 args=args,
21947 noteError=self.getNoteCallFailed(),
21951 class CallCallback(CallbackMethod):
21952 def __init__(self, callback, descriptorProvider):
21953 self.callback = callback
21954 CallbackMethod.__init__(
21955 self,
21956 callback.signatures()[0],
21957 "Call",
21958 descriptorProvider,
21959 needThisHandling=True,
21960 canRunScript=not callback.isRunScriptBoundary(),
21963 def getNoteCallFailed(self):
21964 if self.retvalType.isPromise():
21965 return dedent(
21967 // Convert exception to a rejected promise.
21968 // See https://heycam.github.io/webidl/#call-a-user-objects-operation
21969 // step 12 and step 15.5.
21970 return CreateRejectedPromiseFromThrownException(cx, aRv);
21973 return CallbackMethod.getNoteCallFailed(self)
21975 def getExceptionCode(self, forResult):
21976 # If the result value is a promise, and conversion
21977 # to the promise throws an exception we shouldn't
21978 # try to convert that exception to a promise again.
21979 if self.retvalType.isPromise() and not forResult:
21980 return dedent(
21982 // Convert exception to a rejected promise.
21983 // See https://heycam.github.io/webidl/#call-a-user-objects-operation
21984 // step 10 and step 15.5.
21985 return CreateRejectedPromiseFromThrownException(cx, aRv);
21988 return CallbackMethod.getExceptionCode(self, forResult)
21990 def getThisDecl(self):
21991 return ""
21993 def getThisVal(self):
21994 return "aThisVal"
21996 def getCallableDecl(self):
21997 return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n"
21999 def getPrettyName(self):
22000 return self.callback.identifier.name
22002 def getCallGuard(self):
22003 if self.callback._treatNonObjectAsNull:
22004 return "JS::IsCallable(mCallback) && "
22005 return ""
22008 class CallbackOperationBase(CallbackMethod):
22010 Common class for implementing various callback operations.
22013 def __init__(
22014 self,
22015 signature,
22016 jsName,
22017 nativeName,
22018 descriptor,
22019 singleOperation,
22020 rethrowContentException=False,
22021 spiderMonkeyInterfacesAreStructs=False,
22023 self.singleOperation = singleOperation
22024 self.methodName = descriptor.binaryNameFor(jsName, False)
22025 CallbackMethod.__init__(
22026 self,
22027 signature,
22028 nativeName,
22029 descriptor,
22030 singleOperation,
22031 rethrowContentException,
22032 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
22035 def getThisDecl(self):
22036 if not self.singleOperation:
22037 return "JS::Rooted<JS::Value> thisValue(cx, JS::ObjectValue(*mCallback));\n"
22038 # This relies on getCallableDecl declaring a boolean
22039 # isCallable in the case when we're a single-operation
22040 # interface.
22041 return dedent(
22043 JS::Rooted<JS::Value> thisValue(cx, isCallable ? aThisVal.get()
22044 : JS::ObjectValue(*mCallback));
22048 def getThisVal(self):
22049 return "thisValue"
22051 def getCallableDecl(self):
22052 getCallableFromProp = fill(
22054 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
22055 if ((reinterpret_cast<jsid*>(atomsCache)->isVoid() &&
22056 !InitIds(cx, atomsCache)) ||
22057 !GetCallableProperty(cx, atomsCache->${methodAtomName}, &callable)) {
22058 aRv.Throw(NS_ERROR_UNEXPECTED);
22059 return${errorReturn};
22061 """,
22062 methodAtomName=CGDictionary.makeIdName(self.methodName),
22063 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
22064 errorReturn=self.getDefaultRetval(),
22066 if not self.singleOperation:
22067 return "JS::Rooted<JS::Value> callable(cx);\n" + getCallableFromProp
22068 return fill(
22070 bool isCallable = JS::IsCallable(mCallback);
22071 JS::Rooted<JS::Value> callable(cx);
22072 if (isCallable) {
22073 callable = JS::ObjectValue(*mCallback);
22074 } else {
22075 $*{getCallableFromProp}
22077 """,
22078 getCallableFromProp=getCallableFromProp,
22081 def getCallGuard(self):
22082 return ""
22085 class CallbackOperation(CallbackOperationBase):
22087 Codegen actual WebIDL operations on callback interfaces.
22090 def __init__(self, method, signature, descriptor, spiderMonkeyInterfacesAreStructs):
22091 self.ensureASCIIName(method)
22092 self.method = method
22093 jsName = method.identifier.name
22094 CallbackOperationBase.__init__(
22095 self,
22096 signature,
22097 jsName,
22098 MakeNativeName(descriptor.binaryNameFor(jsName, False)),
22099 descriptor,
22100 descriptor.interface.isSingleOperationInterface(),
22101 rethrowContentException=descriptor.interface.isJSImplemented(),
22102 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
22105 def getPrettyName(self):
22106 return "%s.%s" % (
22107 self.descriptorProvider.interface.identifier.name,
22108 self.method.identifier.name,
22112 class CallbackAccessor(CallbackMember):
22114 Shared superclass for CallbackGetter and CallbackSetter.
22117 def __init__(self, attr, sig, name, descriptor, spiderMonkeyInterfacesAreStructs):
22118 self.ensureASCIIName(attr)
22119 self.attrName = attr.identifier.name
22120 CallbackMember.__init__(
22121 self,
22122 sig,
22123 name,
22124 descriptor,
22125 needThisHandling=False,
22126 rethrowContentException=descriptor.interface.isJSImplemented(),
22127 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
22130 def getPrettyName(self):
22131 return "%s.%s" % (
22132 self.descriptorProvider.interface.identifier.name,
22133 self.attrName,
22137 class CallbackGetter(CallbackAccessor):
22138 def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
22139 CallbackAccessor.__init__(
22140 self,
22141 attr,
22142 (attr.type, []),
22143 callbackGetterName(attr, descriptor),
22144 descriptor,
22145 spiderMonkeyInterfacesAreStructs,
22148 def getRvalDecl(self):
22149 return "JS::Rooted<JS::Value> rval(cx);\n"
22151 def getCall(self):
22152 return fill(
22154 JS::Rooted<JSObject *> callback(cx, mCallback);
22155 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
22156 if ((reinterpret_cast<jsid*>(atomsCache)->isVoid()
22157 && !InitIds(cx, atomsCache)) ||
22158 !JS_GetPropertyById(cx, callback, atomsCache->${attrAtomName}, &rval)) {
22159 aRv.Throw(NS_ERROR_UNEXPECTED);
22160 return${errorReturn};
22162 """,
22163 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
22164 attrAtomName=CGDictionary.makeIdName(
22165 self.descriptorProvider.binaryNameFor(self.attrName, False)
22167 errorReturn=self.getDefaultRetval(),
22171 class CallbackSetter(CallbackAccessor):
22172 def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
22173 CallbackAccessor.__init__(
22174 self,
22175 attr,
22177 BuiltinTypes[IDLBuiltinType.Types.undefined],
22178 [FakeArgument(attr.type)],
22180 callbackSetterName(attr, descriptor),
22181 descriptor,
22182 spiderMonkeyInterfacesAreStructs,
22185 def getRvalDecl(self):
22186 # We don't need an rval
22187 return ""
22189 def getCall(self):
22190 return fill(
22192 MOZ_ASSERT(argv.length() == 1);
22193 JS::Rooted<JSObject*> callback(cx, CallbackKnownNotGray());
22194 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
22195 if ((reinterpret_cast<jsid*>(atomsCache)->isVoid() &&
22196 !InitIds(cx, atomsCache)) ||
22197 !JS_SetPropertyById(cx, callback, atomsCache->${attrAtomName}, argv[0])) {
22198 aRv.Throw(NS_ERROR_UNEXPECTED);
22199 return${errorReturn};
22201 """,
22202 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
22203 attrAtomName=CGDictionary.makeIdName(
22204 self.descriptorProvider.binaryNameFor(self.attrName, False)
22206 errorReturn=self.getDefaultRetval(),
22209 def getArgcDecl(self):
22210 return None
22213 class CGJSImplInitOperation(CallbackOperationBase):
22215 Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL.
22218 def __init__(self, sig, descriptor):
22219 assert sig in descriptor.interface.ctor().signatures()
22220 CallbackOperationBase.__init__(
22221 self,
22222 (BuiltinTypes[IDLBuiltinType.Types.undefined], sig[1]),
22223 "__init",
22224 "__Init",
22225 descriptor,
22226 singleOperation=False,
22227 rethrowContentException=True,
22228 spiderMonkeyInterfacesAreStructs=True,
22231 def getPrettyName(self):
22232 return "__init"
22235 class CGJSImplEventHookOperation(CallbackOperationBase):
22237 Codegen the hooks on a JS impl for adding/removing event listeners.
22240 def __init__(self, descriptor, name):
22241 self.name = name
22243 CallbackOperationBase.__init__(
22244 self,
22246 BuiltinTypes[IDLBuiltinType.Types.undefined],
22247 [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.domstring], "aType")],
22249 name,
22250 MakeNativeName(name),
22251 descriptor,
22252 singleOperation=False,
22253 rethrowContentException=False,
22254 spiderMonkeyInterfacesAreStructs=True,
22257 def getPrettyName(self):
22258 return self.name
22261 def getMaplikeOrSetlikeErrorReturn(helperImpl):
22263 Generate return values based on whether a maplike or setlike generated
22264 method is an interface method (which returns bool) or a helper function
22265 (which uses ErrorResult).
22267 if helperImpl:
22268 return dedent(
22270 aRv.Throw(NS_ERROR_UNEXPECTED);
22271 return%s;
22273 % helperImpl.getDefaultRetval()
22275 return "return false;\n"
22278 def getMaplikeOrSetlikeBackingObject(descriptor, maplikeOrSetlike, helperImpl=None):
22280 Generate code to get/create a JS backing object for a maplike/setlike
22281 declaration from the declaration slot.
22283 func_prefix = maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title()
22284 ret = fill(
22286 JS::Rooted<JSObject*> backingObj(cx);
22287 bool created = false;
22288 if (!Get${func_prefix}BackingObject(cx, obj, ${slot}, &backingObj, &created)) {
22289 $*{errorReturn}
22291 if (created) {
22292 PreserveWrapper<${selfType}>(self);
22294 """,
22295 slot=memberReservedSlot(maplikeOrSetlike, descriptor),
22296 func_prefix=func_prefix,
22297 errorReturn=getMaplikeOrSetlikeErrorReturn(helperImpl),
22298 selfType=descriptor.nativeType,
22300 return ret
22303 def getMaplikeOrSetlikeSizeGetterBody(descriptor, attr):
22305 Creates the body for the size getter method of maplike/setlike interfaces.
22307 # We should only have one declaration attribute currently
22308 assert attr.identifier.name == "size"
22309 assert attr.isMaplikeOrSetlikeAttr()
22310 return fill(
22312 $*{getBackingObj}
22313 uint32_t result = JS::${funcPrefix}Size(cx, backingObj);
22314 MOZ_ASSERT(!JS_IsExceptionPending(cx));
22315 args.rval().setNumber(result);
22316 return true;
22317 """,
22318 getBackingObj=getMaplikeOrSetlikeBackingObject(
22319 descriptor, attr.maplikeOrSetlike
22321 funcPrefix=attr.maplikeOrSetlike.prefix,
22325 class CGMaplikeOrSetlikeMethodGenerator(CGThing):
22327 Creates methods for maplike/setlike interfaces. It is expected that all
22328 methods will be have a maplike/setlike object attached. Unwrapping/wrapping
22329 will be taken care of by the usual method generation machinery in
22330 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
22331 using CGCallGenerator.
22334 def __init__(
22335 self,
22336 descriptor,
22337 maplikeOrSetlike,
22338 methodName,
22339 needsValueTypeReturn=False,
22340 helperImpl=None,
22342 CGThing.__init__(self)
22343 # True if this will be the body of a C++ helper function.
22344 self.helperImpl = helperImpl
22345 self.descriptor = descriptor
22346 self.maplikeOrSetlike = maplikeOrSetlike
22347 self.cgRoot = CGList([])
22348 impl_method_name = methodName
22349 if impl_method_name[0] == "_":
22350 # double underscore means this is a js-implemented chrome only rw
22351 # function. Truncate the double underscore so calling the right
22352 # underlying JSAPI function still works.
22353 impl_method_name = impl_method_name[2:]
22354 self.cgRoot.append(
22355 CGGeneric(
22356 getMaplikeOrSetlikeBackingObject(
22357 self.descriptor, self.maplikeOrSetlike, self.helperImpl
22361 self.returnStmt = getMaplikeOrSetlikeErrorReturn(self.helperImpl)
22363 # Generates required code for the method. Method descriptions included
22364 # in definitions below. Throw if we don't have a method to fill in what
22365 # we're looking for.
22366 try:
22367 methodGenerator = getattr(self, impl_method_name)
22368 except AttributeError:
22369 raise TypeError(
22370 "Missing %s method definition '%s'"
22371 % (self.maplikeOrSetlike.maplikeOrSetlikeType, methodName)
22373 # Method generator returns tuple, containing:
22375 # - a list of CGThings representing setup code for preparing to call
22376 # the JS API function
22377 # - a list of arguments needed for the JS API function we're calling
22378 # - list of code CGThings needed for return value conversion.
22379 (setupCode, arguments, setResult) = methodGenerator()
22381 # Create the actual method call, and then wrap it with the code to
22382 # return the value if needed.
22383 funcName = self.maplikeOrSetlike.prefix + MakeNativeName(impl_method_name)
22384 # Append the list of setup code CGThings
22385 self.cgRoot.append(CGList(setupCode))
22386 # Create the JS API call
22387 code = dedent(
22389 if (!JS::${funcName}(${args})) {
22390 $*{errorReturn}
22395 if needsValueTypeReturn:
22396 assert self.helperImpl and impl_method_name == "get"
22397 code += fill(
22399 if (result.isUndefined()) {
22400 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
22401 return${retval};
22403 """,
22404 retval=self.helperImpl.getDefaultRetval(),
22407 self.cgRoot.append(
22408 CGWrapper(
22409 CGGeneric(
22410 fill(
22411 code,
22412 funcName=funcName,
22413 args=", ".join(["cx", "backingObj"] + arguments),
22414 errorReturn=self.returnStmt,
22419 # Append result conversion
22420 self.cgRoot.append(CGList(setResult))
22422 def mergeTuples(self, a, b):
22424 Expecting to take 2 tuples were all elements are lists, append the lists in
22425 the second tuple to the lists in the first.
22427 return tuple([x + y for x, y in zip(a, b)])
22429 def appendArgConversion(self, name):
22431 Generate code to convert arguments to JS::Values, so they can be
22432 passed into JSAPI functions.
22434 return CGGeneric(
22435 fill(
22437 JS::Rooted<JS::Value> ${name}Val(cx);
22438 if (!ToJSValue(cx, ${name}, &${name}Val)) {
22439 $*{errorReturn}
22441 """,
22442 name=name,
22443 errorReturn=self.returnStmt,
22447 def appendKeyArgConversion(self):
22449 Generates the key argument for methods. Helper functions will use
22450 a RootedVector<JS::Value>, while interface methods have separate JS::Values.
22452 if self.helperImpl:
22453 return ([], ["argv[0]"], [])
22454 return ([self.appendArgConversion("arg0")], ["arg0Val"], [])
22456 def appendKeyAndValueArgConversion(self):
22458 Generates arguments for methods that require a key and value. Helper
22459 functions will use a RootedVector<JS::Value>, while interface methods have
22460 separate JS::Values.
22462 r = self.appendKeyArgConversion()
22463 if self.helperImpl:
22464 return self.mergeTuples(r, ([], ["argv[1]"], []))
22465 return self.mergeTuples(
22466 r, ([self.appendArgConversion("arg1")], ["arg1Val"], [])
22469 def appendIteratorResult(self):
22471 Generate code to output JSObject* return values, needed for functions that
22472 return iterators. Iterators cannot currently be wrapped via Xrays. If
22473 something that would return an iterator is called via Xray, fail early.
22475 # TODO: Bug 1173651 - Remove check once bug 1023984 is fixed.
22476 code = CGGeneric(
22477 dedent(
22479 // TODO (Bug 1173651): Xrays currently cannot wrap iterators. Change
22480 // after bug 1023984 is fixed.
22481 if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
22482 JS_ReportErrorASCII(cx, "Xray wrapping of iterators not supported.");
22483 return false;
22485 JS::Rooted<JSObject*> result(cx);
22486 JS::Rooted<JS::Value> v(cx);
22490 arguments = "&v"
22491 setResult = CGGeneric(
22492 dedent(
22494 result = &v.toObject();
22498 return ([code], [arguments], [setResult])
22500 def appendSelfResult(self):
22502 Generate code to return the interface object itself.
22504 code = CGGeneric(
22505 dedent(
22507 JS::Rooted<JSObject*> result(cx);
22511 setResult = CGGeneric(
22512 dedent(
22514 result = obj;
22518 return ([code], [], [setResult])
22520 def appendBoolResult(self):
22521 if self.helperImpl:
22522 return ([CGGeneric("bool retVal;\n")], ["&retVal"], [])
22523 return ([CGGeneric("bool result;\n")], ["&result"], [])
22525 def forEach(self):
22527 void forEach(callback c, any thisval);
22529 ForEach takes a callback, and a possible value to use as 'this'. The
22530 callback needs to take value, key, and the interface object
22531 implementing maplike/setlike. In order to make sure that the third arg
22532 is our interface object instead of the map/set backing object, we
22533 create a js function with the callback and original object in its
22534 storage slots, then use a helper function in BindingUtils to make sure
22535 the callback is called correctly.
22537 assert not self.helperImpl
22538 code = [
22539 CGGeneric(
22540 dedent(
22542 // Create a wrapper function.
22543 JSFunction* func = js::NewFunctionWithReserved(cx, ForEachHandler, 3, 0, nullptr);
22544 if (!func) {
22545 return false;
22547 JS::Rooted<JSObject*> funcObj(cx, JS_GetFunctionObject(func));
22548 JS::Rooted<JS::Value> funcVal(cx, JS::ObjectValue(*funcObj));
22549 js::SetFunctionNativeReserved(funcObj, FOREACH_CALLBACK_SLOT,
22550 JS::ObjectValue(*arg0));
22551 js::SetFunctionNativeReserved(funcObj, FOREACH_MAPLIKEORSETLIKEOBJ_SLOT,
22552 JS::ObjectValue(*obj));
22557 arguments = ["funcVal", "arg1"]
22558 return (code, arguments, [])
22560 def set(self):
22562 object set(key, value);
22564 Maplike only function, takes key and sets value to it, returns
22565 interface object unless being called from a C++ helper.
22567 assert self.maplikeOrSetlike.isMaplike()
22568 r = self.appendKeyAndValueArgConversion()
22569 if self.helperImpl:
22570 return r
22571 return self.mergeTuples(r, self.appendSelfResult())
22573 def add(self):
22575 object add(value);
22577 Setlike only function, adds value to set, returns interface object
22578 unless being called from a C++ helper
22580 assert self.maplikeOrSetlike.isSetlike()
22581 r = self.appendKeyArgConversion()
22582 if self.helperImpl:
22583 return r
22584 return self.mergeTuples(r, self.appendSelfResult())
22586 def get(self):
22588 type? get(key);
22590 Retrieves a value from a backing object based on the key. Returns value
22591 if key is in backing object, undefined otherwise.
22593 assert self.maplikeOrSetlike.isMaplike()
22594 r = self.appendKeyArgConversion()
22596 code = []
22597 # We don't need to create the result variable because it'll be created elsewhere
22598 # for JSObject Get method
22599 if not self.helperImpl or not self.helperImpl.needsScopeBody():
22600 code = [
22601 CGGeneric(
22602 dedent(
22604 JS::Rooted<JS::Value> result(cx);
22610 arguments = ["&result"]
22611 return self.mergeTuples(r, (code, arguments, []))
22613 def has(self):
22615 bool has(key);
22617 Check if an entry exists in the backing object. Returns true if value
22618 exists in backing object, false otherwise.
22620 return self.mergeTuples(self.appendKeyArgConversion(), self.appendBoolResult())
22622 def keys(self):
22624 object keys();
22626 Returns new object iterator with all keys from backing object.
22628 return self.appendIteratorResult()
22630 def values(self):
22632 object values();
22634 Returns new object iterator with all values from backing object.
22636 return self.appendIteratorResult()
22638 def entries(self):
22640 object entries();
22642 Returns new object iterator with all keys and values from backing
22643 object. Keys will be null for set.
22645 return self.appendIteratorResult()
22647 def clear(self):
22649 void clear();
22651 Removes all entries from map/set.
22653 return ([], [], [])
22655 def delete(self):
22657 bool delete(key);
22659 Deletes an entry from the backing object. Returns true if value existed
22660 in backing object, false otherwise.
22662 return self.mergeTuples(self.appendKeyArgConversion(), self.appendBoolResult())
22664 def define(self):
22665 return self.cgRoot.define()
22668 class CGHelperFunctionGenerator(CallbackMember):
22670 Generates code to allow C++ to perform operations. Gets a context from the
22671 binding wrapper, turns arguments into JS::Values (via
22672 CallbackMember/CGNativeMember argument conversion), then uses
22673 getCall to generate the body for getting result, and maybe convert the
22674 result into return type (via CallbackMember/CGNativeMember result
22675 conversion)
22678 class HelperFunction(CGAbstractMethod):
22680 Generates context retrieval code and rooted JSObject for interface for
22681 method generator to use
22684 def __init__(self, descriptor, name, args, code, returnType):
22685 self.code = code
22686 CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
22688 def definition_body(self):
22689 return self.code
22691 def __init__(
22692 self,
22693 descriptor,
22694 name,
22695 args,
22696 returnType=BuiltinTypes[IDLBuiltinType.Types.undefined],
22697 needsResultConversion=True,
22699 assert returnType.isType()
22700 self.needsResultConversion = needsResultConversion
22702 # Run CallbackMember init function to generate argument conversion code.
22703 # wrapScope is set to 'obj' when generating maplike or setlike helper
22704 # functions, as we don't have access to the CallbackPreserveColor
22705 # method.
22706 CallbackMember.__init__(
22707 self,
22708 [returnType, args],
22709 name,
22710 descriptor,
22711 False,
22712 wrapScope="obj",
22713 passJSBitsAsNeeded=typeNeedsCx(returnType),
22716 # Wrap CallbackMember body code into a CGAbstractMethod to make
22717 # generation easier.
22718 self.implMethod = CGHelperFunctionGenerator.HelperFunction(
22719 descriptor, name, self.args, self.body, self.returnType
22722 def getCallSetup(self):
22723 # If passJSBitsAsNeeded is true, it means the caller will provide a
22724 # JSContext, so we don't need to create JSContext and enter
22725 # UnprivilegedJunkScopeOrWorkerGlobal here.
22726 code = "MOZ_ASSERT(self);\n"
22727 if not self.passJSBitsAsNeeded:
22728 code += dedent(
22730 AutoJSAPI jsapi;
22731 jsapi.Init();
22732 JSContext* cx = jsapi.cx();
22733 // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here because
22734 // all we want is to wrap into _some_ scope and then unwrap to find
22735 // the reflector, and wrapping has no side-effects.
22736 JSObject* scope = UnprivilegedJunkScopeOrWorkerGlobal(fallible);
22737 if (!scope) {
22738 aRv.Throw(NS_ERROR_UNEXPECTED);
22739 return%s;
22741 JSAutoRealm tempRealm(cx, scope);
22743 % self.getDefaultRetval()
22746 code += dedent(
22748 JS::Rooted<JS::Value> v(cx);
22749 if(!ToJSValue(cx, self, &v)) {
22750 aRv.Throw(NS_ERROR_UNEXPECTED);
22751 return%s;
22753 // This is a reflector, but due to trying to name things
22754 // similarly across method generators, it's called obj here.
22755 JS::Rooted<JSObject*> obj(cx);
22756 obj = js::UncheckedUnwrap(&v.toObject(), /* stopAtWindowProxy = */ false);
22758 % self.getDefaultRetval()
22761 # We'd like wrap the inner code in a scope such that the code can use the
22762 # same realm. So here we are creating the result variable outside of the
22763 # scope.
22764 if self.needsScopeBody():
22765 code += "JS::Rooted<JS::Value> result(cx);\n"
22767 return code
22769 def getArgs(self, returnType, argList):
22770 # We don't need the context or the value. We'll generate those instead.
22771 args = CGNativeMember.getArgs(self, returnType, argList)
22772 # Prepend a pointer to the binding object onto the arguments
22773 return [Argument(self.descriptorProvider.nativeType + "*", "self")] + args
22775 def needsScopeBody(self):
22776 return self.passJSBitsAsNeeded
22778 def getArgvDeclFailureCode(self):
22779 return "aRv.Throw(NS_ERROR_UNEXPECTED);\n"
22781 def getResultConversion(self):
22782 if self.needsResultConversion:
22783 code = ""
22784 if self.needsScopeBody():
22785 code = dedent(
22787 if (!JS_WrapValue(cx, &result)) {
22788 aRv.NoteJSContextException(cx);
22789 return;
22794 failureCode = dedent("aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n")
22796 exceptionCode = None
22797 if self.retvalType.isPrimitive():
22798 exceptionCode = dedent(
22799 "aRv.NoteJSContextException(cx);\nreturn%s;\n"
22800 % self.getDefaultRetval()
22803 return code + CallbackMember.getResultConversion(
22804 self,
22805 "result",
22806 failureCode=failureCode,
22807 isDefinitelyObject=True,
22808 exceptionCode=exceptionCode,
22811 assignRetval = string.Template(
22812 self.getRetvalInfo(self.retvalType, False)[2]
22813 ).substitute(
22815 "declName": "retVal",
22818 return assignRetval
22820 def getRvalDecl(self):
22821 # hack to make sure we put JSAutoRealm inside the body scope
22822 return "JSAutoRealm reflectorRealm(cx, obj);\n"
22824 def getArgcDecl(self):
22825 # Don't need argc for anything.
22826 return None
22828 def getCall(self):
22829 assert False # Override me!
22831 def getPrettyName(self):
22832 return self.name
22834 def declare(self):
22835 return self.implMethod.declare()
22837 def define(self):
22838 return self.implMethod.define()
22841 class CGMaplikeOrSetlikeHelperFunctionGenerator(CGHelperFunctionGenerator):
22843 Generates code to allow C++ to perform operations on backing objects. Gets
22844 a context from the binding wrapper, turns arguments into JS::Values (via
22845 CallbackMember/CGNativeMember argument conversion), then uses
22846 CGMaplikeOrSetlikeMethodGenerator to generate the body.
22849 def __init__(
22850 self,
22851 descriptor,
22852 maplikeOrSetlike,
22853 name,
22854 needsKeyArg=False,
22855 needsValueArg=False,
22856 needsValueTypeReturn=False,
22857 needsBoolReturn=False,
22858 needsResultConversion=True,
22860 self.maplikeOrSetlike = maplikeOrSetlike
22861 self.needsValueTypeReturn = needsValueTypeReturn
22863 args = []
22864 if needsKeyArg:
22865 args.append(FakeArgument(maplikeOrSetlike.keyType, "aKey"))
22866 if needsValueArg:
22867 assert needsKeyArg
22868 assert not needsValueTypeReturn
22869 args.append(FakeArgument(maplikeOrSetlike.valueType, "aValue"))
22871 returnType = BuiltinTypes[IDLBuiltinType.Types.undefined]
22872 if needsBoolReturn:
22873 returnType = BuiltinTypes[IDLBuiltinType.Types.boolean]
22874 elif needsValueTypeReturn:
22875 returnType = maplikeOrSetlike.valueType
22877 CGHelperFunctionGenerator.__init__(
22878 self,
22879 descriptor,
22880 name,
22881 args,
22882 returnType,
22883 needsResultConversion,
22886 def getCall(self):
22887 return CGMaplikeOrSetlikeMethodGenerator(
22888 self.descriptorProvider,
22889 self.maplikeOrSetlike,
22890 self.name.lower(),
22891 self.needsValueTypeReturn,
22892 helperImpl=self,
22893 ).define()
22896 class CGMaplikeOrSetlikeHelperGenerator(CGNamespace):
22898 Declares and defines convenience methods for accessing backing objects on
22899 setlike/maplike interface. Generates function signatures, un/packs
22900 backing objects from slot, etc.
22903 def __init__(self, descriptor, maplikeOrSetlike):
22904 self.descriptor = descriptor
22905 # Since iterables are folded in with maplike/setlike, make sure we've
22906 # got the right type here.
22907 assert maplikeOrSetlike.isMaplike() or maplikeOrSetlike.isSetlike()
22908 self.maplikeOrSetlike = maplikeOrSetlike
22909 self.namespace = "%sHelpers" % (
22910 self.maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title()
22912 self.helpers = [
22913 CGMaplikeOrSetlikeHelperFunctionGenerator(
22914 descriptor, maplikeOrSetlike, "Clear"
22916 CGMaplikeOrSetlikeHelperFunctionGenerator(
22917 descriptor,
22918 maplikeOrSetlike,
22919 "Delete",
22920 needsKeyArg=True,
22921 needsBoolReturn=True,
22922 needsResultConversion=False,
22924 CGMaplikeOrSetlikeHelperFunctionGenerator(
22925 descriptor,
22926 maplikeOrSetlike,
22927 "Has",
22928 needsKeyArg=True,
22929 needsBoolReturn=True,
22930 needsResultConversion=False,
22933 if self.maplikeOrSetlike.isMaplike():
22934 self.helpers.append(
22935 CGMaplikeOrSetlikeHelperFunctionGenerator(
22936 descriptor,
22937 maplikeOrSetlike,
22938 "Set",
22939 needsKeyArg=True,
22940 needsValueArg=True,
22943 self.helpers.append(
22944 CGMaplikeOrSetlikeHelperFunctionGenerator(
22945 descriptor,
22946 maplikeOrSetlike,
22947 "Get",
22948 needsKeyArg=True,
22949 needsValueTypeReturn=True,
22952 else:
22953 assert self.maplikeOrSetlike.isSetlike()
22954 self.helpers.append(
22955 CGMaplikeOrSetlikeHelperFunctionGenerator(
22956 descriptor, maplikeOrSetlike, "Add", needsKeyArg=True
22959 CGNamespace.__init__(self, self.namespace, CGList(self.helpers))
22962 class CGIterableMethodGenerator(CGGeneric):
22964 Creates methods for iterable interfaces. Unwrapping/wrapping
22965 will be taken care of by the usual method generation machinery in
22966 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
22967 using CGCallGenerator.
22970 def __init__(self, descriptor, methodName, args):
22971 if methodName == "forEach":
22972 assert len(args) == 2
22974 CGGeneric.__init__(
22975 self,
22976 fill(
22978 if (!JS::IsCallable(arg0)) {
22979 cx.ThrowErrorMessage<MSG_NOT_CALLABLE>("Argument 1");
22980 return false;
22982 JS::RootedValueArray<3> callArgs(cx);
22983 callArgs[2].setObject(*obj);
22984 JS::Rooted<JS::Value> ignoredReturnVal(cx);
22985 auto GetKeyAtIndex = &${selfType}::GetKeyAtIndex;
22986 auto GetValueAtIndex = &${selfType}::GetValueAtIndex;
22987 for (size_t i = 0; i < self->GetIterableLength(); ++i) {
22988 if (!CallIterableGetter(cx, GetValueAtIndex, self, i,
22989 callArgs[0])) {
22990 return false;
22992 if (!CallIterableGetter(cx, GetKeyAtIndex, self, i,
22993 callArgs[1])) {
22994 return false;
22996 if (!JS::Call(cx, arg1, arg0, JS::HandleValueArray(callArgs),
22997 &ignoredReturnVal)) {
22998 return false;
23001 """,
23002 ifaceName=descriptor.interface.identifier.name,
23003 selfType=descriptor.nativeType,
23006 return
23008 if descriptor.interface.isIterable():
23009 assert descriptor.interface.maplikeOrSetlikeOrIterable.isPairIterator()
23010 assert len(args) == 0
23012 wrap = f"{descriptor.interface.identifier.name}Iterator_Binding::Wrap"
23013 iterClass = f"mozilla::dom::binding_detail::WrappableIterableIterator<{descriptor.nativeType}, &{wrap}>"
23014 else:
23015 needReturnMethod = toStringBool(
23016 descriptor.interface.maplikeOrSetlikeOrIterable.getExtendedAttribute(
23017 "GenerateReturnMethod"
23019 is not None
23021 wrap = f"{descriptor.interface.identifier.name}AsyncIterator_Binding::Wrap"
23022 iterClass = f"mozilla::dom::binding_detail::WrappableAsyncIterableIterator<{descriptor.nativeType}, {needReturnMethod}, &{wrap}>"
23024 createIterator = fill(
23026 typedef ${iterClass} itrType;
23027 RefPtr<itrType> result(new itrType(self,
23028 itrType::IteratorType::${itrMethod}));
23029 """,
23030 iterClass=iterClass,
23031 itrMethod=methodName.title(),
23034 if descriptor.interface.isAsyncIterable():
23035 args.append("initError")
23036 createIterator = fill(
23038 $*{createIterator}
23040 ErrorResult initError;
23041 self->InitAsyncIteratorData(result->Data(), itrType::IteratorType::${itrMethod}, ${args});
23042 if (initError.MaybeSetPendingException(cx, "Asynchronous iterator initialization steps for ${ifaceName} failed")) {
23043 return false;
23046 """,
23047 createIterator=createIterator,
23048 itrMethod=methodName.title(),
23049 args=", ".join(args),
23050 ifaceName=descriptor.interface.identifier.name,
23053 CGGeneric.__init__(self, createIterator)
23056 def getObservableArrayBackingObject(descriptor, attr, errorReturn="return false;\n"):
23058 Generate code to get/create a JS backing list for an observableArray attribute
23059 from the declaration slot.
23061 assert attr.isAttr()
23062 assert attr.type.isObservableArray()
23064 # GetObservableArrayBackingObject may return a wrapped object for Xrays, so
23065 # when we create it we need to unwrap it to store the interface in the
23066 # reserved slot.
23067 return fill(
23069 JS::Rooted<JSObject*> backingObj(cx);
23070 bool created = false;
23071 if (!GetObservableArrayBackingObject(cx, obj, ${slot},
23072 &backingObj, &created, ${namespace}::ObservableArrayProxyHandler::getInstance(),
23073 self)) {
23074 $*{errorReturn}
23076 if (created) {
23077 PreserveWrapper(self);
23079 """,
23080 namespace=toBindingNamespace(MakeNativeName(attr.identifier.name)),
23081 slot=memberReservedSlot(attr, descriptor),
23082 errorReturn=errorReturn,
23083 selfType=descriptor.nativeType,
23087 def getObservableArrayGetterBody(descriptor, attr):
23089 Creates the body for the getter method of an observableArray attribute.
23091 assert attr.type.isObservableArray()
23092 return fill(
23094 $*{getBackingObj}
23095 MOZ_ASSERT(!JS_IsExceptionPending(cx));
23096 args.rval().setObject(*backingObj);
23097 return true;
23098 """,
23099 getBackingObj=getObservableArrayBackingObject(descriptor, attr),
23103 class CGObservableArrayProxyHandler_callback(ClassMethod):
23105 Base class for declaring and defining the hook methods for ObservableArrayProxyHandler
23106 subclasses to get the interface native object from backing object and calls
23107 its On{Set|Delete}* callback.
23109 * 'callbackType': "Set" or "Delete".
23110 * 'invalidTypeFatal' (optional): If True, we don't expect the type
23111 conversion would fail, so generate the
23112 assertion code if type conversion fails.
23115 def __init__(
23116 self, descriptor, attr, name, args, callbackType, invalidTypeFatal=False
23118 assert attr.isAttr()
23119 assert attr.type.isObservableArray()
23120 assert callbackType in ["Set", "Delete"]
23121 self.descriptor = descriptor
23122 self.attr = attr
23123 self.innertype = attr.type.inner
23124 self.callbackType = callbackType
23125 self.invalidTypeFatal = invalidTypeFatal
23126 ClassMethod.__init__(
23127 self,
23128 name,
23129 "bool",
23130 args,
23131 visibility="protected",
23132 virtual=True,
23133 override=True,
23134 const=True,
23137 def preConversion(self):
23139 The code to run before the conversion steps.
23141 return ""
23143 def preCallback(self):
23145 The code to run before calling the callback.
23147 return ""
23149 def postCallback(self):
23151 The code to run after calling the callback, all subclasses should override
23152 this to generate the return values.
23154 assert False # Override me!
23156 def getBody(self):
23157 exceptionCode = (
23158 fill(
23160 MOZ_ASSERT_UNREACHABLE("The item in ObservableArray backing list is not ${innertype}?");
23161 return false;
23162 """,
23163 innertype=self.innertype,
23165 if self.invalidTypeFatal
23166 else None
23168 convertType = instantiateJSToNativeConversion(
23169 getJSToNativeConversionInfo(
23170 self.innertype,
23171 self.descriptor,
23172 sourceDescription="Element in ObservableArray backing list",
23173 exceptionCode=exceptionCode,
23176 "declName": "decl",
23177 "holderName": "holder",
23178 "val": "aValue",
23181 callbackArgs = ["decl", "aIndex", "rv"]
23182 if typeNeedsCx(self.innertype):
23183 callbackArgs.insert(0, "cx")
23184 return fill(
23186 MOZ_ASSERT(IsObservableArrayProxy(aProxy));
23187 $*{preConversion}
23189 BindingCallContext cx(aCx, "ObservableArray ${name}");
23190 $*{convertType}
23192 $*{preCallback}
23193 JS::Value val = js::GetProxyReservedSlot(aProxy, OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT);
23194 auto* interface = static_cast<${ifaceType}*>(val.toPrivate());
23195 MOZ_ASSERT(interface);
23197 ErrorResult rv;
23198 MOZ_KnownLive(interface)->${methodName}(${callbackArgs});
23199 $*{postCallback}
23200 """,
23201 preConversion=self.preConversion(),
23202 name=self.name,
23203 convertType=convertType.define(),
23204 preCallback=self.preCallback(),
23205 ifaceType=self.descriptor.nativeType,
23206 methodName="On%s%s"
23207 % (self.callbackType, MakeNativeName(self.attr.identifier.name)),
23208 callbackArgs=", ".join(callbackArgs),
23209 postCallback=self.postCallback(),
23213 class CGObservableArrayProxyHandler_OnDeleteItem(
23214 CGObservableArrayProxyHandler_callback
23217 Declares and defines the hook methods for ObservableArrayProxyHandler
23218 subclasses to get the interface native object from backing object and calls
23219 its OnDelete* callback.
23222 def __init__(self, descriptor, attr):
23223 args = [
23224 Argument("JSContext*", "aCx"),
23225 Argument("JS::Handle<JSObject*>", "aProxy"),
23226 Argument("JS::Handle<JS::Value>", "aValue"),
23227 Argument("uint32_t", "aIndex"),
23229 CGObservableArrayProxyHandler_callback.__init__(
23230 self,
23231 descriptor,
23232 attr,
23233 "OnDeleteItem",
23234 args,
23235 "Delete",
23236 True,
23239 def postCallback(self):
23240 return dedent(
23242 return !rv.MaybeSetPendingException(cx);
23247 class CGObservableArrayProxyHandler_SetIndexedValue(
23248 CGObservableArrayProxyHandler_callback
23251 Declares and defines the hook methods for ObservableArrayProxyHandler
23252 subclasses to run the setting the indexed value steps.
23255 def __init__(self, descriptor, attr):
23256 args = [
23257 Argument("JSContext*", "aCx"),
23258 Argument("JS::Handle<JSObject*>", "aProxy"),
23259 Argument("JS::Handle<JSObject*>", "aBackingList"),
23260 Argument("uint32_t", "aIndex"),
23261 Argument("JS::Handle<JS::Value>", "aValue"),
23262 Argument("JS::ObjectOpResult&", "aResult"),
23264 CGObservableArrayProxyHandler_callback.__init__(
23265 self,
23266 descriptor,
23267 attr,
23268 "SetIndexedValue",
23269 args,
23270 "Set",
23273 def preConversion(self):
23274 return dedent(
23276 uint32_t oldLen;
23277 if (!JS::GetArrayLength(aCx, aBackingList, &oldLen)) {
23278 return false;
23281 if (aIndex > oldLen) {
23282 return aResult.failBadIndex();
23287 def preCallback(self):
23288 return dedent(
23290 if (aIndex < oldLen) {
23291 JS::Rooted<JS::Value> value(aCx);
23292 if (!JS_GetElement(aCx, aBackingList, aIndex, &value)) {
23293 return false;
23296 if (!OnDeleteItem(aCx, aProxy, value, aIndex)) {
23297 return false;
23304 def postCallback(self):
23305 return dedent(
23307 if (rv.MaybeSetPendingException(cx)) {
23308 return false;
23311 if (!JS_SetElement(aCx, aBackingList, aIndex, aValue)) {
23312 return false;
23315 return aResult.succeed();
23320 class CGObservableArrayProxyHandler(CGThing):
23322 A class for declaring a ObservableArrayProxyHandler.
23325 def __init__(self, descriptor, attr):
23326 assert attr.isAttr()
23327 assert attr.type.isObservableArray()
23328 methods = [
23329 # The item stored in backing object should always be converted successfully.
23330 CGObservableArrayProxyHandler_OnDeleteItem(descriptor, attr),
23331 CGObservableArrayProxyHandler_SetIndexedValue(descriptor, attr),
23332 CGJSProxyHandler_getInstance("ObservableArrayProxyHandler"),
23334 parentClass = "mozilla::dom::ObservableArrayProxyHandler"
23335 self.proxyHandler = CGClass(
23336 "ObservableArrayProxyHandler",
23337 bases=[ClassBase(parentClass)],
23338 constructors=[],
23339 methods=methods,
23342 def declare(self):
23343 # Our class declaration should happen when we're defining
23344 return ""
23346 def define(self):
23347 return self.proxyHandler.declare() + "\n" + self.proxyHandler.define()
23350 class CGObservableArrayProxyHandlerGenerator(CGNamespace):
23352 Declares and defines convenience methods for accessing backing list objects
23353 for observable array attribute. Generates function signatures, un/packs
23354 backing list objects from slot, etc.
23357 def __init__(self, descriptor, attr):
23358 assert attr.isAttr()
23359 assert attr.type.isObservableArray()
23360 namespace = toBindingNamespace(MakeNativeName(attr.identifier.name))
23361 proxyHandler = CGObservableArrayProxyHandler(descriptor, attr)
23362 CGNamespace.__init__(self, namespace, proxyHandler)
23365 class CGObservableArraySetterGenerator(CGGeneric):
23367 Creates setter for an observableArray attributes.
23370 def __init__(self, descriptor, attr):
23371 assert attr.isAttr()
23372 assert attr.type.isObservableArray()
23373 getBackingObject = getObservableArrayBackingObject(descriptor, attr)
23374 setElement = dedent(
23376 if (!JS_SetElement(cx, backingObj, i, val)) {
23377 return false;
23381 conversion = wrapForType(
23382 attr.type.inner,
23383 descriptor,
23385 "result": "arg0.ElementAt(i)",
23386 "successCode": setElement,
23387 "jsvalRef": "val",
23388 "jsvalHandle": "&val",
23391 CGGeneric.__init__(
23392 self,
23393 fill(
23395 if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
23396 JS_ReportErrorASCII(cx, "Accessing from Xray wrapper is not supported.");
23397 return false;
23400 ${getBackingObject}
23401 const ObservableArrayProxyHandler* handler = GetObservableArrayProxyHandler(backingObj);
23402 if (!handler->SetLength(cx, backingObj, 0)) {
23403 return false;
23406 JS::Rooted<JS::Value> val(cx);
23407 for (size_t i = 0; i < arg0.Length(); i++) {
23408 $*{conversion}
23410 """,
23411 conversion=conversion,
23412 getBackingObject=getBackingObject,
23417 class CGObservableArrayHelperFunctionGenerator(CGHelperFunctionGenerator):
23419 Generates code to allow C++ to perform operations on backing objects. Gets
23420 a context from the binding wrapper, turns arguments into JS::Values (via
23421 CallbackMember/CGNativeMember argument conversion), then uses
23422 MethodBodyGenerator to generate the body.
23425 class MethodBodyGenerator(CGThing):
23427 Creates methods body for observable array attribute. It is expected that all
23428 methods will be have a maplike/setlike object attached. Unwrapping/wrapping
23429 will be taken care of by the usual method generation machinery in
23430 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
23431 using CGCallGenerator.
23434 def __init__(
23435 self,
23436 descriptor,
23437 attr,
23438 methodName,
23439 helperGenerator,
23440 needsIndexArg,
23442 assert attr.isAttr()
23443 assert attr.type.isObservableArray()
23445 CGThing.__init__(self)
23446 self.helperGenerator = helperGenerator
23447 self.cgRoot = CGList([])
23449 self.cgRoot.append(
23450 CGGeneric(
23451 getObservableArrayBackingObject(
23452 descriptor,
23453 attr,
23454 dedent(
23456 aRv.Throw(NS_ERROR_UNEXPECTED);
23457 return%s;
23459 % helperGenerator.getDefaultRetval()
23465 # Generates required code for the method. Method descriptions included
23466 # in definitions below. Throw if we don't have a method to fill in what
23467 # we're looking for.
23468 try:
23469 methodGenerator = getattr(self, methodName)
23470 except AttributeError:
23471 raise TypeError(
23472 "Missing observable array method definition '%s'" % methodName
23474 # Method generator returns tuple, containing:
23476 # - a list of CGThings representing setup code for preparing to call
23477 # the JS API function
23478 # - JS API function name
23479 # - a list of arguments needed for the JS API function we're calling
23480 # - a list of CGThings representing code needed before return.
23481 (setupCode, funcName, arguments, returnCode) = methodGenerator()
23483 # Append the list of setup code CGThings
23484 self.cgRoot.append(CGList(setupCode))
23485 # Create the JS API call
23486 if needsIndexArg:
23487 arguments.insert(0, "aIndex")
23488 self.cgRoot.append(
23489 CGWrapper(
23490 CGGeneric(
23491 fill(
23493 aRv.MightThrowJSException();
23494 if (!${funcName}(${args})) {
23495 aRv.StealExceptionFromJSContext(cx);
23496 return${retval};
23498 """,
23499 funcName=funcName,
23500 args=", ".join(["cx", "backingObj"] + arguments),
23501 retval=helperGenerator.getDefaultRetval(),
23506 # Append code before return
23507 self.cgRoot.append(CGList(returnCode))
23509 def elementat(self):
23510 setupCode = []
23511 if not self.helperGenerator.needsScopeBody():
23512 setupCode.append(CGGeneric("JS::Rooted<JS::Value> result(cx);\n"))
23513 returnCode = [
23514 CGGeneric(
23515 fill(
23517 if (result.isUndefined()) {
23518 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
23519 return${retval};
23521 """,
23522 retval=self.helperGenerator.getDefaultRetval(),
23526 return (setupCode, "JS_GetElement", ["&result"], returnCode)
23528 def replaceelementat(self):
23529 setupCode = [
23530 CGGeneric(
23531 fill(
23533 uint32_t length;
23534 aRv.MightThrowJSException();
23535 if (!JS::GetArrayLength(cx, backingObj, &length)) {
23536 aRv.StealExceptionFromJSContext(cx);
23537 return${retval};
23539 if (aIndex > length) {
23540 aRv.ThrowRangeError("Invalid index");
23541 return${retval};
23543 """,
23544 retval=self.helperGenerator.getDefaultRetval(),
23548 return (setupCode, "JS_SetElement", ["argv[0]"], [])
23550 def appendelement(self):
23551 setupCode = [
23552 CGGeneric(
23553 fill(
23555 uint32_t length;
23556 aRv.MightThrowJSException();
23557 if (!JS::GetArrayLength(cx, backingObj, &length)) {
23558 aRv.StealExceptionFromJSContext(cx);
23559 return${retval};
23561 """,
23562 retval=self.helperGenerator.getDefaultRetval(),
23566 return (setupCode, "JS_SetElement", ["length", "argv[0]"], [])
23568 def removelastelement(self):
23569 setupCode = [
23570 CGGeneric(
23571 fill(
23573 uint32_t length;
23574 aRv.MightThrowJSException();
23575 if (!JS::GetArrayLength(cx, backingObj, &length)) {
23576 aRv.StealExceptionFromJSContext(cx);
23577 return${retval};
23579 if (length == 0) {
23580 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
23581 return${retval};
23583 """,
23584 retval=self.helperGenerator.getDefaultRetval(),
23588 return (setupCode, "JS::SetArrayLength", ["length - 1"], [])
23590 def length(self):
23591 return (
23592 [CGGeneric("uint32_t retVal;\n")],
23593 "JS::GetArrayLength",
23594 ["&retVal"],
23598 def define(self):
23599 return self.cgRoot.define()
23601 def __init__(
23602 self,
23603 descriptor,
23604 attr,
23605 name,
23606 returnType=BuiltinTypes[IDLBuiltinType.Types.undefined],
23607 needsResultConversion=True,
23608 needsIndexArg=False,
23609 needsValueArg=False,
23611 assert attr.isAttr()
23612 assert attr.type.isObservableArray()
23613 self.attr = attr
23614 self.needsIndexArg = needsIndexArg
23616 args = []
23617 if needsValueArg:
23618 args.append(FakeArgument(attr.type.inner, "aValue"))
23620 CGHelperFunctionGenerator.__init__(
23621 self,
23622 descriptor,
23623 name,
23624 args,
23625 returnType,
23626 needsResultConversion,
23629 def getArgs(self, returnType, argList):
23630 if self.needsIndexArg:
23631 argList = [
23632 FakeArgument(BuiltinTypes[IDLBuiltinType.Types.unsigned_long], "aIndex")
23633 ] + argList
23634 return CGHelperFunctionGenerator.getArgs(self, returnType, argList)
23636 def getCall(self):
23637 return CGObservableArrayHelperFunctionGenerator.MethodBodyGenerator(
23638 self.descriptorProvider,
23639 self.attr,
23640 self.name.lower(),
23641 self,
23642 self.needsIndexArg,
23643 ).define()
23646 class CGObservableArrayHelperGenerator(CGNamespace):
23648 Declares and defines convenience methods for accessing backing object for
23649 observable array type. Generates function signatures, un/packs
23650 backing objects from slot, etc.
23653 def __init__(self, descriptor, attr):
23654 assert attr.isAttr()
23655 assert attr.type.isObservableArray()
23657 namespace = "%sHelpers" % MakeNativeName(attr.identifier.name)
23658 helpers = [
23659 CGObservableArrayHelperFunctionGenerator(
23660 descriptor,
23661 attr,
23662 "ElementAt",
23663 returnType=attr.type.inner,
23664 needsIndexArg=True,
23666 CGObservableArrayHelperFunctionGenerator(
23667 descriptor,
23668 attr,
23669 "ReplaceElementAt",
23670 needsIndexArg=True,
23671 needsValueArg=True,
23673 CGObservableArrayHelperFunctionGenerator(
23674 descriptor,
23675 attr,
23676 "AppendElement",
23677 needsValueArg=True,
23679 CGObservableArrayHelperFunctionGenerator(
23680 descriptor,
23681 attr,
23682 "RemoveLastElement",
23684 CGObservableArrayHelperFunctionGenerator(
23685 descriptor,
23686 attr,
23687 "Length",
23688 returnType=BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
23689 needsResultConversion=False,
23692 CGNamespace.__init__(self, namespace, CGList(helpers, "\n"))
23695 class GlobalGenRoots:
23697 Roots for global codegen.
23699 To generate code, call the method associated with the target, and then
23700 call the appropriate define/declare method.
23703 @staticmethod
23704 def GeneratedAtomList(config):
23705 # Atom enum
23706 dictionaries = config.dictionaries
23708 structs = []
23710 def memberToAtomCacheMember(binaryNameFor, m):
23711 binaryMemberName = binaryNameFor(m)
23712 return ClassMember(
23713 CGDictionary.makeIdName(binaryMemberName),
23714 "PinnedStringId",
23715 visibility="public",
23718 def buildAtomCacheStructure(idlobj, binaryNameFor, members):
23719 classMembers = [memberToAtomCacheMember(binaryNameFor, m) for m in members]
23720 structName = idlobj.identifier.name + "Atoms"
23721 return (
23722 structName,
23723 CGWrapper(
23724 CGClass(
23725 structName, bases=None, isStruct=True, members=classMembers
23727 post="\n",
23731 for dict in dictionaries:
23732 if len(dict.members) == 0:
23733 continue
23735 structs.append(
23736 buildAtomCacheStructure(dict, lambda m: m.identifier.name, dict.members)
23739 for d in config.getDescriptors(isJSImplemented=True) + config.getDescriptors(
23740 isCallback=True
23742 members = [m for m in d.interface.members if m.isAttr() or m.isMethod()]
23743 if d.interface.isJSImplemented() and d.interface.ctor():
23744 # We'll have an __init() method.
23745 members.append(FakeMember("__init"))
23746 if d.interface.isJSImplemented() and d.interface.getExtendedAttribute(
23747 "WantsEventListenerHooks"
23749 members.append(FakeMember("eventListenerAdded"))
23750 members.append(FakeMember("eventListenerRemoved"))
23751 if len(members) == 0:
23752 continue
23754 structs.append(
23755 buildAtomCacheStructure(
23756 d.interface,
23757 lambda m: d.binaryNameFor(m.identifier.name, m.isStatic()),
23758 members,
23762 structs.sort()
23763 generatedStructs = [struct for structName, struct in structs]
23764 structNames = [structName for structName, struct in structs]
23766 mainStruct = CGWrapper(
23767 CGClass(
23768 "PerThreadAtomCache",
23769 bases=[ClassBase(structName) for structName in structNames],
23770 isStruct=True,
23772 post="\n",
23775 structs = CGList(generatedStructs + [mainStruct])
23777 # Wrap all of that in our namespaces.
23778 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(structs, pre="\n"))
23779 curr = CGWrapper(curr, post="\n")
23781 # Add include statement for PinnedStringId.
23782 declareIncludes = ["mozilla/dom/PinnedStringId.h"]
23783 curr = CGHeaders([], [], [], [], declareIncludes, [], "GeneratedAtomList", curr)
23785 # Add include guards.
23786 curr = CGIncludeGuard("GeneratedAtomList", curr)
23788 # Add the auto-generated comment.
23789 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
23791 # Done.
23792 return curr
23794 @staticmethod
23795 def GeneratedEventList(config):
23796 eventList = CGList([])
23797 for generatedEvent in config.generatedEvents:
23798 eventList.append(
23799 CGGeneric(declare=("GENERATED_EVENT(%s)\n" % generatedEvent))
23801 return eventList
23803 @staticmethod
23804 def PrototypeList(config):
23805 # Prototype ID enum.
23806 descriptorsWithPrototype = config.getDescriptors(
23807 hasInterfacePrototypeObject=True
23809 descriptorsWithPrototype.extend(
23810 config.getDescriptors(hasOrdinaryObjectPrototype=True)
23812 descriptorsWithPrototype.sort(key=attrgetter("name"))
23813 protos = [d.name for d in descriptorsWithPrototype]
23814 idEnum = CGNamespacedEnum("id", "ID", ["_ID_Start"] + protos, [0, "_ID_Start"])
23815 idEnum = CGList([idEnum])
23817 def fieldSizeAssert(amount, jitInfoField, message):
23818 maxFieldValue = (
23819 "(uint64_t(1) << (sizeof(std::declval<JSJitInfo>().%s) * 8))"
23820 % jitInfoField
23822 return CGGeneric(
23823 define='static_assert(%s < %s, "%s");\n\n'
23824 % (amount, maxFieldValue, message)
23827 idEnum.append(
23828 fieldSizeAssert("id::_ID_Count", "protoID", "Too many prototypes!")
23831 # Wrap all of that in our namespaces.
23832 idEnum = CGNamespace.build(
23833 ["mozilla", "dom", "prototypes"], CGWrapper(idEnum, pre="\n")
23835 idEnum = CGWrapper(idEnum, post="\n")
23837 curr = CGList(
23839 CGGeneric(define="#include <stdint.h>\n"),
23840 CGGeneric(define="#include <type_traits>\n\n"),
23841 CGGeneric(define='#include "js/experimental/JitInfo.h"\n\n'),
23842 CGGeneric(define='#include "mozilla/dom/BindingNames.h"\n\n'),
23843 CGGeneric(define='#include "mozilla/dom/PrototypeList.h"\n\n'),
23844 idEnum,
23848 # Let things know the maximum length of the prototype chain.
23849 maxMacroName = "MAX_PROTOTYPE_CHAIN_LENGTH"
23850 maxMacro = CGGeneric(
23851 declare="#define " + maxMacroName + " " + str(config.maxProtoChainLength)
23853 curr.append(CGWrapper(maxMacro, post="\n\n"))
23854 curr.append(
23855 fieldSizeAssert(
23856 maxMacroName, "depth", "Some inheritance chain is too long!"
23860 # Constructor ID enum.
23861 constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)]
23862 idEnum = CGNamespacedEnum(
23863 "id",
23864 "ID",
23865 ["_ID_Start"] + constructors,
23866 ["prototypes::id::_ID_Count", "_ID_Start"],
23869 # Wrap all of that in our namespaces.
23870 idEnum = CGNamespace.build(
23871 ["mozilla", "dom", "constructors"], CGWrapper(idEnum, pre="\n")
23873 idEnum = CGWrapper(idEnum, post="\n")
23875 curr.append(idEnum)
23877 # Named properties object enum.
23878 namedPropertiesObjects = [
23879 d.name for d in config.getDescriptors(hasNamedPropertiesObject=True)
23881 idEnum = CGNamespacedEnum(
23882 "id",
23883 "ID",
23884 ["_ID_Start"] + namedPropertiesObjects,
23885 ["constructors::id::_ID_Count", "_ID_Start"],
23888 # Wrap all of that in our namespaces.
23889 idEnum = CGNamespace.build(
23890 ["mozilla", "dom", "namedpropertiesobjects"], CGWrapper(idEnum, pre="\n")
23892 idEnum = CGWrapper(idEnum, post="\n")
23894 curr.append(idEnum)
23896 traitsDecls = [
23897 CGGeneric(
23898 declare=dedent(
23900 template <prototypes::ID PrototypeID>
23901 struct PrototypeTraits;
23906 traitsDecls.extend(CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype)
23908 ifaceNamesWithProto = [
23909 d.interface.getClassName() for d in descriptorsWithPrototype
23911 traitsDecls.append(
23912 CGStringTable("NamesOfInterfacesWithProtos", ifaceNamesWithProto)
23915 traitsDecl = CGNamespace.build(["mozilla", "dom"], CGList(traitsDecls))
23917 curr.append(traitsDecl)
23919 # Add include guards.
23920 curr = CGIncludeGuard("PrototypeList", curr)
23922 # Add the auto-generated comment.
23923 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
23925 # Done.
23926 return curr
23928 @staticmethod
23929 def BindingNames(config):
23930 declare = fill(
23932 enum class BindingNamesOffset : uint16_t {
23933 $*{enumValues}
23936 namespace binding_detail {
23937 extern const char sBindingNames[];
23938 } // namespace binding_detail
23940 MOZ_ALWAYS_INLINE const char* BindingName(BindingNamesOffset aOffset) {
23941 return binding_detail::sBindingNames + static_cast<size_t>(aOffset);
23943 """,
23944 enumValues="".join(
23945 "%s = %i,\n" % (BindingNamesOffsetEnum(n), o)
23946 for (n, o) in config.namesStringOffsets
23949 define = fill(
23951 namespace binding_detail {
23953 const char sBindingNames[] = {
23954 $*{namesString}
23957 } // namespace binding_detail
23959 // Making this enum bigger than a uint16_t has consequences on the size
23960 // of some structs (eg. WebIDLNameTableEntry) and tables. We should try
23961 // to avoid that.
23962 static_assert(EnumTypeFitsWithin<BindingNamesOffset, uint16_t>::value,
23963 "Size increase");
23964 """,
23965 namesString=' "\\0"\n'.join(
23966 '/* %5i */ "%s"' % (o, n) for (n, o) in config.namesStringOffsets
23968 + "\n",
23971 curr = CGGeneric(declare=declare, define=define)
23972 curr = CGWrapper(curr, pre="\n", post="\n")
23974 curr = CGNamespace.build(["mozilla", "dom"], curr)
23975 curr = CGWrapper(curr, post="\n")
23977 curr = CGHeaders(
23982 ["<stddef.h>", "<stdint.h>", "mozilla/Attributes.h"],
23983 ["mozilla/dom/BindingNames.h", "mozilla/EnumTypeTraits.h"],
23984 "BindingNames",
23985 curr,
23988 # Add include guards.
23989 curr = CGIncludeGuard("BindingNames", curr)
23991 # Done.
23992 return curr
23994 @staticmethod
23995 def RegisterBindings(config):
23996 curr = CGNamespace.build(
23997 ["mozilla", "dom"], CGGlobalNames(config.windowGlobalNames)
23999 curr = CGWrapper(curr, post="\n")
24001 # Add the includes
24002 defineIncludes = [
24003 CGHeaders.getDeclarationFilename(desc.interface)
24004 for desc in config.getDescriptors(
24005 hasInterfaceObject=True, isExposedInWindow=True, register=True
24008 defineIncludes.append("mozilla/dom/BindingNames.h")
24009 defineIncludes.append("mozilla/dom/WebIDLGlobalNameHash.h")
24010 defineIncludes.append("mozilla/dom/PrototypeList.h")
24011 defineIncludes.append("mozilla/PerfectHash.h")
24012 defineIncludes.append("js/String.h")
24013 curr = CGHeaders([], [], [], [], [], defineIncludes, "RegisterBindings", curr)
24015 # Add include guards.
24016 curr = CGIncludeGuard("RegisterBindings", curr)
24018 # Done.
24019 return curr
24021 @staticmethod
24022 def RegisterWorkerBindings(config):
24023 curr = CGRegisterWorkerBindings(config)
24025 # Wrap all of that in our namespaces.
24026 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
24027 curr = CGWrapper(curr, post="\n")
24029 # Add the includes
24030 defineIncludes = [
24031 CGHeaders.getDeclarationFilename(desc.interface)
24032 for desc in config.getDescriptors(
24033 hasInterfaceObject=True, register=True, isExposedInAnyWorker=True
24037 curr = CGHeaders(
24038 [], [], [], [], [], defineIncludes, "RegisterWorkerBindings", curr
24041 # Add include guards.
24042 curr = CGIncludeGuard("RegisterWorkerBindings", curr)
24044 # Done.
24045 return curr
24047 @staticmethod
24048 def RegisterWorkerDebuggerBindings(config):
24049 curr = CGRegisterWorkerDebuggerBindings(config)
24051 # Wrap all of that in our namespaces.
24052 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
24053 curr = CGWrapper(curr, post="\n")
24055 # Add the includes
24056 defineIncludes = [
24057 CGHeaders.getDeclarationFilename(desc.interface)
24058 for desc in config.getDescriptors(
24059 hasInterfaceObject=True, register=True, isExposedInWorkerDebugger=True
24063 curr = CGHeaders(
24064 [], [], [], [], [], defineIncludes, "RegisterWorkerDebuggerBindings", curr
24067 # Add include guards.
24068 curr = CGIncludeGuard("RegisterWorkerDebuggerBindings", curr)
24070 # Done.
24071 return curr
24073 @staticmethod
24074 def RegisterWorkletBindings(config):
24075 curr = CGRegisterWorkletBindings(config)
24077 # Wrap all of that in our namespaces.
24078 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
24079 curr = CGWrapper(curr, post="\n")
24081 # Add the includes
24082 defineIncludes = [
24083 CGHeaders.getDeclarationFilename(desc.interface)
24084 for desc in config.getDescriptors(
24085 hasInterfaceObject=True, register=True, isExposedInAnyWorklet=True
24089 curr = CGHeaders(
24090 [], [], [], [], [], defineIncludes, "RegisterWorkletBindings", curr
24093 # Add include guards.
24094 curr = CGIncludeGuard("RegisterWorkletBindings", curr)
24096 # Done.
24097 return curr
24099 @staticmethod
24100 def RegisterShadowRealmBindings(config):
24101 curr = CGRegisterShadowRealmBindings(config)
24103 # Wrap all of that in our namespaces.
24104 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
24105 curr = CGWrapper(curr, post="\n")
24107 # Add the includes
24108 defineIncludes = [
24109 CGHeaders.getDeclarationFilename(desc.interface)
24110 for desc in config.getDescriptors(
24111 hasInterfaceObject=True, register=True, isExposedInShadowRealms=True
24115 curr = CGHeaders(
24116 [], [], [], [], [], defineIncludes, "RegisterShadowRealmBindings", curr
24119 # Add include guards.
24120 curr = CGIncludeGuard("RegisterShadowRealmBindings", curr)
24122 # Done.
24123 return curr
24125 @staticmethod
24126 def UnionTypes(config):
24127 unionTypes = UnionsForFile(config, None)
24129 includes,
24130 implincludes,
24131 declarations,
24132 traverseMethods,
24133 unlinkMethods,
24134 unionStructs,
24135 ) = UnionTypes(unionTypes, config)
24137 unionStructs = dependencySortDictionariesAndUnionsAndCallbacks(unionStructs)
24139 unions = CGList(
24140 traverseMethods
24141 + unlinkMethods
24142 + [CGUnionStruct(t, config) for t in unionStructs]
24143 + [CGUnionStruct(t, config, True) for t in unionStructs],
24144 "\n",
24147 includes.add("mozilla/OwningNonNull.h")
24148 includes.add("mozilla/dom/UnionMember.h")
24149 includes.add("mozilla/dom/BindingDeclarations.h")
24150 # BindingUtils.h is only needed for SetToObject.
24151 # If it stops being inlined or stops calling CallerSubsumes
24152 # both this bit and the bit in CGBindingRoot can be removed.
24153 includes.add("mozilla/dom/BindingUtils.h")
24155 # Wrap all of that in our namespaces.
24156 curr = CGNamespace.build(["mozilla", "dom"], unions)
24158 curr = CGWrapper(curr, post="\n")
24160 builder = ForwardDeclarationBuilder()
24161 for className, isStruct in declarations:
24162 builder.add(className, isStruct=isStruct)
24164 curr = CGList([builder.build(), curr], "\n")
24166 curr = CGHeaders([], [], [], [], includes, implincludes, "UnionTypes", curr)
24168 # Add include guards.
24169 curr = CGIncludeGuard("UnionTypes", curr)
24171 # Done.
24172 return curr
24174 @staticmethod
24175 def WebIDLPrefs(config):
24176 prefs = set()
24177 headers = set(["mozilla/dom/WebIDLPrefs.h"])
24178 for d in config.getDescriptors(hasInterfaceOrInterfacePrototypeObject=True):
24179 for m in d.interface.members:
24180 pref = PropertyDefiner.getStringAttr(m, "Pref")
24181 if pref:
24182 headers.add(prefHeader(pref))
24183 prefs.add((pref, prefIdentifier(pref)))
24184 prefs = sorted(prefs)
24185 declare = fill(
24187 enum class WebIDLPrefIndex : uint8_t {
24188 NoPref,
24189 $*{prefs}
24191 typedef bool (*WebIDLPrefFunc)();
24192 extern const WebIDLPrefFunc sWebIDLPrefs[${len}];
24193 """,
24194 prefs=",\n".join(map(lambda p: "// " + p[0] + "\n" + p[1], prefs)) + "\n",
24195 len=len(prefs) + 1,
24197 define = fill(
24199 const WebIDLPrefFunc sWebIDLPrefs[] = {
24200 nullptr,
24201 $*{prefs}
24203 """,
24204 prefs=",\n".join(
24205 map(lambda p: "// " + p[0] + "\nStaticPrefs::" + p[1], prefs)
24207 + "\n",
24209 prefFunctions = CGGeneric(declare=declare, define=define)
24211 # Wrap all of that in our namespaces.
24212 curr = CGNamespace.build(["mozilla", "dom"], prefFunctions)
24214 curr = CGWrapper(curr, post="\n")
24216 curr = CGHeaders([], [], [], [], [], headers, "WebIDLPrefs", curr)
24218 # Add include guards.
24219 curr = CGIncludeGuard("WebIDLPrefs", curr)
24221 # Done.
24222 return curr
24224 @staticmethod
24225 def WebIDLSerializable(config):
24226 # We need a declaration of StructuredCloneTags in the header.
24227 declareIncludes = set(
24229 "mozilla/dom/DOMJSClass.h",
24230 "mozilla/dom/StructuredCloneTags.h",
24231 "js/TypeDecls.h",
24234 defineIncludes = set(
24235 ["mozilla/dom/WebIDLSerializable.h", "mozilla/PerfectHash.h"]
24237 names = list()
24238 for d in config.getDescriptors(isSerializable=True):
24239 names.append(d.name)
24240 defineIncludes.add(CGHeaders.getDeclarationFilename(d.interface))
24242 if len(names) == 0:
24243 # We can't really create a PerfectHash out of this, but also there's
24244 # not much point to this file if we have no [Serializable] objects.
24245 # Just spit out an empty file.
24246 return CGIncludeGuard("WebIDLSerializable", CGGeneric(""))
24248 # If we had a lot of serializable things, it might be worth it to use a
24249 # PerfectHash here, or an array ordered by sctag value and binary
24250 # search. But setting those up would require knowing in this python
24251 # code the values of the various SCTAG_DOM_*. We could hardcode them
24252 # here and add static asserts that the values are right, or switch to
24253 # code-generating StructuredCloneTags.h or something. But in practice,
24254 # there's a pretty small number of serializable interfaces, and just
24255 # doing a linear walk is fine. It's not obviously worse than the
24256 # if-cascade we used to have. Let's just make sure we notice if we do
24257 # end up with a lot of serializable things here.
24259 # Also, in practice it looks like compilers compile this linear walk to
24260 # an out-of-bounds check followed by a direct index into an array, by
24261 # basically making a second copy of this array ordered by tag, with the
24262 # holes filled in. Again, worth checking whether this still happens if
24263 # we have too many serializable things.
24264 if len(names) > 20:
24265 raise TypeError(
24266 "We now have %s serializable interfaces. "
24267 "Double-check that the compiler is still "
24268 "generating a jump table." % len(names)
24271 entries = list()
24272 # Make sure we have stable ordering.
24273 for name in sorted(names):
24274 exposedGlobals = computeGlobalNamesFromExposureSet(d.interface.exposureSet)
24275 # Strip off trailing newline to make our formatting look right.
24276 entries.append(
24277 fill(
24280 /* mTag */ ${tag},
24281 /* mDeserialize */ ${name}_Binding::Deserialize,
24282 /* mExposedGlobals */ ${exposedGlobals},
24284 """,
24285 tag=StructuredCloneTag(name),
24286 name=name,
24287 exposedGlobals=exposedGlobals,
24288 )[:-1]
24291 declare = dedent(
24293 Maybe<std::pair<uint16_t, WebIDLDeserializer>> LookupDeserializer(StructuredCloneTags aTag);
24296 define = fill(
24298 struct WebIDLSerializableEntry {
24299 StructuredCloneTags mTag;
24300 WebIDLDeserializer mDeserialize;
24301 uint16_t mExposedGlobals;
24304 static const WebIDLSerializableEntry sEntries[] = {
24305 $*{entries}
24308 Maybe<std::pair<uint16_t, WebIDLDeserializer>> LookupDeserializer(StructuredCloneTags aTag) {
24309 for (auto& entry : sEntries) {
24310 if (entry.mTag == aTag) {
24311 return Some(std::pair(entry.mExposedGlobals, entry.mDeserialize));
24314 return Nothing();
24316 """,
24317 entries=",\n".join(entries) + "\n",
24320 code = CGGeneric(declare=declare, define=define)
24322 # Wrap all of that in our namespaces.
24323 curr = CGNamespace.build(["mozilla", "dom"], code)
24325 curr = CGWrapper(curr, post="\n")
24327 curr = CGHeaders(
24328 [], [], [], [], declareIncludes, defineIncludes, "WebIDLSerializable", curr
24331 # Add include guards.
24332 curr = CGIncludeGuard("WebIDLSerializable", curr)
24334 # Done.
24335 return curr
24338 # Code generator for simple events
24339 class CGEventGetter(CGNativeMember):
24340 def __init__(self, descriptor, attr):
24341 ea = descriptor.getExtendedAttributes(attr, getter=True)
24342 CGNativeMember.__init__(
24343 self,
24344 descriptor,
24345 attr,
24346 CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
24347 (attr.type, []),
24349 resultNotAddRefed=not attr.type.isSequence(),
24351 self.body = self.getMethodBody()
24353 def getArgs(self, returnType, argList):
24354 if "needsErrorResult" in self.extendedAttrs:
24355 raise TypeError("Event code generator does not support [Throws]!")
24356 if "canOOM" in self.extendedAttrs:
24357 raise TypeError("Event code generator does not support [CanOOM]!")
24358 if not self.member.isAttr():
24359 raise TypeError("Event code generator does not support methods")
24360 if self.member.isStatic():
24361 raise TypeError("Event code generators does not support static attributes")
24362 return CGNativeMember.getArgs(self, returnType, argList)
24364 def getMethodBody(self):
24365 type = self.member.type
24366 memberName = CGDictionary.makeMemberName(self.member.identifier.name)
24367 if (
24368 (type.isPrimitive() and type.tag() in builtinNames)
24369 or type.isEnum()
24370 or type.isPromise()
24371 or type.isGeckoInterface()
24373 return "return " + memberName + ";\n"
24374 if type.isJSString():
24375 # https://bugzilla.mozilla.org/show_bug.cgi?id=1580167
24376 raise TypeError("JSString not supported as member of a generated event")
24377 if (
24378 type.isDOMString()
24379 or type.isByteString()
24380 or type.isUSVString()
24381 or type.isUTF8String()
24383 return "aRetVal = " + memberName + ";\n"
24384 if type.isSpiderMonkeyInterface() or type.isObject():
24385 return fill(
24387 if (${memberName}) {
24388 JS::ExposeObjectToActiveJS(${memberName});
24390 aRetVal.set(${memberName});
24391 return;
24392 """,
24393 memberName=memberName,
24395 if type.isAny():
24396 return fill(
24398 ${selfName}(aRetVal);
24399 """,
24400 selfName=self.name,
24402 if type.isUnion():
24403 return "aRetVal = " + memberName + ";\n"
24404 if type.isSequence():
24405 if type.nullable():
24406 return (
24407 "if ("
24408 + memberName
24409 + ".IsNull()) { aRetVal.SetNull(); } else { aRetVal.SetValue("
24410 + memberName
24411 + ".Value().Clone()); }\n"
24413 else:
24414 return "aRetVal = " + memberName + ".Clone();\n"
24415 if type.isDictionary():
24416 return "aRetVal = " + memberName + ";\n"
24417 raise TypeError("Event code generator does not support this type!")
24419 def declare(self, cgClass):
24420 if (
24421 getattr(self.member, "originatingInterface", cgClass.descriptor.interface)
24422 != cgClass.descriptor.interface
24424 return ""
24425 return CGNativeMember.declare(self, cgClass)
24427 def define(self, cgClass):
24428 if (
24429 getattr(self.member, "originatingInterface", cgClass.descriptor.interface)
24430 != cgClass.descriptor.interface
24432 return ""
24433 return CGNativeMember.define(self, cgClass)
24436 class CGEventSetter(CGNativeMember):
24437 def __init__(self):
24438 raise TypeError("Event code generator does not support setters!")
24441 class CGEventMethod(CGNativeMember):
24442 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
24443 self.isInit = False
24445 CGNativeMember.__init__(
24446 self,
24447 descriptor,
24448 method,
24449 CGSpecializedMethod.makeNativeName(descriptor, method),
24450 signature,
24451 descriptor.getExtendedAttributes(method),
24452 breakAfter=breakAfter,
24453 variadicIsSequence=True,
24455 self.originalArgs = list(self.args)
24457 iface = descriptor.interface
24458 allowed = isConstructor
24459 if not allowed and iface.getExtendedAttribute("LegacyEventInit"):
24460 # Allow it, only if it fits the initFooEvent profile exactly
24461 # We could check the arg types but it's not worth the effort.
24462 if (
24463 method.identifier.name == "init" + iface.identifier.name
24464 and signature[1][0].type.isDOMString()
24465 and signature[1][1].type.isBoolean()
24466 and signature[1][2].type.isBoolean()
24468 # -3 on the left to ignore the type, bubbles, and cancelable parameters
24469 # -1 on the right to ignore the .trusted property which bleeds through
24470 # here because it is [Unforgeable].
24471 len(signature[1]) - 3
24472 == len([x for x in iface.members if x.isAttr()]) - 1
24474 allowed = True
24475 self.isInit = True
24477 if not allowed:
24478 raise TypeError("Event code generator does not support methods!")
24480 def getArgs(self, returnType, argList):
24481 args = [self.getArg(arg) for arg in argList]
24482 return args
24484 def getArg(self, arg):
24485 decl, ref = self.getArgType(
24486 arg.type, arg.canHaveMissingValue(), "Variadic" if arg.variadic else False
24488 if ref:
24489 decl = CGWrapper(decl, pre="const ", post="&")
24491 name = arg.identifier.name
24492 name = "a" + name[0].upper() + name[1:]
24493 return Argument(decl.define(), name)
24495 def declare(self, cgClass):
24496 if self.isInit:
24497 constructorForNativeCaller = ""
24498 else:
24499 self.args = list(self.originalArgs)
24500 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
24501 constructorForNativeCaller = CGNativeMember.declare(self, cgClass)
24503 self.args = list(self.originalArgs)
24504 if needCx(None, self.arguments(), [], considerTypes=True, static=True):
24505 self.args.insert(0, Argument("JSContext*", "aCx"))
24506 if not self.isInit:
24507 self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
24509 return constructorForNativeCaller + CGNativeMember.declare(self, cgClass)
24511 def defineInit(self, cgClass):
24512 iface = self.descriptorProvider.interface
24513 members = ""
24514 while iface.identifier.name != "Event":
24515 i = 3 # Skip the boilerplate args: type, bubble,s cancelable.
24516 for m in iface.members:
24517 if m.isAttr():
24518 # We need to initialize all the member variables that do
24519 # not come from Event.
24520 if (
24521 getattr(m, "originatingInterface", iface).identifier.name
24522 == "Event"
24524 continue
24525 name = CGDictionary.makeMemberName(m.identifier.name)
24526 members += "%s = %s;\n" % (name, self.args[i].name)
24527 i += 1
24528 iface = iface.parent
24530 self.body = fill(
24532 InitEvent(${typeArg}, ${bubblesArg}, ${cancelableArg});
24533 ${members}
24534 """,
24535 typeArg=self.args[0].name,
24536 bubblesArg=self.args[1].name,
24537 cancelableArg=self.args[2].name,
24538 members=members,
24541 return CGNativeMember.define(self, cgClass)
24543 def define(self, cgClass):
24544 self.args = list(self.originalArgs)
24545 if self.isInit:
24546 return self.defineInit(cgClass)
24547 members = ""
24548 holdJS = ""
24549 iface = self.descriptorProvider.interface
24550 while iface.identifier.name != "Event":
24551 for m in self.descriptorProvider.getDescriptor(
24552 iface.identifier.name
24553 ).interface.members:
24554 if m.isAttr():
24555 # We initialize all the other member variables in the
24556 # Constructor except those ones coming from the Event.
24557 if (
24558 getattr(
24559 m, "originatingInterface", cgClass.descriptor.interface
24560 ).identifier.name
24561 == "Event"
24563 continue
24564 name = CGDictionary.makeMemberName(m.identifier.name)
24565 if m.type.isSequence():
24566 # For sequences we may not be able to do a simple
24567 # assignment because the underlying types may not match.
24568 # For example, the argument can be a
24569 # Sequence<OwningNonNull<SomeInterface>> while our
24570 # member is an nsTArray<RefPtr<SomeInterface>>. So
24571 # use AppendElements, which is actually a template on
24572 # the incoming type on nsTArray and does the right thing
24573 # for this case.
24574 target = name
24575 source = "%s.%s" % (self.args[1].name, name)
24576 sequenceCopy = "e->%s.AppendElements(%s);\n"
24577 if m.type.nullable():
24578 sequenceCopy = CGIfWrapper(
24579 CGGeneric(sequenceCopy), "!%s.IsNull()" % source
24580 ).define()
24581 target += ".SetValue()"
24582 source += ".Value()"
24583 members += sequenceCopy % (target, source)
24584 elif m.type.isSpiderMonkeyInterface():
24585 srcname = "%s.%s" % (self.args[1].name, name)
24586 if m.type.nullable():
24587 members += fill(
24589 if (${srcname}.IsNull()) {
24590 e->${varname} = nullptr;
24591 } else {
24592 e->${varname} = ${srcname}.Value().Obj();
24594 """,
24595 varname=name,
24596 srcname=srcname,
24598 else:
24599 members += fill(
24601 e->${varname}.set(${srcname}.Obj());
24602 """,
24603 varname=name,
24604 srcname=srcname,
24606 else:
24607 members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name)
24608 if (
24609 m.type.isAny()
24610 or m.type.isObject()
24611 or m.type.isSpiderMonkeyInterface()
24613 holdJS = "mozilla::HoldJSObjects(e.get());\n"
24614 iface = iface.parent
24616 self.body = fill(
24618 RefPtr<${nativeType}> e = new ${nativeType}(aOwner);
24619 bool trusted = e->Init(aOwner);
24620 e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable);
24621 $*{members}
24622 e->SetTrusted(trusted);
24623 e->SetComposed(${eventInit}.mComposed);
24624 $*{holdJS}
24625 return e.forget();
24626 """,
24627 nativeType=self.descriptorProvider.nativeType.split("::")[-1],
24628 eventType=self.args[0].name,
24629 eventInit=self.args[1].name,
24630 members=members,
24631 holdJS=holdJS,
24634 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
24635 constructorForNativeCaller = CGNativeMember.define(self, cgClass) + "\n"
24636 self.args = list(self.originalArgs)
24637 self.body = fill(
24639 nsCOMPtr<mozilla::dom::EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
24640 return Constructor(owner, ${arg0}, ${arg1});
24641 """,
24642 arg0=self.args[0].name,
24643 arg1=self.args[1].name,
24645 if needCx(None, self.arguments(), [], considerTypes=True, static=True):
24646 self.args.insert(0, Argument("JSContext*", "aCx"))
24647 self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
24648 return constructorForNativeCaller + CGNativeMember.define(self, cgClass)
24651 class CGEventClass(CGBindingImplClass):
24653 Codegen for the actual Event class implementation for this descriptor
24656 def __init__(self, descriptor):
24657 CGBindingImplClass.__init__(
24658 self,
24659 descriptor,
24660 CGEventMethod,
24661 CGEventGetter,
24662 CGEventSetter,
24663 False,
24664 "WrapObjectInternal",
24666 members = []
24667 extraMethods = []
24668 self.membersNeedingCC = []
24669 self.membersNeedingTrace = []
24671 for m in descriptor.interface.members:
24672 if (
24673 getattr(m, "originatingInterface", descriptor.interface)
24674 != descriptor.interface
24676 continue
24678 if m.isAttr():
24679 if m.type.isAny():
24680 self.membersNeedingTrace.append(m)
24681 # Add a getter that doesn't need a JSContext. Note that we
24682 # don't need to do this if our originating interface is not
24683 # the descriptor's interface, because in that case we
24684 # wouldn't generate the getter that _does_ need a JSContext
24685 # either.
24686 extraMethods.append(
24687 ClassMethod(
24688 CGSpecializedGetterCommon.makeNativeName(descriptor, m),
24689 "void",
24690 [Argument("JS::MutableHandle<JS::Value>", "aRetVal")],
24691 const=True,
24692 body=fill(
24694 JS::ExposeValueToActiveJS(${memberName});
24695 aRetVal.set(${memberName});
24696 """,
24697 memberName=CGDictionary.makeMemberName(
24698 m.identifier.name
24703 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
24704 self.membersNeedingTrace.append(m)
24705 elif typeNeedsRooting(m.type):
24706 raise TypeError(
24707 "Need to implement tracing for event member of type %s" % m.type
24709 elif idlTypeNeedsCycleCollection(m.type):
24710 self.membersNeedingCC.append(m)
24712 nativeType = self.getNativeTypeForIDLType(m.type).define()
24713 members.append(
24714 ClassMember(
24715 CGDictionary.makeMemberName(m.identifier.name),
24716 nativeType,
24717 visibility="private",
24718 body="body",
24722 parent = self.descriptor.interface.parent
24723 self.parentType = self.descriptor.getDescriptor(
24724 parent.identifier.name
24725 ).nativeType.split("::")[-1]
24726 self.nativeType = self.descriptor.nativeType.split("::")[-1]
24728 if self.needCC():
24729 isupportsDecl = fill(
24731 NS_DECL_ISUPPORTS_INHERITED
24732 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(${nativeType}, ${parentType})
24733 """,
24734 nativeType=self.nativeType,
24735 parentType=self.parentType,
24737 else:
24738 isupportsDecl = fill(
24740 NS_INLINE_DECL_REFCOUNTING_INHERITED(${nativeType}, ${parentType})
24741 """,
24742 nativeType=self.nativeType,
24743 parentType=self.parentType,
24746 baseDeclarations = fill(
24748 public:
24749 $*{isupportsDecl}
24751 protected:
24752 virtual ~${nativeType}();
24753 explicit ${nativeType}(mozilla::dom::EventTarget* aOwner);
24755 """,
24756 isupportsDecl=isupportsDecl,
24757 nativeType=self.nativeType,
24758 parentType=self.parentType,
24761 className = self.nativeType
24762 asConcreteTypeMethod = ClassMethod(
24763 "As%s" % className,
24764 "%s*" % className,
24766 virtual=True,
24767 body="return this;\n",
24768 breakAfterReturnDecl=" ",
24769 override=True,
24771 extraMethods.append(asConcreteTypeMethod)
24773 CGClass.__init__(
24774 self,
24775 className,
24776 bases=[ClassBase(self.parentType)],
24777 methods=extraMethods + self.methodDecls,
24778 members=members,
24779 extradeclarations=baseDeclarations,
24782 def getWrapObjectBody(self):
24783 return (
24784 "return %s_Binding::Wrap(aCx, this, aGivenProto);\n" % self.descriptor.name
24787 def needCC(self):
24788 return len(self.membersNeedingCC) != 0 or len(self.membersNeedingTrace) != 0
24790 def implTraverse(self):
24791 retVal = ""
24792 for m in self.membersNeedingCC:
24793 retVal += (
24794 " NS_IMPL_CYCLE_COLLECTION_TRAVERSE(%s)\n"
24795 % CGDictionary.makeMemberName(m.identifier.name)
24797 return retVal
24799 def implUnlink(self):
24800 retVal = ""
24801 for m in self.membersNeedingCC:
24802 retVal += (
24803 " NS_IMPL_CYCLE_COLLECTION_UNLINK(%s)\n"
24804 % CGDictionary.makeMemberName(m.identifier.name)
24806 for m in self.membersNeedingTrace:
24807 name = CGDictionary.makeMemberName(m.identifier.name)
24808 if m.type.isAny():
24809 retVal += " tmp->" + name + ".setUndefined();\n"
24810 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
24811 retVal += " tmp->" + name + " = nullptr;\n"
24812 else:
24813 raise TypeError("Unknown traceable member type %s" % m.type)
24814 return retVal
24816 def implTrace(self):
24817 retVal = ""
24818 for m in self.membersNeedingTrace:
24819 retVal += (
24820 " NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(%s)\n"
24821 % CGDictionary.makeMemberName(m.identifier.name)
24823 return retVal
24825 def define(self):
24826 for m in self.membersNeedingTrace:
24827 if not (
24828 m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface()
24830 raise TypeError("Unknown traceable member type %s" % m.type)
24832 if len(self.membersNeedingTrace) > 0:
24833 dropJS = "mozilla::DropJSObjects(this);\n"
24834 else:
24835 dropJS = ""
24836 # Just override CGClass and do our own thing
24837 ctorParams = (
24838 "aOwner, nullptr, nullptr" if self.parentType == "Event" else "aOwner"
24841 if self.needCC():
24842 classImpl = fill(
24845 NS_IMPL_CYCLE_COLLECTION_CLASS(${nativeType})
24847 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
24848 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
24850 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(${nativeType}, ${parentType})
24851 $*{traverse}
24852 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
24854 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(${nativeType}, ${parentType})
24855 $*{trace}
24856 NS_IMPL_CYCLE_COLLECTION_TRACE_END
24858 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(${nativeType}, ${parentType})
24859 $*{unlink}
24860 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
24862 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
24863 NS_INTERFACE_MAP_END_INHERITING(${parentType})
24864 """,
24865 nativeType=self.nativeType,
24866 parentType=self.parentType,
24867 traverse=self.implTraverse(),
24868 unlink=self.implUnlink(),
24869 trace=self.implTrace(),
24871 else:
24872 classImpl = ""
24874 classImpl += fill(
24877 ${nativeType}::${nativeType}(mozilla::dom::EventTarget* aOwner)
24878 : ${parentType}(${ctorParams})
24882 ${nativeType}::~${nativeType}()
24884 $*{dropJS}
24887 """,
24888 nativeType=self.nativeType,
24889 ctorParams=ctorParams,
24890 parentType=self.parentType,
24891 dropJS=dropJS,
24894 return classImpl + CGBindingImplClass.define(self)
24896 def getNativeTypeForIDLType(self, type):
24897 if type.isPrimitive() and type.tag() in builtinNames:
24898 nativeType = CGGeneric(builtinNames[type.tag()])
24899 if type.nullable():
24900 nativeType = CGTemplatedType("Nullable", nativeType)
24901 elif type.isEnum():
24902 nativeType = CGGeneric(type.unroll().inner.identifier.name)
24903 if type.nullable():
24904 nativeType = CGTemplatedType("Nullable", nativeType)
24905 elif type.isJSString():
24906 nativeType = CGGeneric("JS::Heap<JSString*>")
24907 elif type.isDOMString() or type.isUSVString():
24908 nativeType = CGGeneric("nsString")
24909 elif type.isByteString() or type.isUTF8String():
24910 nativeType = CGGeneric("nsCString")
24911 elif type.isPromise():
24912 nativeType = CGGeneric("RefPtr<Promise>")
24913 elif type.isDictionary():
24914 if typeNeedsRooting(type):
24915 raise TypeError(
24916 "We don't support event members that are dictionary types "
24917 "that need rooting (%s)" % type
24919 nativeType = CGGeneric(CGDictionary.makeDictionaryName(type.unroll().inner))
24920 if type.nullable():
24921 nativeType = CGTemplatedType("Nullable", nativeType)
24922 elif type.isGeckoInterface():
24923 iface = type.unroll().inner
24924 nativeType = self.descriptor.getDescriptor(iface.identifier.name).nativeType
24925 # Now trim off unnecessary namespaces
24926 nativeType = nativeType.split("::")
24927 if nativeType[0] == "mozilla":
24928 nativeType.pop(0)
24929 if nativeType[0] == "dom":
24930 nativeType.pop(0)
24931 nativeType = CGWrapper(
24932 CGGeneric("::".join(nativeType)), pre="RefPtr<", post=">"
24934 elif type.isAny():
24935 nativeType = CGGeneric("JS::Heap<JS::Value>")
24936 elif type.isObject() or type.isSpiderMonkeyInterface():
24937 nativeType = CGGeneric("JS::Heap<JSObject*>")
24938 elif type.isUnion():
24939 nativeType = CGGeneric(CGUnionStruct.unionTypeDecl(type, True))
24940 elif type.isSequence():
24941 if type.nullable():
24942 innerType = type.inner.inner
24943 else:
24944 innerType = type.inner
24945 if (
24946 not innerType.isPrimitive()
24947 and not innerType.isEnum()
24948 and not innerType.isDOMString()
24949 and not innerType.isByteString()
24950 and not innerType.isUTF8String()
24951 and not innerType.isPromise()
24952 and not innerType.isGeckoInterface()
24954 raise TypeError(
24955 "Don't know how to properly manage GC/CC for "
24956 "event member of type %s" % type
24958 nativeType = CGTemplatedType(
24959 "nsTArray", self.getNativeTypeForIDLType(innerType)
24961 if type.nullable():
24962 nativeType = CGTemplatedType("Nullable", nativeType)
24963 else:
24964 raise TypeError("Don't know how to declare event member of type %s" % type)
24965 return nativeType
24968 class CGEventRoot(CGThing):
24969 def __init__(self, config, interfaceName):
24970 descriptor = config.getDescriptor(interfaceName)
24972 self.root = CGWrapper(CGEventClass(descriptor), pre="\n", post="\n")
24974 self.root = CGNamespace.build(["mozilla", "dom"], self.root)
24976 self.root = CGList(
24977 [CGClassForwardDeclare("JSContext", isStruct=True), self.root]
24980 parent = descriptor.interface.parent.identifier.name
24982 # Throw in our #includes
24983 self.root = CGHeaders(
24984 [descriptor],
24989 config.getDescriptor(parent).headerFile,
24990 "mozilla/Attributes.h",
24991 "mozilla/dom/%sBinding.h" % interfaceName,
24992 "mozilla/dom/BindingUtils.h",
24995 "%s.h" % interfaceName,
24996 "js/GCAPI.h",
24997 "mozilla/HoldDropJSObjects.h",
24998 "mozilla/dom/Nullable.h",
25001 self.root,
25002 config,
25005 # And now some include guards
25006 self.root = CGIncludeGuard(interfaceName, self.root)
25008 self.root = CGWrapper(
25009 self.root,
25010 pre=(
25011 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT
25012 % os.path.basename(descriptor.interface.filename)
25016 self.root = CGWrapper(
25017 self.root,
25018 pre=dedent(
25020 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
25021 /* vim:set ts=2 sw=2 sts=2 et cindent: */
25022 /* This Source Code Form is subject to the terms of the Mozilla Public
25023 * License, v. 2.0. If a copy of the MPL was not distributed with this
25024 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
25030 def declare(self):
25031 return self.root.declare()
25033 def define(self):
25034 return self.root.define()