Bug 1874684 - Part 4: Prefer const references instead of copying Instant values....
[gecko.git] / dom / bindings / Codegen.py
blob72870524085909a3bb7b95327971c3f15ff6581f
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 import os
10 import re
11 import string
12 import textwrap
14 from Configuration import (
15 Descriptor,
16 MemberIsLegacyUnforgeable,
17 NoSuchDescriptorError,
18 getAllTypes,
19 getTypesFromCallback,
20 getTypesFromDescriptor,
21 getTypesFromDictionary,
23 from perfecthash import PerfectHash
24 from WebIDL import (
25 BuiltinTypes,
26 IDLAttribute,
27 IDLBuiltinType,
28 IDLDefaultDictionaryValue,
29 IDLDictionary,
30 IDLEmptySequenceValue,
31 IDLInterfaceMember,
32 IDLNullValue,
33 IDLSequenceType,
34 IDLType,
35 IDLUndefinedValue,
38 AUTOGENERATED_WARNING_COMMENT = (
39 "/* THIS FILE IS AUTOGENERATED BY Codegen.py - DO NOT EDIT */\n\n"
41 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT = (
42 "/* THIS FILE IS AUTOGENERATED FROM %s BY Codegen.py - DO NOT EDIT */\n\n"
44 ADDPROPERTY_HOOK_NAME = "_addProperty"
45 GETWRAPPERCACHE_HOOK_NAME = "_getWrapperCache"
46 FINALIZE_HOOK_NAME = "_finalize"
47 OBJECT_MOVED_HOOK_NAME = "_objectMoved"
48 CONSTRUCT_HOOK_NAME = "_constructor"
49 LEGACYCALLER_HOOK_NAME = "_legacycaller"
50 RESOLVE_HOOK_NAME = "_resolve"
51 MAY_RESOLVE_HOOK_NAME = "_mayResolve"
52 NEW_ENUMERATE_HOOK_NAME = "_newEnumerate"
53 INSTANCE_RESERVED_SLOTS = 1
55 # This size is arbitrary. It is a power of 2 to make using it as a modulo
56 # operand cheap, and is usually around 1/3-1/5th of the set size (sometimes
57 # smaller for very large sets).
58 GLOBAL_NAMES_PHF_SIZE = 256
61 def memberReservedSlot(member, descriptor):
62 return (
63 "(DOM_INSTANCE_RESERVED_SLOTS + %d)"
64 % member.slotIndices[descriptor.interface.identifier.name]
68 def memberXrayExpandoReservedSlot(member, descriptor):
69 return (
70 "(xpc::JSSLOT_EXPANDO_COUNT + %d)"
71 % member.slotIndices[descriptor.interface.identifier.name]
75 def mayUseXrayExpandoSlots(descriptor, attr):
76 assert not attr.getExtendedAttribute("NewObject")
77 # For attributes whose type is a Gecko interface we always use
78 # slots on the reflector for caching. Also, for interfaces that
79 # don't want Xrays we obviously never use the Xray expando slot.
80 return descriptor.wantsXrays and not attr.type.isGeckoInterface()
83 def toStringBool(arg):
84 """
85 Converts IDL/Python Boolean (True/False) to C++ Boolean (true/false)
86 """
87 return str(not not arg).lower()
90 def toBindingNamespace(arg):
91 return arg + "_Binding"
94 def isTypeCopyConstructible(type):
95 # Nullable and sequence stuff doesn't affect copy-constructibility
96 type = type.unroll()
97 return (
98 type.isUndefined()
99 or type.isPrimitive()
100 or type.isString()
101 or type.isEnum()
102 or (type.isUnion() and CGUnionStruct.isUnionCopyConstructible(type))
103 or (
104 type.isDictionary()
105 and CGDictionary.isDictionaryCopyConstructible(type.inner)
108 # Interface types are only copy-constructible if they're Gecko
109 # interfaces. SpiderMonkey interfaces are not copy-constructible
110 # because of rooting issues.
111 (type.isInterface() and type.isGeckoInterface())
115 class CycleCollectionUnsupported(TypeError):
116 def __init__(self, message):
117 TypeError.__init__(self, message)
120 def idlTypeNeedsCycleCollection(type):
121 type = type.unroll() # Takes care of sequences and nullables
122 if (
123 (type.isPrimitive() and type.tag() in builtinNames)
124 or type.isUndefined()
125 or type.isEnum()
126 or type.isString()
127 or type.isAny()
128 or type.isObject()
129 or type.isSpiderMonkeyInterface()
131 return False
132 elif type.isCallback() or type.isPromise() or type.isGeckoInterface():
133 return True
134 elif type.isUnion():
135 return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes)
136 elif type.isRecord():
137 if idlTypeNeedsCycleCollection(type.inner):
138 raise CycleCollectionUnsupported(
139 "Cycle collection for type %s is not supported" % type
141 return False
142 elif type.isDictionary():
143 return CGDictionary.dictionaryNeedsCycleCollection(type.inner)
144 else:
145 raise CycleCollectionUnsupported(
146 "Don't know whether to cycle-collect type %s" % type
150 def idlTypeNeedsCallContext(type, descriptor=None, allowTreatNonCallableAsNull=False):
152 Returns whether the given type needs error reporting via a
153 BindingCallContext for JS-to-C++ conversions. This will happen when the
154 conversion can throw an exception due to logic in the IDL spec or
155 Gecko-specific security checks. In particular, a type needs a
156 BindingCallContext if and only if the JS-to-C++ conversion for that type can
157 end up calling ThrowErrorMessage.
159 For some types this depends on the descriptor (e.g. because we do certain
160 checks only for some kinds of interfaces).
162 The allowTreatNonCallableAsNull optimization is there so we can avoid
163 generating an unnecessary BindingCallContext for all the event handler
164 attribute setters.
167 while True:
168 if type.isSequence():
169 # Sequences can always throw "not an object"
170 return True
171 if type.nullable():
172 # treatNonObjectAsNull() and treatNonCallableAsNull() are
173 # only sane things to test on nullable types, so do that now.
174 if (
175 allowTreatNonCallableAsNull
176 and type.isCallback()
177 and (type.treatNonObjectAsNull() or type.treatNonCallableAsNull())
179 # This can't throw. so never needs a method description.
180 return False
181 type = type.inner
182 else:
183 break
185 if type.isUndefined():
186 # Clearly doesn't need a method description; we can only get here from
187 # CGHeaders trying to decide whether to include the method description
188 # header.
189 return False
190 # The float check needs to come before the isPrimitive() check,
191 # because floats are primitives too.
192 if type.isFloat():
193 # Floats can throw if restricted.
194 return not type.isUnrestricted()
195 if type.isPrimitive() and type.tag() in builtinNames:
196 # Numbers can throw if enforcing range.
197 return type.hasEnforceRange()
198 if type.isEnum():
199 # Can throw on invalid value.
200 return True
201 if type.isString():
202 # Can throw if it's a ByteString
203 return type.isByteString()
204 if type.isAny():
205 # JS-implemented interfaces do extra security checks so need a
206 # method description here. If we have no descriptor, this
207 # might be JS-implemented thing, so it will do the security
208 # check and we need the method description.
209 return not descriptor or descriptor.interface.isJSImplemented()
210 if type.isPromise():
211 # JS-to-Promise conversion won't cause us to throw any
212 # specific exceptions, so does not need a method description.
213 return False
214 if (
215 type.isObject()
216 or type.isInterface()
217 or type.isCallback()
218 or type.isDictionary()
219 or type.isRecord()
220 or type.isObservableArray()
222 # These can all throw if a primitive is passed in, at the very least.
223 # There are some rare cases when we know we have an object, but those
224 # are not worth the complexity of optimizing for.
226 # Note that we checked the [LegacyTreatNonObjectAsNull] case already when
227 # unwrapping nullables.
228 return True
229 if type.isUnion():
230 # Can throw if a type not in the union is passed in.
231 return True
232 raise TypeError("Don't know whether type '%s' needs a method description" % type)
235 # TryPreserveWrapper uses the addProperty hook to preserve the wrapper of
236 # non-nsISupports cycle collected objects, so if wantsAddProperty is changed
237 # to not cover that case then TryPreserveWrapper will need to be changed.
238 def wantsAddProperty(desc):
239 return desc.concrete and desc.wrapperCache and not desc.isGlobal()
242 def wantsGetWrapperCache(desc):
243 return (
244 desc.concrete and desc.wrapperCache and not desc.isGlobal() and not desc.proxy
248 def indent(s, indentLevel=2):
250 Indent C++ code.
252 Weird secret feature: this doesn't indent lines that start with # (such as
253 #include lines or #ifdef/#endif).
256 # We'll want to insert the indent at the beginnings of lines, but we
257 # don't want to indent empty lines.
258 padding = indentLevel * " "
259 return "\n".join(
261 (padding + line) if line and line[0] != "#" else line
262 for line in s.split("\n")
267 # dedent() and fill() are often called on the same string multiple
268 # times. We want to memoize their return values so we don't keep
269 # recomputing them all the time.
270 def memoize(fn):
272 Decorator to memoize a function of one argument. The cache just
273 grows without bound.
275 cache = {}
277 @functools.wraps(fn)
278 def wrapper(arg):
279 retval = cache.get(arg)
280 if retval is None:
281 retval = cache[arg] = fn(arg)
282 return retval
284 return wrapper
287 @memoize
288 def dedent(s):
290 Remove all leading whitespace from s, and remove a blank line
291 at the beginning.
293 if s.startswith("\n"):
294 s = s[1:]
295 return textwrap.dedent(s)
298 # This works by transforming the fill()-template to an equivalent
299 # string.Template.
300 fill_multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?")
303 find_substitutions = re.compile(r"\${")
306 @memoize
307 def compile_fill_template(template):
309 Helper function for fill(). Given the template string passed to fill(),
310 do the reusable part of template processing and return a pair (t,
311 argModList) that can be used every time fill() is called with that
312 template argument.
314 argsModList is list of tuples that represent modifications to be
315 made to args. Each modification has, in order: i) the arg name,
316 ii) the modified name, iii) the indent depth.
318 t = dedent(template)
319 assert t.endswith("\n") or "\n" not in t
320 argModList = []
322 def replace(match):
324 Replaces a line like ' $*{xyz}\n' with '${xyz_n}',
325 where n is the indent depth, and add a corresponding entry to
326 argModList.
328 Note that this needs to close over argModList, so it has to be
329 defined inside compile_fill_template().
331 indentation, name, nl = match.groups()
332 depth = len(indentation)
334 # Check that $*{xyz} appears by itself on a line.
335 prev = match.string[: match.start()]
336 if (prev and not prev.endswith("\n")) or nl is None:
337 raise ValueError(
338 "Invalid fill() template: $*{%s} must appear by itself on a line" % name
341 # Now replace this whole line of template with the indented equivalent.
342 modified_name = name + "_" + str(depth)
343 argModList.append((name, modified_name, depth))
344 return "${" + modified_name + "}"
346 t = re.sub(fill_multiline_substitution_re, replace, t)
347 if not re.search(find_substitutions, t):
348 raise TypeError("Using fill() when dedent() would do.")
349 return (string.Template(t), argModList)
352 def fill(template, **args):
354 Convenience function for filling in a multiline template.
356 `fill(template, name1=v1, name2=v2)` is a lot like
357 `string.Template(template).substitute({"name1": v1, "name2": v2})`.
359 However, it's shorter, and has a few nice features:
361 * If `template` is indented, fill() automatically dedents it!
362 This makes code using fill() with Python's multiline strings
363 much nicer to look at.
365 * If `template` starts with a blank line, fill() strips it off.
366 (Again, convenient with multiline strings.)
368 * fill() recognizes a special kind of substitution
369 of the form `$*{name}`.
371 Use this to paste in, and automatically indent, multiple lines.
372 (Mnemonic: The `*` is for "multiple lines").
374 A `$*` substitution must appear by itself on a line, with optional
375 preceding indentation (spaces only). The whole line is replaced by the
376 corresponding keyword argument, indented appropriately. If the
377 argument is an empty string, no output is generated, not even a blank
378 line.
381 t, argModList = compile_fill_template(template)
382 # Now apply argModList to args
383 for name, modified_name, depth in argModList:
384 if not (args[name] == "" or args[name].endswith("\n")):
385 raise ValueError(
386 "Argument %s with value %r is missing a newline" % (name, args[name])
388 args[modified_name] = indent(args[name], depth)
390 return t.substitute(args)
393 class CGThing:
395 Abstract base class for things that spit out code.
398 def __init__(self):
399 pass # Nothing for now
401 def declare(self):
402 """Produce code for a header file."""
403 assert False # Override me!
405 def define(self):
406 """Produce code for a cpp file."""
407 assert False # Override me!
409 def deps(self):
410 """Produce the deps for a pp file"""
411 assert False # Override me!
414 class CGStringTable(CGThing):
416 Generate a function accessor for a WebIDL string table, using the existing
417 concatenated names string and mapping indexes to offsets in that string:
419 const char *accessorName(unsigned int index) {
420 static const uint16_t offsets = { ... };
421 return BindingName(offsets[index]);
424 This is more efficient than the more natural:
426 const char *table[] = {
430 The uint16_t offsets are smaller than the pointer equivalents, and the
431 concatenated string requires no runtime relocations.
434 def __init__(self, accessorName, strings, static=False):
435 CGThing.__init__(self)
436 self.accessorName = accessorName
437 self.strings = strings
438 self.static = static
440 def declare(self):
441 if self.static:
442 return ""
443 return "const char *%s(unsigned int aIndex);\n" % self.accessorName
445 def define(self):
446 offsets = []
447 for s in self.strings:
448 offsets.append(BindingNamesOffsetEnum(s))
449 return fill(
451 ${static}const char *${name}(unsigned int aIndex)
453 static const BindingNamesOffset offsets[] = {
454 $*{offsets}
456 return BindingName(offsets[aIndex]);
458 """,
459 static="static " if self.static else "",
460 name=self.accessorName,
461 offsets="".join("BindingNamesOffset::%s,\n" % o for o in offsets),
465 class CGNativePropertyHooks(CGThing):
467 Generate a NativePropertyHooks for a given descriptor
470 def __init__(self, descriptor, properties):
471 CGThing.__init__(self)
472 assert descriptor.wantsXrays
473 self.descriptor = descriptor
474 self.properties = properties
476 def declare(self):
477 return ""
479 def define(self):
480 if (
481 self.descriptor.concrete
482 and self.descriptor.proxy
483 and not self.descriptor.isMaybeCrossOriginObject()
485 if self.descriptor.needsXrayNamedDeleterHook():
486 deleteNamedProperty = "DeleteNamedProperty"
487 else:
488 deleteNamedProperty = "nullptr"
489 namedOrIndexed = fill(
491 const NativeNamedOrIndexedPropertyHooks sNativeNamedOrIndexedPropertyHooks = {
492 binding_detail::ResolveOwnProperty,
493 binding_detail::EnumerateOwnProperties,
494 ${deleteNamedProperty}
496 """,
497 deleteNamedProperty=deleteNamedProperty,
499 namedOrIndexedPointer = "&sNativeNamedOrIndexedPropertyHooks"
500 elif self.descriptor.needsXrayResolveHooks():
501 namedOrIndexed = dedent(
503 const NativeNamedOrIndexedPropertyHooks sNativeNamedOrIndexedPropertyHooks = {
504 ResolveOwnPropertyViaResolve,
505 EnumerateOwnPropertiesViaGetOwnPropertyNames,
506 nullptr
510 namedOrIndexedPointer = "&sNativeNamedOrIndexedPropertyHooks"
511 else:
512 namedOrIndexed = ""
513 namedOrIndexedPointer = "nullptr"
514 if self.properties.hasNonChromeOnly():
515 regular = "sNativeProperties.Upcast()"
516 else:
517 regular = "nullptr"
518 if self.properties.hasChromeOnly():
519 chrome = "sChromeOnlyNativeProperties.Upcast()"
520 else:
521 chrome = "nullptr"
522 constructorID = "constructors::id::"
523 if self.descriptor.interface.hasInterfaceObject():
524 constructorID += self.descriptor.name
525 else:
526 constructorID += "_ID_Count"
527 prototypeID = "prototypes::id::"
528 if self.descriptor.interface.hasInterfacePrototypeObject():
529 prototypeID += self.descriptor.name
530 else:
531 prototypeID += "_ID_Count"
533 if self.descriptor.wantsXrayExpandoClass:
534 expandoClass = "&sXrayExpandoObjectClass"
535 else:
536 expandoClass = "&DefaultXrayExpandoObjectClass"
538 return namedOrIndexed + fill(
540 bool sNativePropertiesInited = false;
541 const NativePropertyHooks sNativePropertyHooks = {
542 ${namedOrIndexedPointer},
543 { ${regular}, ${chrome}, &sNativePropertiesInited },
544 ${prototypeID},
545 ${constructorID},
546 ${expandoClass}
548 """,
549 namedOrIndexedPointer=namedOrIndexedPointer,
550 regular=regular,
551 chrome=chrome,
552 prototypeID=prototypeID,
553 constructorID=constructorID,
554 expandoClass=expandoClass,
558 def NativePropertyHooks(descriptor):
559 return (
560 "&sEmptyNativePropertyHooks"
561 if not descriptor.wantsXrays
562 else "&sNativePropertyHooks"
566 def DOMClass(descriptor):
567 protoList = ["prototypes::id::" + proto for proto in descriptor.prototypeNameChain]
568 # Pad out the list to the right length with _ID_Count so we
569 # guarantee that all the lists are the same length. _ID_Count
570 # is never the ID of any prototype, so it's safe to use as
571 # padding.
572 protoList.extend(
573 ["prototypes::id::_ID_Count"]
574 * (descriptor.config.maxProtoChainLength - len(protoList))
577 if descriptor.interface.isSerializable():
578 serializer = "Serialize"
579 else:
580 serializer = "nullptr"
582 if wantsGetWrapperCache(descriptor):
583 wrapperCacheGetter = GETWRAPPERCACHE_HOOK_NAME
584 else:
585 wrapperCacheGetter = "nullptr"
587 if descriptor.hasOrdinaryObjectPrototype:
588 getProto = "JS::GetRealmObjectPrototypeHandle"
589 else:
590 getProto = "GetProtoObjectHandle"
592 return fill(
594 { ${protoChain} },
595 std::is_base_of_v<nsISupports, ${nativeType}>,
596 ${hooks},
597 FindAssociatedGlobalForNative<${nativeType}>::Get,
598 ${getProto},
599 GetCCParticipant<${nativeType}>::Get(),
600 ${serializer},
601 ${wrapperCacheGetter}
602 """,
603 protoChain=", ".join(protoList),
604 nativeType=descriptor.nativeType,
605 hooks=NativePropertyHooks(descriptor),
606 serializer=serializer,
607 wrapperCacheGetter=wrapperCacheGetter,
608 getProto=getProto,
612 def InstanceReservedSlots(descriptor):
613 slots = INSTANCE_RESERVED_SLOTS + descriptor.interface.totalMembersInSlots
614 if descriptor.isMaybeCrossOriginObject():
615 # We need a slot for the cross-origin holder too.
616 if descriptor.interface.hasChildInterfaces():
617 raise TypeError(
618 "We don't support non-leaf cross-origin interfaces "
619 "like %s" % descriptor.interface.identifier.name
621 slots += 1
622 return slots
625 class CGDOMJSClass(CGThing):
627 Generate a DOMJSClass for a given descriptor
630 def __init__(self, descriptor):
631 CGThing.__init__(self)
632 self.descriptor = descriptor
634 def declare(self):
635 return ""
637 def define(self):
638 callHook = (
639 LEGACYCALLER_HOOK_NAME
640 if self.descriptor.operations["LegacyCaller"]
641 else "nullptr"
643 objectMovedHook = (
644 OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else "nullptr"
646 slotCount = InstanceReservedSlots(self.descriptor)
647 classFlags = "JSCLASS_IS_DOMJSCLASS | JSCLASS_FOREGROUND_FINALIZE | "
648 if self.descriptor.isGlobal():
649 classFlags += (
650 "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS)"
652 traceHook = "JS_GlobalObjectTraceHook"
653 reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
654 else:
655 classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
656 traceHook = "nullptr"
657 reservedSlots = slotCount
658 if self.descriptor.interface.hasProbablyShortLivingWrapper():
659 if not self.descriptor.wrapperCache:
660 raise TypeError(
661 "Need a wrapper cache to support nursery "
662 "allocation of DOM objects"
664 classFlags += " | JSCLASS_SKIP_NURSERY_FINALIZE"
666 if self.descriptor.interface.getExtendedAttribute("NeedResolve"):
667 resolveHook = RESOLVE_HOOK_NAME
668 mayResolveHook = MAY_RESOLVE_HOOK_NAME
669 newEnumerateHook = NEW_ENUMERATE_HOOK_NAME
670 elif self.descriptor.isGlobal():
671 resolveHook = "mozilla::dom::ResolveGlobal"
672 mayResolveHook = "mozilla::dom::MayResolveGlobal"
673 newEnumerateHook = "mozilla::dom::EnumerateGlobal"
674 else:
675 resolveHook = "nullptr"
676 mayResolveHook = "nullptr"
677 newEnumerateHook = "nullptr"
679 return fill(
681 static const JSClassOps sClassOps = {
682 ${addProperty}, /* addProperty */
683 nullptr, /* delProperty */
684 nullptr, /* enumerate */
685 ${newEnumerate}, /* newEnumerate */
686 ${resolve}, /* resolve */
687 ${mayResolve}, /* mayResolve */
688 ${finalize}, /* finalize */
689 ${call}, /* call */
690 nullptr, /* construct */
691 ${trace}, /* trace */
694 static const js::ClassExtension sClassExtension = {
695 ${objectMoved} /* objectMovedOp */
698 static const DOMJSClass sClass = {
699 { "${name}",
700 ${flags},
701 &sClassOps,
702 JS_NULL_CLASS_SPEC,
703 &sClassExtension,
704 JS_NULL_OBJECT_OPS
706 $*{descriptor}
708 static_assert(${instanceReservedSlots} == DOM_INSTANCE_RESERVED_SLOTS,
709 "Must have the right minimal number of reserved slots.");
710 static_assert(${reservedSlots} >= ${slotCount},
711 "Must have enough reserved slots.");
712 """,
713 name=self.descriptor.interface.getClassName(),
714 flags=classFlags,
715 addProperty=ADDPROPERTY_HOOK_NAME
716 if wantsAddProperty(self.descriptor)
717 else "nullptr",
718 newEnumerate=newEnumerateHook,
719 resolve=resolveHook,
720 mayResolve=mayResolveHook,
721 finalize=FINALIZE_HOOK_NAME,
722 call=callHook,
723 trace=traceHook,
724 objectMoved=objectMovedHook,
725 descriptor=DOMClass(self.descriptor),
726 instanceReservedSlots=INSTANCE_RESERVED_SLOTS,
727 reservedSlots=reservedSlots,
728 slotCount=slotCount,
732 class CGDOMProxyJSClass(CGThing):
734 Generate a DOMJSClass for a given proxy descriptor
737 def __init__(self, descriptor):
738 CGThing.__init__(self)
739 self.descriptor = descriptor
741 def declare(self):
742 return ""
744 def define(self):
745 slotCount = InstanceReservedSlots(self.descriptor)
746 # We need one reserved slot (DOM_OBJECT_SLOT).
747 flags = ["JSCLASS_IS_DOMJSCLASS", "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount]
748 # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because
749 # we don't want people ever adding that to any interface other than
750 # HTMLAllCollection. So just hardcode it here.
751 if self.descriptor.interface.identifier.name == "HTMLAllCollection":
752 flags.append("JSCLASS_EMULATES_UNDEFINED")
753 return fill(
755 static const DOMJSClass sClass = {
756 PROXY_CLASS_DEF("${name}",
757 ${flags}),
758 $*{descriptor}
760 """,
761 name=self.descriptor.interface.identifier.name,
762 flags=" | ".join(flags),
763 descriptor=DOMClass(self.descriptor),
767 class CGXrayExpandoJSClass(CGThing):
769 Generate a JSClass for an Xray expando object. This is only
770 needed if we have members in slots (for [Cached] or [StoreInSlot]
771 stuff).
774 def __init__(self, descriptor):
775 assert descriptor.interface.totalMembersInSlots != 0
776 assert descriptor.wantsXrays
777 assert descriptor.wantsXrayExpandoClass
778 CGThing.__init__(self)
779 self.descriptor = descriptor
781 def declare(self):
782 return ""
784 def define(self):
785 return fill(
787 // This may allocate too many slots, because we only really need
788 // slots for our non-interface-typed members that we cache. But
789 // allocating slots only for those would make the slot index
790 // computations much more complicated, so let's do this the simple
791 // way for now.
792 DEFINE_XRAY_EXPANDO_CLASS(static, sXrayExpandoObjectClass, ${memberSlots});
793 """,
794 memberSlots=self.descriptor.interface.totalMembersInSlots,
798 def PrototypeIDAndDepth(descriptor):
799 prototypeID = "prototypes::id::"
800 if descriptor.interface.hasInterfacePrototypeObject():
801 prototypeID += descriptor.interface.identifier.name
802 depth = "PrototypeTraits<%s>::Depth" % prototypeID
803 else:
804 prototypeID += "_ID_Count"
805 depth = "0"
806 return (prototypeID, depth)
809 def InterfacePrototypeObjectProtoGetter(descriptor):
811 Returns a tuple with two elements:
813 1) The name of the function to call to get the prototype to use for the
814 interface prototype object as a JSObject*.
816 2) The name of the function to call to get the prototype to use for the
817 interface prototype object as a JS::Handle<JSObject*> or None if no
818 such function exists.
820 parentProtoName = descriptor.parentPrototypeName
821 if descriptor.hasNamedPropertiesObject:
822 protoGetter = "GetNamedPropertiesObject"
823 protoHandleGetter = None
824 elif parentProtoName is None:
825 if descriptor.interface.getExtendedAttribute("ExceptionClass"):
826 protoGetter = "JS::GetRealmErrorPrototype"
827 elif descriptor.interface.isIteratorInterface():
828 protoGetter = "JS::GetRealmIteratorPrototype"
829 elif descriptor.interface.isAsyncIteratorInterface():
830 protoGetter = "JS::GetRealmAsyncIteratorPrototype"
831 else:
832 protoGetter = "JS::GetRealmObjectPrototype"
833 protoHandleGetter = None
834 else:
835 prefix = toBindingNamespace(parentProtoName)
836 protoGetter = prefix + "::GetProtoObject"
837 protoHandleGetter = prefix + "::GetProtoObjectHandle"
839 return (protoGetter, protoHandleGetter)
842 class CGPrototypeJSClass(CGThing):
843 def __init__(self, descriptor, properties):
844 CGThing.__init__(self)
845 self.descriptor = descriptor
846 self.properties = properties
848 def declare(self):
849 # We're purely for internal consumption
850 return ""
852 def define(self):
853 prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
854 slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE"
855 # Globals handle unforgeables directly in Wrap() instead of
856 # via a holder.
857 if (
858 self.descriptor.hasLegacyUnforgeableMembers
859 and not self.descriptor.isGlobal()
861 slotCount += (
862 " + 1 /* slot for the JSObject holding the unforgeable properties */"
864 (protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor)
865 type = (
866 "eGlobalInterfacePrototype"
867 if self.descriptor.isGlobal()
868 else "eInterfacePrototype"
870 return fill(
872 static const DOMIfaceAndProtoJSClass sPrototypeClass = {
874 "${name}Prototype",
875 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
876 JS_NULL_CLASS_OPS,
877 JS_NULL_CLASS_SPEC,
878 JS_NULL_CLASS_EXT,
879 JS_NULL_OBJECT_OPS
881 ${type},
882 ${prototypeID},
883 ${depth},
884 ${hooks},
885 ${protoGetter}
887 """,
888 name=self.descriptor.interface.getClassName(),
889 slotCount=slotCount,
890 type=type,
891 hooks=NativePropertyHooks(self.descriptor),
892 prototypeID=prototypeID,
893 depth=depth,
894 protoGetter=protoGetter,
898 def InterfaceObjectProtoGetter(descriptor, forXrays=False):
900 Returns a tuple with two elements:
902 1) The name of the function to call to get the prototype to use for the
903 interface object as a JSObject*.
905 2) The name of the function to call to get the prototype to use for the
906 interface prototype as a JS::Handle<JSObject*> or None if no such
907 function exists.
909 parentInterface = descriptor.interface.parent
910 if parentInterface:
911 assert not descriptor.interface.isNamespace()
912 parentIfaceName = parentInterface.identifier.name
913 parentDesc = descriptor.getDescriptor(parentIfaceName)
914 prefix = toBindingNamespace(parentDesc.name)
915 protoGetter = prefix + "::GetConstructorObject"
916 protoHandleGetter = prefix + "::GetConstructorObjectHandle"
917 elif descriptor.interface.isNamespace():
918 if forXrays or not descriptor.interface.getExtendedAttribute("ProtoObjectHack"):
919 protoGetter = "JS::GetRealmObjectPrototype"
920 else:
921 protoGetter = "GetHackedNamespaceProtoObject"
922 protoHandleGetter = None
923 else:
924 protoGetter = "JS::GetRealmFunctionPrototype"
925 protoHandleGetter = None
926 return (protoGetter, protoHandleGetter)
929 class CGNamespaceObjectJSClass(CGThing):
930 def __init__(self, descriptor):
931 CGThing.__init__(self)
932 self.descriptor = descriptor
934 def declare(self):
935 # We're purely for internal consumption
936 return ""
938 def define(self):
939 (protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor, forXrays=True)
941 classString = self.descriptor.interface.getExtendedAttribute("ClassString")
942 if classString is None:
943 classString = self.descriptor.interface.identifier.name
944 else:
945 classString = classString[0]
946 return fill(
948 static const DOMIfaceAndProtoJSClass sNamespaceObjectClass = {
950 "${classString}",
951 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS,
952 JS_NULL_CLASS_OPS,
953 JS_NULL_CLASS_SPEC,
954 JS_NULL_CLASS_EXT,
955 JS_NULL_OBJECT_OPS
957 eNamespace,
958 prototypes::id::_ID_Count,
960 ${hooks},
961 ${protoGetter}
963 """,
964 classString=classString,
965 hooks=NativePropertyHooks(self.descriptor),
966 protoGetter=protoGetter,
970 class CGInterfaceObjectInfo(CGThing):
971 def __init__(self, descriptor):
972 CGThing.__init__(self)
973 self.descriptor = descriptor
975 def declare(self):
976 # We're purely for internal consumption
977 return ""
979 def define(self):
980 if self.descriptor.interface.ctor():
981 ctorname = CONSTRUCT_HOOK_NAME
982 else:
983 ctorname = "ThrowingConstructor"
984 wantsIsInstance = self.descriptor.interface.hasInterfacePrototypeObject()
986 prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
987 (protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor, forXrays=True)
989 return fill(
991 static const DOMInterfaceInfo sInterfaceObjectInfo = {
992 { ${ctorname}, ${hooks} },
993 ${protoGetter},
994 ${prototypeID},
995 ${depth},
996 ${wantsIsInstance},
998 """,
999 ctorname=ctorname,
1000 hooks=NativePropertyHooks(self.descriptor),
1001 protoGetter=protoGetter,
1002 prototypeID=prototypeID,
1003 depth=depth,
1004 wantsIsInstance=toStringBool(wantsIsInstance),
1008 class CGList(CGThing):
1010 Generate code for a list of GCThings. Just concatenates them together, with
1011 an optional joiner string. "\n" is a common joiner.
1014 def __init__(self, children, joiner=""):
1015 CGThing.__init__(self)
1016 # Make a copy of the kids into a list, because if someone passes in a
1017 # generator we won't be able to both declare and define ourselves, or
1018 # define ourselves more than once!
1019 self.children = list(children)
1020 self.joiner = joiner
1022 def append(self, child):
1023 self.children.append(child)
1025 def prepend(self, child):
1026 self.children.insert(0, child)
1028 def extend(self, kids):
1029 self.children.extend(kids)
1031 def join(self, iterable):
1032 return self.joiner.join(s for s in iterable if len(s) > 0)
1034 def declare(self):
1035 return self.join(
1036 child.declare() for child in self.children if child is not None
1039 def define(self):
1040 return self.join(child.define() for child in self.children if child is not None)
1042 def deps(self):
1043 deps = set()
1044 for child in self.children:
1045 if child is None:
1046 continue
1047 deps = deps.union(child.deps())
1048 return deps
1050 def __len__(self):
1051 return len(self.children)
1054 class CGGeneric(CGThing):
1056 A class that spits out a fixed string into the codegen. Can spit out a
1057 separate string for the declaration too.
1060 def __init__(self, define="", declare=""):
1061 self.declareText = declare
1062 self.defineText = define
1064 def declare(self):
1065 return self.declareText
1067 def define(self):
1068 return self.defineText
1070 def deps(self):
1071 return set()
1074 class CGIndenter(CGThing):
1076 A class that takes another CGThing and generates code that indents that
1077 CGThing by some number of spaces. The default indent is two spaces.
1080 def __init__(self, child, indentLevel=2, declareOnly=False):
1081 assert isinstance(child, CGThing)
1082 CGThing.__init__(self)
1083 self.child = child
1084 self.indentLevel = indentLevel
1085 self.declareOnly = declareOnly
1087 def declare(self):
1088 return indent(self.child.declare(), self.indentLevel)
1090 def define(self):
1091 defn = self.child.define()
1092 if self.declareOnly:
1093 return defn
1094 else:
1095 return indent(defn, self.indentLevel)
1098 class CGWrapper(CGThing):
1100 Generic CGThing that wraps other CGThings with pre and post text.
1103 def __init__(
1104 self,
1105 child,
1106 pre="",
1107 post="",
1108 declarePre=None,
1109 declarePost=None,
1110 definePre=None,
1111 definePost=None,
1112 declareOnly=False,
1113 defineOnly=False,
1114 reindent=False,
1116 CGThing.__init__(self)
1117 self.child = child
1118 self.declarePre = declarePre or pre
1119 self.declarePost = declarePost or post
1120 self.definePre = definePre or pre
1121 self.definePost = definePost or post
1122 self.declareOnly = declareOnly
1123 self.defineOnly = defineOnly
1124 self.reindent = reindent
1126 def declare(self):
1127 if self.defineOnly:
1128 return ""
1129 decl = self.child.declare()
1130 if self.reindent:
1131 decl = self.reindentString(decl, self.declarePre)
1132 return self.declarePre + decl + self.declarePost
1134 def define(self):
1135 if self.declareOnly:
1136 return ""
1137 defn = self.child.define()
1138 if self.reindent:
1139 defn = self.reindentString(defn, self.definePre)
1140 return self.definePre + defn + self.definePost
1142 @staticmethod
1143 def reindentString(stringToIndent, widthString):
1144 # We don't use lineStartDetector because we don't want to
1145 # insert whitespace at the beginning of our _first_ line.
1146 # Use the length of the last line of width string, in case
1147 # it is a multiline string.
1148 lastLineWidth = len(widthString.splitlines()[-1])
1149 return stripTrailingWhitespace(
1150 stringToIndent.replace("\n", "\n" + (" " * lastLineWidth))
1153 def deps(self):
1154 return self.child.deps()
1157 class CGIfWrapper(CGList):
1158 def __init__(self, child, condition):
1159 CGList.__init__(
1160 self,
1162 CGWrapper(
1163 CGGeneric(condition), pre="if (", post=") {\n", reindent=True
1165 CGIndenter(child),
1166 CGGeneric("}\n"),
1171 class CGIfElseWrapper(CGList):
1172 def __init__(self, condition, ifTrue, ifFalse):
1173 CGList.__init__(
1174 self,
1176 CGWrapper(
1177 CGGeneric(condition), pre="if (", post=") {\n", reindent=True
1179 CGIndenter(ifTrue),
1180 CGGeneric("} else {\n"),
1181 CGIndenter(ifFalse),
1182 CGGeneric("}\n"),
1187 class CGElseChain(CGThing):
1189 Concatenate if statements in an if-else-if-else chain.
1192 def __init__(self, children):
1193 self.children = [c for c in children if c is not None]
1195 def declare(self):
1196 assert False
1198 def define(self):
1199 if not self.children:
1200 return ""
1201 s = self.children[0].define()
1202 assert s.endswith("\n")
1203 for child in self.children[1:]:
1204 code = child.define()
1205 assert code.startswith("if") or code.startswith("{")
1206 assert code.endswith("\n")
1207 s = s.rstrip() + " else " + code
1208 return s
1211 class CGTemplatedType(CGWrapper):
1212 def __init__(self, templateName, child, isConst=False, isReference=False):
1213 if isinstance(child, list):
1214 child = CGList(child, ", ")
1215 const = "const " if isConst else ""
1216 pre = "%s%s<" % (const, templateName)
1217 ref = "&" if isReference else ""
1218 post = ">%s" % ref
1219 CGWrapper.__init__(self, child, pre=pre, post=post)
1222 class CGNamespace(CGThing):
1224 Generates namespace block that wraps other CGThings.
1227 def __init__(self, namespace, child):
1228 CGThing.__init__(self)
1229 self.child = child
1230 self.pre = "namespace %s {\n" % namespace
1231 self.post = "} // namespace %s\n" % namespace
1233 def declare(self):
1234 decl = self.child.declare()
1235 if len(decl.strip()) == 0:
1236 return ""
1237 return self.pre + decl + self.post
1239 def define(self):
1240 defn = self.child.define()
1241 if len(defn.strip()) == 0:
1242 return ""
1243 return self.pre + defn + self.post
1245 def deps(self):
1246 return self.child.deps()
1248 @staticmethod
1249 def build(namespaces, child):
1251 Static helper method to build multiple wrapped namespaces.
1253 if not namespaces:
1254 return CGWrapper(child)
1255 return CGNamespace("::".join(namespaces), child)
1258 class CGIncludeGuard(CGWrapper):
1260 Generates include guards for a header.
1263 def __init__(self, prefix, child):
1264 """|prefix| is the filename without the extension."""
1265 define = "DOM_%s_H_" % prefix.upper()
1266 CGWrapper.__init__(
1267 self,
1268 child,
1269 declarePre="#ifndef %s\n#define %s\n\n" % (define, define),
1270 declarePost="\n#endif // %s\n" % define,
1274 class CGHeaders(CGWrapper):
1276 Generates the appropriate include statements.
1279 def __init__(
1280 self,
1281 descriptors,
1282 dictionaries,
1283 callbacks,
1284 callbackDescriptors,
1285 declareIncludes,
1286 defineIncludes,
1287 prefix,
1288 child,
1289 config=None,
1290 jsImplementedDescriptors=[],
1293 Builds a set of includes to cover |descriptors|.
1295 Also includes the files in |declareIncludes| in the header
1296 file and the files in |defineIncludes| in the .cpp.
1298 |prefix| contains the basename of the file that we generate include
1299 statements for.
1302 # Determine the filenames for which we need headers.
1303 interfaceDeps = [d.interface for d in descriptors]
1304 ancestors = []
1305 for iface in interfaceDeps:
1306 if iface.parent:
1307 # We're going to need our parent's prototype, to use as the
1308 # prototype of our prototype object.
1309 ancestors.append(iface.parent)
1310 # And if we have an interface object, we'll need the nearest
1311 # ancestor with an interface object too, so we can use its
1312 # interface object as the proto of our interface object.
1313 if iface.hasInterfaceObject():
1314 parent = iface.parent
1315 while parent and not parent.hasInterfaceObject():
1316 parent = parent.parent
1317 if parent:
1318 ancestors.append(parent)
1319 interfaceDeps.extend(ancestors)
1321 # Include parent interface headers needed for default toJSON code.
1322 jsonInterfaceParents = []
1323 for desc in descriptors:
1324 if not desc.hasDefaultToJSON:
1325 continue
1326 parent = desc.interface.parent
1327 while parent:
1328 parentDesc = desc.getDescriptor(parent.identifier.name)
1329 if parentDesc.hasDefaultToJSON:
1330 jsonInterfaceParents.append(parentDesc.interface)
1331 parent = parent.parent
1332 interfaceDeps.extend(jsonInterfaceParents)
1334 bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps)
1336 # Grab all the implementation declaration files we need.
1337 implementationIncludes = set(
1338 d.headerFile for d in descriptors if d.needsHeaderInclude()
1341 # Now find all the things we'll need as arguments because we
1342 # need to wrap or unwrap them.
1343 bindingHeaders = set()
1344 declareIncludes = set(declareIncludes)
1346 def addHeadersForType(typeAndPossibleOriginType):
1348 Add the relevant headers for this type. We use its origin type, if
1349 passed, to decide what to do with interface types.
1351 t, originType = typeAndPossibleOriginType
1352 isFromDictionary = originType and originType.isDictionary()
1353 isFromCallback = originType and originType.isCallback()
1354 # Dictionaries have members that need to be actually
1355 # declared, not just forward-declared.
1356 # Callbacks have nullable union arguments that need to be actually
1357 # declared, not just forward-declared.
1358 if isFromDictionary:
1359 headerSet = declareIncludes
1360 elif isFromCallback and t.nullable() and t.isUnion():
1361 headerSet = declareIncludes
1362 else:
1363 headerSet = bindingHeaders
1364 # Strip off outer layers and add headers they might require. (This
1365 # is conservative: only nullable non-pointer types need Nullable.h;
1366 # only sequences or observable arrays outside unions need
1367 # ForOfIterator.h; only functions that return, and attributes that
1368 # are, sequences or observable arrays in interfaces need Array.h, &c.)
1369 unrolled = t
1370 while True:
1371 if idlTypeNeedsCallContext(unrolled):
1372 bindingHeaders.add("mozilla/dom/BindingCallContext.h")
1373 if unrolled.nullable():
1374 headerSet.add("mozilla/dom/Nullable.h")
1375 elif unrolled.isSequence() or unrolled.isObservableArray():
1376 bindingHeaders.add("js/Array.h")
1377 bindingHeaders.add("js/ForOfIterator.h")
1378 if unrolled.isObservableArray():
1379 bindingHeaders.add("mozilla/dom/ObservableArrayProxyHandler.h")
1380 else:
1381 break
1382 unrolled = unrolled.inner
1383 if unrolled.isUnion():
1384 headerSet.add(self.getUnionDeclarationFilename(config, unrolled))
1385 for t in unrolled.flatMemberTypes:
1386 addHeadersForType((t, None))
1387 elif unrolled.isPromise():
1388 # See comment in the isInterface() case for why we add
1389 # Promise.h to headerSet, not bindingHeaders.
1390 headerSet.add("mozilla/dom/Promise.h")
1391 # We need ToJSValue to do the Promise to JS conversion.
1392 bindingHeaders.add("mozilla/dom/ToJSValue.h")
1393 elif unrolled.isInterface():
1394 if unrolled.isSpiderMonkeyInterface():
1395 bindingHeaders.add("jsfriendapi.h")
1396 if jsImplementedDescriptors:
1397 # Since we can't forward-declare typed array types
1398 # (because they're typedefs), we have to go ahead and
1399 # just include their header if we need to have functions
1400 # taking references to them declared in that header.
1401 headerSet = declareIncludes
1402 headerSet.add("mozilla/dom/TypedArray.h")
1403 else:
1404 try:
1405 typeDesc = config.getDescriptor(unrolled.inner.identifier.name)
1406 except NoSuchDescriptorError:
1407 return
1408 # Dictionaries with interface members rely on the
1409 # actual class definition of that interface member
1410 # being visible in the binding header, because they
1411 # store them in RefPtr and have inline
1412 # constructors/destructors.
1414 # XXXbz maybe dictionaries with interface members
1415 # should just have out-of-line constructors and
1416 # destructors?
1417 headerSet.add(typeDesc.headerFile)
1418 elif unrolled.isDictionary():
1419 headerSet.add(self.getDeclarationFilename(unrolled.inner))
1420 # And if it needs rooting, we need RootedDictionary too
1421 if typeNeedsRooting(unrolled):
1422 headerSet.add("mozilla/dom/RootedDictionary.h")
1423 elif unrolled.isCallback():
1424 headerSet.add(self.getDeclarationFilename(unrolled.callback))
1425 elif unrolled.isFloat() and not unrolled.isUnrestricted():
1426 # Restricted floats are tested for finiteness
1427 bindingHeaders.add("mozilla/FloatingPoint.h")
1428 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
1429 elif unrolled.isEnum():
1430 filename = self.getDeclarationFilename(unrolled.inner)
1431 declareIncludes.add(filename)
1432 elif unrolled.isPrimitive():
1433 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
1434 elif unrolled.isRecord():
1435 if isFromDictionary or jsImplementedDescriptors:
1436 declareIncludes.add("mozilla/dom/Record.h")
1437 else:
1438 bindingHeaders.add("mozilla/dom/Record.h")
1439 # Also add headers for the type the record is
1440 # parametrized over, if needed.
1441 addHeadersForType((t.inner, originType if isFromDictionary else None))
1443 for t in getAllTypes(
1444 descriptors + callbackDescriptors, dictionaries, callbacks
1446 addHeadersForType(t)
1448 def addHeaderForFunc(func, desc):
1449 if func is None:
1450 return
1451 # Include the right class header, which we can only do
1452 # if this is a class member function.
1453 if desc is not None and not desc.headerIsDefault:
1454 # An explicit header file was provided, assume that we know
1455 # what we're doing.
1456 return
1458 if "::" in func:
1459 # Strip out the function name and convert "::" to "/"
1460 bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")
1462 # Now for non-callback descriptors make sure we include any
1463 # headers needed by Func declarations and other things like that.
1464 for desc in descriptors:
1465 # If this is an iterator or an async iterator interface generated
1466 # for a separate iterable interface, skip generating type includes,
1467 # as we have what we need in IterableIterator.h
1468 if (
1469 desc.interface.isIteratorInterface()
1470 or desc.interface.isAsyncIteratorInterface()
1472 continue
1474 for m in desc.interface.members:
1475 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), desc)
1476 staticTypeOverride = PropertyDefiner.getStringAttr(
1477 m, "StaticClassOverride"
1479 if staticTypeOverride:
1480 bindingHeaders.add("/".join(staticTypeOverride.split("::")) + ".h")
1481 # getExtendedAttribute() returns a list, extract the entry.
1482 funcList = desc.interface.getExtendedAttribute("Func")
1483 if funcList is not None:
1484 addHeaderForFunc(funcList[0], desc)
1486 if desc.interface.maplikeOrSetlikeOrIterable:
1487 # We need ToJSValue.h for maplike/setlike type conversions
1488 bindingHeaders.add("mozilla/dom/ToJSValue.h")
1489 # Add headers for the key and value types of the
1490 # maplike/setlike/iterable, since they'll be needed for
1491 # convenience functions
1492 if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType():
1493 addHeadersForType(
1494 (desc.interface.maplikeOrSetlikeOrIterable.keyType, None)
1496 if desc.interface.maplikeOrSetlikeOrIterable.hasValueType():
1497 addHeadersForType(
1498 (desc.interface.maplikeOrSetlikeOrIterable.valueType, None)
1501 for d in dictionaries:
1502 if d.parent:
1503 declareIncludes.add(self.getDeclarationFilename(d.parent))
1504 bindingHeaders.add(self.getDeclarationFilename(d))
1505 for m in d.members:
1506 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), None)
1507 # No need to worry about Func on members of ancestors, because that
1508 # will happen automatically in whatever files those ancestors live
1509 # in.
1511 for c in callbacks:
1512 bindingHeaders.add(self.getDeclarationFilename(c))
1514 for c in callbackDescriptors:
1515 bindingHeaders.add(self.getDeclarationFilename(c.interface))
1517 if len(callbacks) != 0:
1518 # We need CallbackFunction to serve as our parent class
1519 declareIncludes.add("mozilla/dom/CallbackFunction.h")
1520 # And we need ToJSValue.h so we can wrap "this" objects
1521 declareIncludes.add("mozilla/dom/ToJSValue.h")
1523 if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0:
1524 # We need CallbackInterface to serve as our parent class
1525 declareIncludes.add("mozilla/dom/CallbackInterface.h")
1526 # And we need ToJSValue.h so we can wrap "this" objects
1527 declareIncludes.add("mozilla/dom/ToJSValue.h")
1529 # Also need to include the headers for ancestors of
1530 # JS-implemented interfaces.
1531 for jsImplemented in jsImplementedDescriptors:
1532 jsParent = jsImplemented.interface.parent
1533 if jsParent:
1534 parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name)
1535 declareIncludes.add(parentDesc.jsImplParentHeader)
1537 # Now make sure we're not trying to include the header from inside itself
1538 declareIncludes.discard(prefix + ".h")
1540 # Let the machinery do its thing.
1541 def _includeString(includes):
1542 def headerName(include):
1543 # System headers are specified inside angle brackets.
1544 if include.startswith("<"):
1545 return include
1546 # Non-system headers need to be placed in quotes.
1547 return '"%s"' % include
1549 return "".join(["#include %s\n" % headerName(i) for i in includes]) + "\n"
1551 CGWrapper.__init__(
1552 self,
1553 child,
1554 declarePre=_includeString(sorted(declareIncludes)),
1555 definePre=_includeString(
1556 sorted(
1557 set(defineIncludes)
1558 | bindingIncludes
1559 | bindingHeaders
1560 | implementationIncludes
1565 @staticmethod
1566 def getDeclarationFilename(decl):
1567 # Use our local version of the header, not the exported one, so that
1568 # test bindings, which don't export, will work correctly.
1569 basename = os.path.basename(decl.filename)
1570 return basename.replace(".webidl", "Binding.h")
1572 @staticmethod
1573 def getUnionDeclarationFilename(config, unionType):
1574 assert unionType.isUnion()
1575 assert unionType.unroll() == unionType
1576 # If a union is "defined" in multiple files, it goes in UnionTypes.h.
1577 if len(config.filenamesPerUnion[unionType.name]) > 1:
1578 return "mozilla/dom/UnionTypes.h"
1579 # If a union is defined by a built-in typedef, it also goes in
1580 # UnionTypes.h.
1581 assert len(config.filenamesPerUnion[unionType.name]) == 1
1582 if "<unknown>" in config.filenamesPerUnion[unionType.name]:
1583 return "mozilla/dom/UnionTypes.h"
1584 return CGHeaders.getDeclarationFilename(unionType)
1587 def SortedDictValues(d):
1589 Returns a list of values from the dict sorted by key.
1591 return [v for k, v in sorted(d.items())]
1594 def UnionsForFile(config, webIDLFile):
1596 Returns a list of union types for all union types that are only used in
1597 webIDLFile. If webIDLFile is None this will return the list of tuples for
1598 union types that are used in more than one WebIDL file.
1600 return config.unionsPerFilename.get(webIDLFile, [])
1603 def UnionTypes(unionTypes, config):
1605 The unionTypes argument should be a list of union types. This is typically
1606 the list generated by UnionsForFile.
1608 Returns a tuple containing a set of header filenames to include in
1609 the header for the types in unionTypes, a set of header filenames to
1610 include in the implementation file for the types in unionTypes, a set
1611 of tuples containing a type declaration and a boolean if the type is a
1612 struct for member types of the union, a list of traverse methods,
1613 unlink methods and a list of union types. These last three lists only
1614 contain unique union types.
1617 headers = set()
1618 implheaders = set()
1619 declarations = set()
1620 unionStructs = dict()
1621 traverseMethods = dict()
1622 unlinkMethods = dict()
1624 for t in unionTypes:
1625 name = str(t)
1626 if name not in unionStructs:
1627 unionStructs[name] = t
1629 def addHeadersForType(f):
1630 if f.nullable():
1631 headers.add("mozilla/dom/Nullable.h")
1632 isSequence = f.isSequence()
1633 if isSequence:
1634 # Dealing with sequences requires for-of-compatible
1635 # iteration.
1636 implheaders.add("js/ForOfIterator.h")
1637 # Sequences can always throw "not an object" exceptions.
1638 implheaders.add("mozilla/dom/BindingCallContext.h")
1639 if typeNeedsRooting(f):
1640 headers.add("mozilla/dom/RootedSequence.h")
1641 f = f.unroll()
1642 if idlTypeNeedsCallContext(f):
1643 implheaders.add("mozilla/dom/BindingCallContext.h")
1644 if f.isPromise():
1645 headers.add("mozilla/dom/Promise.h")
1646 # We need ToJSValue to do the Promise to JS conversion.
1647 headers.add("mozilla/dom/ToJSValue.h")
1648 elif f.isInterface():
1649 if f.isSpiderMonkeyInterface():
1650 headers.add("js/RootingAPI.h")
1651 headers.add("js/Value.h")
1652 headers.add("mozilla/dom/TypedArray.h")
1653 else:
1654 try:
1655 typeDesc = config.getDescriptor(f.inner.identifier.name)
1656 except NoSuchDescriptorError:
1657 return
1658 if typeDesc.interface.isCallback() or isSequence:
1659 # Callback interfaces always use strong refs, so
1660 # we need to include the right header to be able
1661 # to Release() in our inlined code.
1663 # Similarly, sequences always contain strong
1664 # refs, so we'll need the header to handler
1665 # those.
1666 headers.add(typeDesc.headerFile)
1667 elif typeDesc.interface.identifier.name == "WindowProxy":
1668 # In UnionTypes.h we need to see the declaration of the
1669 # WindowProxyHolder that we use to store the WindowProxy, so
1670 # we have its sizeof and know how big to make our union.
1671 headers.add(typeDesc.headerFile)
1672 else:
1673 declarations.add((typeDesc.nativeType, False))
1674 implheaders.add(typeDesc.headerFile)
1675 elif f.isDictionary():
1676 # For a dictionary, we need to see its declaration in
1677 # UnionTypes.h so we have its sizeof and know how big to
1678 # make our union.
1679 headers.add(CGHeaders.getDeclarationFilename(f.inner))
1680 # And if it needs rooting, we need RootedDictionary too
1681 if typeNeedsRooting(f):
1682 headers.add("mozilla/dom/RootedDictionary.h")
1683 elif f.isFloat() and not f.isUnrestricted():
1684 # Restricted floats are tested for finiteness
1685 implheaders.add("mozilla/FloatingPoint.h")
1686 implheaders.add("mozilla/dom/PrimitiveConversions.h")
1687 elif f.isEnum():
1688 # Need to see the actual definition of the enum,
1689 # unfortunately.
1690 headers.add(CGHeaders.getDeclarationFilename(f.inner))
1691 elif f.isPrimitive():
1692 implheaders.add("mozilla/dom/PrimitiveConversions.h")
1693 elif f.isCallback():
1694 # Callbacks always use strong refs, so we need to include
1695 # the right header to be able to Release() in our inlined
1696 # code.
1697 headers.add(CGHeaders.getDeclarationFilename(f.callback))
1698 elif f.isRecord():
1699 headers.add("mozilla/dom/Record.h")
1700 # And add headers for the type we're parametrized over
1701 addHeadersForType(f.inner)
1702 # And if it needs rooting, we need RootedRecord too
1703 if typeNeedsRooting(f):
1704 headers.add("mozilla/dom/RootedRecord.h")
1706 implheaders.add(CGHeaders.getUnionDeclarationFilename(config, t))
1707 for f in t.flatMemberTypes:
1708 assert not f.nullable()
1709 addHeadersForType(f)
1711 if idlTypeNeedsCycleCollection(t):
1712 declarations.add(
1713 ("mozilla::dom::%s" % CGUnionStruct.unionTypeName(t, True), False)
1715 traverseMethods[name] = CGCycleCollectionTraverseForOwningUnionMethod(t)
1716 unlinkMethods[name] = CGCycleCollectionUnlinkForOwningUnionMethod(t)
1718 # The order of items in CGList is important.
1719 # Since the union structs friend the unlinkMethods, the forward-declaration
1720 # for these methods should come before the class declaration. Otherwise
1721 # some compilers treat the friend declaration as a forward-declaration in
1722 # the class scope.
1723 return (
1724 headers,
1725 implheaders,
1726 declarations,
1727 SortedDictValues(traverseMethods),
1728 SortedDictValues(unlinkMethods),
1729 SortedDictValues(unionStructs),
1733 class Argument:
1735 A class for outputting the type and name of an argument
1738 def __init__(self, argType, name, default=None):
1739 self.argType = argType
1740 self.name = name
1741 self.default = default
1743 def declare(self):
1744 string = self.argType + " " + self.name
1745 if self.default is not None:
1746 string += " = " + self.default
1747 return string
1749 def define(self):
1750 return self.argType + " " + self.name
1753 class CGAbstractMethod(CGThing):
1755 An abstract class for generating code for a method. Subclasses
1756 should override definition_body to create the actual code.
1758 descriptor is the descriptor for the interface the method is associated with
1760 name is the name of the method as a string
1762 returnType is the IDLType of the return value
1764 args is a list of Argument objects
1766 inline should be True to generate an inline method, whose body is
1767 part of the declaration.
1769 alwaysInline should be True to generate an inline method annotated with
1770 MOZ_ALWAYS_INLINE.
1772 static should be True to generate a static method, which only has
1773 a definition.
1775 If templateArgs is not None it should be a list of strings containing
1776 template arguments, and the function will be templatized using those
1777 arguments.
1779 canRunScript should be True to generate a MOZ_CAN_RUN_SCRIPT annotation.
1781 signatureOnly should be True to only declare the signature (either in
1782 the header, or if static is True in the cpp file).
1785 def __init__(
1786 self,
1787 descriptor,
1788 name,
1789 returnType,
1790 args,
1791 inline=False,
1792 alwaysInline=False,
1793 static=False,
1794 templateArgs=None,
1795 canRunScript=False,
1796 signatureOnly=False,
1798 CGThing.__init__(self)
1799 self.descriptor = descriptor
1800 self.name = name
1801 self.returnType = returnType
1802 self.args = args
1803 self.inline = inline
1804 self.alwaysInline = alwaysInline
1805 self.static = static
1806 self.templateArgs = templateArgs
1807 self.canRunScript = canRunScript
1808 self.signatureOnly = signatureOnly
1810 def _argstring(self, declare):
1811 return ", ".join([a.declare() if declare else a.define() for a in self.args])
1813 def _template(self):
1814 if self.templateArgs is None:
1815 return ""
1816 return "template <%s>\n" % ", ".join(self.templateArgs)
1818 def _decorators(self):
1819 decorators = []
1820 if self.canRunScript:
1821 decorators.append("MOZ_CAN_RUN_SCRIPT")
1822 if self.alwaysInline:
1823 decorators.append("MOZ_ALWAYS_INLINE")
1824 elif self.inline:
1825 decorators.append("inline")
1826 if self.static:
1827 decorators.append("static")
1828 decorators.append(self.returnType)
1829 maybeNewline = " " if self.inline else "\n"
1830 return " ".join(decorators) + maybeNewline
1832 def signature(self):
1833 return "%s%s%s(%s);\n" % (
1834 self._template(),
1835 self._decorators(),
1836 self.name,
1837 self._argstring(True),
1840 def declare(self):
1841 if self.static:
1842 return ""
1843 if self.inline:
1844 return self._define(True)
1845 return self.signature()
1847 def indent_body(self, body):
1849 Indent the code returned by self.definition_body(). Most classes
1850 simply indent everything two spaces. This is here for
1851 CGRegisterProtos, which needs custom indentation.
1853 return indent(body)
1855 def _define(self, fromDeclare=False):
1856 return (
1857 self.definition_prologue(fromDeclare)
1858 + self.indent_body(self.definition_body())
1859 + self.definition_epilogue()
1862 def define(self):
1863 if self.signatureOnly:
1864 if self.static:
1865 # self.static makes us not output anything in the header, so output the signature here.
1866 return self.signature()
1867 return ""
1868 return "" if (self.inline and not self.static) else self._define()
1870 def definition_prologue(self, fromDeclare):
1871 error_reporting_label = self.error_reporting_label()
1872 if error_reporting_label:
1873 # We're going to want a BindingCallContext. Rename our JSContext*
1874 # arg accordingly.
1875 i = 0
1876 while i < len(self.args):
1877 arg = self.args[i]
1878 if arg.argType == "JSContext*":
1879 cxname = arg.name
1880 self.args[i] = Argument(arg.argType, "cx_", arg.default)
1881 break
1882 i += 1
1883 if i == len(self.args):
1884 raise TypeError("Must have a JSContext* to create a BindingCallContext")
1886 prologue = "%s%s%s(%s)\n{\n" % (
1887 self._template(),
1888 self._decorators(),
1889 self.name,
1890 self._argstring(fromDeclare),
1892 if error_reporting_label:
1893 prologue += indent(
1894 fill(
1896 BindingCallContext ${cxname}(cx_, ${label});
1897 """,
1898 cxname=cxname,
1899 label=error_reporting_label,
1903 profiler_label = self.auto_profiler_label()
1904 if profiler_label:
1905 prologue += indent(profiler_label) + "\n"
1907 return prologue
1909 def definition_epilogue(self):
1910 return "}\n"
1912 def definition_body(self):
1913 assert False # Override me!
1916 Override this method to return a pair of (descriptive string, name of a
1917 JSContext* variable) in order to generate a profiler label for this method.
1920 def auto_profiler_label(self):
1921 return None # Override me!
1924 Override this method to return a string to be used as the label for a
1925 BindingCallContext. If this does not return None, one of the arguments of
1926 this method must be of type 'JSContext*'. Its name will be replaced with
1927 'cx_' and a BindingCallContext named 'cx' will be instantiated with the
1928 given label.
1931 def error_reporting_label(self):
1932 return None # Override me!
1935 class CGAbstractStaticMethod(CGAbstractMethod):
1937 Abstract base class for codegen of implementation-only (no
1938 declaration) static methods.
1941 def __init__(self, descriptor, name, returnType, args, canRunScript=False):
1942 CGAbstractMethod.__init__(
1943 self,
1944 descriptor,
1945 name,
1946 returnType,
1947 args,
1948 inline=False,
1949 static=True,
1950 canRunScript=canRunScript,
1954 class CGAbstractClassHook(CGAbstractStaticMethod):
1956 Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
1957 'this' unwrapping as it assumes that the unwrapped type is always known.
1960 def __init__(self, descriptor, name, returnType, args):
1961 CGAbstractStaticMethod.__init__(self, descriptor, name, returnType, args)
1963 def definition_body_prologue(self):
1964 return "%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" % (
1965 self.descriptor.nativeType,
1966 self.descriptor.nativeType,
1969 def definition_body(self):
1970 return self.definition_body_prologue() + self.generate_code()
1972 def generate_code(self):
1973 assert False # Override me!
1976 class CGAddPropertyHook(CGAbstractClassHook):
1978 A hook for addProperty, used to preserve our wrapper from GC.
1981 def __init__(self, descriptor):
1982 args = [
1983 Argument("JSContext*", "cx"),
1984 Argument("JS::Handle<JSObject*>", "obj"),
1985 Argument("JS::Handle<jsid>", "id"),
1986 Argument("JS::Handle<JS::Value>", "val"),
1988 CGAbstractClassHook.__init__(
1989 self, descriptor, ADDPROPERTY_HOOK_NAME, "bool", args
1992 def generate_code(self):
1993 assert self.descriptor.wrapperCache
1994 # This hook is also called by TryPreserveWrapper on non-nsISupports
1995 # cycle collected objects, so if addProperty is ever changed to do
1996 # anything more or less than preserve the wrapper, TryPreserveWrapper
1997 # will need to be changed.
1998 return dedent(
2000 // We don't want to preserve if we don't have a wrapper, and we
2001 // obviously can't preserve if we're not initialized.
2002 if (self && self->GetWrapperPreserveColor()) {
2003 PreserveWrapper(self);
2005 return true;
2010 class CGGetWrapperCacheHook(CGAbstractClassHook):
2012 A hook for GetWrapperCache, used by HasReleasedWrapper to get the
2013 nsWrapperCache pointer for a non-nsISupports object.
2016 def __init__(self, descriptor):
2017 args = [Argument("JS::Handle<JSObject*>", "obj")]
2018 CGAbstractClassHook.__init__(
2019 self, descriptor, GETWRAPPERCACHE_HOOK_NAME, "nsWrapperCache*", args
2022 def generate_code(self):
2023 assert self.descriptor.wrapperCache
2024 return dedent(
2026 return self;
2031 def finalizeHook(descriptor, hookName, gcx, obj):
2032 finalize = "JS::SetReservedSlot(%s, DOM_OBJECT_SLOT, JS::UndefinedValue());\n" % obj
2033 if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
2034 finalize += fill(
2036 // Either our proxy created an expando object or not. If it did,
2037 // then we would have preserved ourselves, and hence if we're going
2038 // away so is our C++ object and we should reset its expando value.
2039 // It's possible that in this situation the C++ object's reflector
2040 // pointer has been nulled out, but if not it's pointing to us. If
2041 // our proxy did _not_ create an expando object then it's possible
2042 // that we're no longer the reflector for our C++ object (and
2043 // incremental finalization is finally getting to us), and that in
2044 // the meantime the new reflector has created an expando object.
2045 // In that case we do NOT want to clear the expando pointer in the
2046 // C++ object.
2048 // It's important to do this before we ClearWrapper, of course.
2049 JSObject* reflector = self->GetWrapperMaybeDead();
2050 if (!reflector || reflector == ${obj}) {
2051 self->mExpandoAndGeneration.expando = JS::UndefinedValue();
2053 """,
2054 obj=obj,
2056 for m in descriptor.interface.members:
2057 if m.isAttr() and m.type.isObservableArray():
2058 finalize += fill(
2061 JS::Value val = JS::GetReservedSlot(obj, ${slot});
2062 if (!val.isUndefined()) {
2063 JSObject* obj = &val.toObject();
2064 js::SetProxyReservedSlot(obj, OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT, JS::UndefinedValue());
2067 """,
2068 slot=memberReservedSlot(m, descriptor),
2070 if descriptor.wrapperCache:
2071 finalize += "ClearWrapper(self, self, %s);\n" % obj
2072 if descriptor.isGlobal():
2073 finalize += "mozilla::dom::FinalizeGlobal(%s, %s);\n" % (gcx, obj)
2074 finalize += fill(
2076 if (size_t mallocBytes = BindingJSObjectMallocBytes(self)) {
2077 JS::RemoveAssociatedMemory(${obj}, mallocBytes,
2078 JS::MemoryUse::DOMBinding);
2080 """,
2081 obj=obj,
2083 finalize += "AddForDeferredFinalization<%s>(self);\n" % descriptor.nativeType
2084 return CGIfWrapper(CGGeneric(finalize), "self")
2087 class CGClassFinalizeHook(CGAbstractClassHook):
2089 A hook for finalize, used to release our native object.
2092 def __init__(self, descriptor):
2093 args = [Argument("JS::GCContext*", "gcx"), Argument("JSObject*", "obj")]
2094 CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, "void", args)
2096 def generate_code(self):
2097 return finalizeHook(
2098 self.descriptor, self.name, self.args[0].name, self.args[1].name
2099 ).define()
2102 def objectMovedHook(descriptor, hookName, obj, old):
2103 assert descriptor.wrapperCache
2104 return fill(
2106 if (self) {
2107 UpdateWrapper(self, self, ${obj}, ${old});
2110 return 0;
2111 """,
2112 obj=obj,
2113 old=old,
2117 class CGClassObjectMovedHook(CGAbstractClassHook):
2119 A hook for objectMovedOp, used to update the wrapper cache when an object it
2120 is holding moves.
2123 def __init__(self, descriptor):
2124 args = [Argument("JSObject*", "obj"), Argument("JSObject*", "old")]
2125 CGAbstractClassHook.__init__(
2126 self, descriptor, OBJECT_MOVED_HOOK_NAME, "size_t", args
2129 def generate_code(self):
2130 return objectMovedHook(
2131 self.descriptor, self.name, self.args[0].name, self.args[1].name
2135 def JSNativeArguments():
2136 return [
2137 Argument("JSContext*", "cx"),
2138 Argument("unsigned", "argc"),
2139 Argument("JS::Value*", "vp"),
2143 class CGClassConstructor(CGAbstractStaticMethod):
2145 JS-visible constructor for our objects
2148 def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
2149 CGAbstractStaticMethod.__init__(
2150 self, descriptor, name, "bool", JSNativeArguments()
2152 self._ctor = ctor
2154 def define(self):
2155 if not self._ctor:
2156 return ""
2157 return CGAbstractStaticMethod.define(self)
2159 def definition_body(self):
2160 return self.generate_code()
2162 def generate_code(self):
2163 if self._ctor.isHTMLConstructor():
2164 # We better have a prototype object. Otherwise our proto
2165 # id won't make sense.
2166 assert self.descriptor.interface.hasInterfacePrototypeObject()
2167 # We also better have a constructor object, if this is
2168 # getting called!
2169 assert self.descriptor.interface.hasInterfaceObject()
2170 # We can't just pass null for the CreateInterfaceObjects callback,
2171 # because our newTarget might be in a different compartment, in
2172 # which case we'll need to look up constructor objects in that
2173 # compartment.
2174 return fill(
2176 return HTMLConstructor(cx, argc, vp,
2177 constructors::id::${name},
2178 prototypes::id::${name},
2179 CreateInterfaceObjects);
2180 """,
2181 name=self.descriptor.name,
2184 # If the interface is already SecureContext, notify getConditionList to skip that check,
2185 # because the constructor won't be exposed in non-secure contexts to start with.
2186 alreadySecureContext = self.descriptor.interface.getExtendedAttribute(
2187 "SecureContext"
2190 # We want to throw if any of the conditions returned by getConditionList are false.
2191 conditionsCheck = ""
2192 rawConditions = getRawConditionList(
2193 self._ctor, "cx", "obj", alreadySecureContext
2195 if len(rawConditions) > 0:
2196 notConditions = " ||\n".join("!" + cond for cond in rawConditions)
2197 failedCheckAction = CGGeneric("return ThrowingConstructor(cx, argc, vp);\n")
2198 conditionsCheck = (
2199 CGIfWrapper(failedCheckAction, notConditions).define() + "\n"
2202 # Additionally, we want to throw if a caller does a bareword invocation
2203 # of a constructor without |new|.
2204 ctorName = GetConstructorNameForReporting(self.descriptor, self._ctor)
2206 preamble = fill(
2208 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2209 JS::Rooted<JSObject*> obj(cx, &args.callee());
2210 $*{conditionsCheck}
2211 if (!args.isConstructing()) {
2212 return ThrowConstructorWithoutNew(cx, "${ctorName}");
2215 JS::Rooted<JSObject*> desiredProto(cx);
2216 if (!GetDesiredProto(cx, args,
2217 prototypes::id::${name},
2218 CreateInterfaceObjects,
2219 &desiredProto)) {
2220 return false;
2222 """,
2223 conditionsCheck=conditionsCheck,
2224 ctorName=ctorName,
2225 name=self.descriptor.name,
2228 name = self._ctor.identifier.name
2229 nativeName = MakeNativeName(self.descriptor.binaryNameFor(name, True))
2230 callGenerator = CGMethodCall(
2231 nativeName, True, self.descriptor, self._ctor, isConstructor=True
2233 return preamble + "\n" + callGenerator.define()
2235 def auto_profiler_label(self):
2236 return fill(
2238 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
2239 "${ctorName}", "constructor", DOM, cx,
2240 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
2241 """,
2242 ctorName=GetConstructorNameForReporting(self.descriptor, self._ctor),
2245 def error_reporting_label(self):
2246 return CGSpecializedMethod.error_reporting_label_helper(
2247 self.descriptor, self._ctor, isConstructor=True
2251 def LegacyFactoryFunctionName(m):
2252 return "_" + m.identifier.name
2255 class CGLegacyFactoryFunctions(CGThing):
2256 def __init__(self, descriptor):
2257 self.descriptor = descriptor
2258 CGThing.__init__(self)
2260 def declare(self):
2261 return ""
2263 def define(self):
2264 if len(self.descriptor.interface.legacyFactoryFunctions) == 0:
2265 return ""
2267 constructorID = "constructors::id::"
2268 if self.descriptor.interface.hasInterfaceObject():
2269 constructorID += self.descriptor.name
2270 else:
2271 constructorID += "_ID_Count"
2273 legacyFactoryFunctions = ""
2274 for n in self.descriptor.interface.legacyFactoryFunctions:
2275 legacyFactoryFunctions += (
2276 '{ "%s", { %s, &sLegacyFactoryFunctionNativePropertyHooks }, %i },\n'
2277 % (n.identifier.name, LegacyFactoryFunctionName(n), methodLength(n))
2280 return fill(
2282 bool sLegacyFactoryFunctionNativePropertiesInited = true;
2283 const NativePropertyHooks sLegacyFactoryFunctionNativePropertyHooks = {
2284 nullptr,
2285 { nullptr, nullptr, &sLegacyFactoryFunctionNativePropertiesInited },
2286 prototypes::id::${name},
2287 ${constructorID},
2288 nullptr
2291 static const LegacyFactoryFunction legacyFactoryFunctions[] = {
2292 $*{legacyFactoryFunctions}
2294 """,
2295 name=self.descriptor.name,
2296 constructorID=constructorID,
2297 legacyFactoryFunctions=legacyFactoryFunctions,
2301 def isChromeOnly(m):
2302 return m.getExtendedAttribute("ChromeOnly")
2305 def prefIdentifier(pref):
2306 return pref.replace(".", "_").replace("-", "_")
2309 def prefHeader(pref):
2310 return "mozilla/StaticPrefs_%s.h" % pref.partition(".")[0]
2313 def computeGlobalNamesFromExposureSet(exposureSet):
2314 assert exposureSet is None or isinstance(exposureSet, set)
2316 if exposureSet:
2317 # Nonempty set
2318 return " | ".join(map(lambda g: "GlobalNames::%s" % g, sorted(exposureSet)))
2320 return "0"
2323 class MemberCondition:
2325 An object representing the condition for a member to actually be
2326 exposed. Any of the arguments can be None. If not
2327 None, they should have the following types:
2329 pref: The name of the preference.
2330 func: The name of the function.
2331 secureContext: A bool indicating whether a secure context is required.
2332 nonExposedGlobals: A set of names of globals. Can be empty, in which case
2333 it's treated the same way as None.
2334 trial: The name of the origin trial.
2337 def __init__(
2338 self,
2339 pref=None,
2340 func=None,
2341 secureContext=False,
2342 nonExposedGlobals=None,
2343 trial=None,
2345 assert pref is None or isinstance(pref, str)
2346 assert func is None or isinstance(func, str)
2347 assert trial is None or isinstance(trial, str)
2348 assert isinstance(secureContext, bool)
2349 self.pref = pref
2350 if self.pref:
2351 identifier = prefIdentifier(self.pref)
2352 self.prefFuncIndex = "WebIDLPrefIndex::" + identifier
2353 else:
2354 self.prefFuncIndex = "WebIDLPrefIndex::NoPref"
2356 self.secureContext = secureContext
2358 def toFuncPtr(val):
2359 if val is None:
2360 return "nullptr"
2361 return "&" + val
2363 self.func = toFuncPtr(func)
2365 self.nonExposedGlobals = computeGlobalNamesFromExposureSet(nonExposedGlobals)
2367 if trial:
2368 self.trial = "OriginTrial::" + trial
2369 else:
2370 self.trial = "OriginTrial(0)"
2372 def __eq__(self, other):
2373 return (
2374 self.pref == other.pref
2375 and self.func == other.func
2376 and self.secureContext == other.secureContext
2377 and self.nonExposedGlobals == other.nonExposedGlobals
2378 and self.trial == other.trial
2381 def __ne__(self, other):
2382 return not self.__eq__(other)
2384 def hasDisablers(self):
2385 return (
2386 self.pref is not None
2387 or self.secureContext
2388 or self.func != "nullptr"
2389 or self.nonExposedGlobals != "0"
2390 or self.trial != "OriginTrial(0)"
2394 class PropertyDefiner:
2396 A common superclass for defining things on prototype objects.
2398 Subclasses should implement generateArray to generate the actual arrays of
2399 things we're defining. They should also set self.chrome to the list of
2400 things only exposed to chrome and self.regular to the list of things exposed
2401 to both chrome and web pages.
2404 def __init__(self, descriptor, name):
2405 self.descriptor = descriptor
2406 self.name = name
2408 def hasChromeOnly(self):
2409 return len(self.chrome) > 0
2411 def hasNonChromeOnly(self):
2412 return len(self.regular) > 0
2414 def variableName(self, chrome):
2415 if chrome:
2416 if self.hasChromeOnly():
2417 return "sChrome" + self.name
2418 else:
2419 if self.hasNonChromeOnly():
2420 return "s" + self.name
2421 return "nullptr"
2423 def usedForXrays(self):
2424 return self.descriptor.wantsXrays
2426 def length(self, chrome):
2427 return len(self.chrome) if chrome else len(self.regular)
2429 def __str__(self):
2430 # We only need to generate id arrays for things that will end
2431 # up used via ResolveProperty or EnumerateProperties.
2432 str = self.generateArray(self.regular, self.variableName(False))
2433 if self.hasChromeOnly():
2434 str += self.generateArray(self.chrome, self.variableName(True))
2435 return str
2437 @staticmethod
2438 def getStringAttr(member, name):
2439 attr = member.getExtendedAttribute(name)
2440 if attr is None:
2441 return None
2442 # It's a list of strings
2443 assert len(attr) == 1
2444 assert attr[0] is not None
2445 return attr[0]
2447 @staticmethod
2448 def getControllingCondition(interfaceMember, descriptor):
2449 interface = descriptor.interface
2450 nonExposureSet = interface.exposureSet - interfaceMember.exposureSet
2452 trial = PropertyDefiner.getStringAttr(interfaceMember, "Trial")
2453 if trial and interface.identifier.name in ["Window", "Document"]:
2454 raise TypeError(
2455 "[Trial] not yet supported for %s.%s, see bug 1757935"
2456 % (interface.identifier.name, interfaceMember.identifier.name)
2459 return MemberCondition(
2460 PropertyDefiner.getStringAttr(interfaceMember, "Pref"),
2461 PropertyDefiner.getStringAttr(interfaceMember, "Func"),
2462 interfaceMember.getExtendedAttribute("SecureContext") is not None,
2463 nonExposureSet,
2464 trial,
2467 @staticmethod
2468 def generatePrefableArrayValues(
2469 array,
2470 descriptor,
2471 specFormatter,
2472 specTerminator,
2473 getCondition,
2474 getDataTuple,
2475 switchToCondition=None,
2478 This method generates an array of spec entries for interface members. It returns
2479 a tuple containing the array of spec entries and the maximum of the number of
2480 spec entries per condition.
2482 array is an array of interface members.
2484 descriptor is the descriptor for the interface that array contains members of.
2486 specFormatter is a function that takes a single argument, a tuple,
2487 and returns a string, a spec array entry.
2489 specTerminator is a terminator for the spec array (inserted every time
2490 our controlling pref changes and at the end of the array).
2492 getCondition is a callback function that takes an array entry and
2493 returns the corresponding MemberCondition.
2495 getDataTuple is a callback function that takes an array entry and
2496 returns a tuple suitable to be passed to specFormatter.
2498 switchToCondition is a function that takes a MemberCondition and an array of
2499 previously generated spec entries. If None is passed for this function then all
2500 the interface members should return the same value from getCondition.
2503 def unsupportedSwitchToCondition(condition, specs):
2504 # If no specs have been added yet then this is just the first call to
2505 # switchToCondition that we call to avoid putting a specTerminator at the
2506 # front of the list.
2507 if len(specs) == 0:
2508 return
2509 raise "Not supported"
2511 if switchToCondition is None:
2512 switchToCondition = unsupportedSwitchToCondition
2514 specs = []
2515 numSpecsInCurPrefable = 0
2516 maxNumSpecsInPrefable = 0
2518 # So we won't put a specTerminator at the very front of the list:
2519 lastCondition = getCondition(array[0], descriptor)
2521 switchToCondition(lastCondition, specs)
2523 for member in array:
2524 curCondition = getCondition(member, descriptor)
2525 if lastCondition != curCondition:
2526 # Terminate previous list
2527 specs.append(specTerminator)
2528 if numSpecsInCurPrefable > maxNumSpecsInPrefable:
2529 maxNumSpecsInPrefable = numSpecsInCurPrefable
2530 numSpecsInCurPrefable = 0
2531 # And switch to our new condition
2532 switchToCondition(curCondition, specs)
2533 lastCondition = curCondition
2534 # And the actual spec
2535 specs.append(specFormatter(getDataTuple(member, descriptor)))
2536 numSpecsInCurPrefable += 1
2537 if numSpecsInCurPrefable > maxNumSpecsInPrefable:
2538 maxNumSpecsInPrefable = numSpecsInCurPrefable
2539 specs.append(specTerminator)
2541 return (specs, maxNumSpecsInPrefable)
2543 def generatePrefableArray(
2544 self,
2545 array,
2546 name,
2547 specFormatter,
2548 specTerminator,
2549 specType,
2550 getCondition,
2551 getDataTuple,
2554 This method generates our various arrays.
2556 array is an array of interface members as passed to generateArray
2558 name is the name as passed to generateArray
2560 specFormatter is a function that takes a single argument, a tuple,
2561 and returns a string, a spec array entry
2563 specTerminator is a terminator for the spec array (inserted every time
2564 our controlling pref changes and at the end of the array)
2566 specType is the actual typename of our spec
2568 getCondition is a callback function that takes an array entry and
2569 returns the corresponding MemberCondition.
2571 getDataTuple is a callback function that takes an array entry and
2572 returns a tuple suitable to be passed to specFormatter.
2575 # We want to generate a single list of specs, but with specTerminator
2576 # inserted at every point where the pref name controlling the member
2577 # changes. That will make sure the order of the properties as exposed
2578 # on the interface and interface prototype objects does not change when
2579 # pref control is added to members while still allowing us to define all
2580 # the members in the smallest number of JSAPI calls.
2581 assert len(array) != 0
2583 disablers = []
2584 prefableSpecs = []
2586 disablersTemplate = dedent(
2588 static const PrefableDisablers %s_disablers%d = {
2589 %s, %s, %s, %s, %s
2593 prefableWithDisablersTemplate = " { &%s_disablers%d, &%s_specs[%d] }"
2594 prefableWithoutDisablersTemplate = " { nullptr, &%s_specs[%d] }"
2596 def switchToCondition(condition, specs):
2597 # Set up pointers to the new sets of specs inside prefableSpecs
2598 if condition.hasDisablers():
2599 prefableSpecs.append(
2600 prefableWithDisablersTemplate % (name, len(specs), name, len(specs))
2602 disablers.append(
2603 disablersTemplate
2605 name,
2606 len(specs),
2607 condition.prefFuncIndex,
2608 condition.nonExposedGlobals,
2609 toStringBool(condition.secureContext),
2610 condition.trial,
2611 condition.func,
2614 else:
2615 prefableSpecs.append(
2616 prefableWithoutDisablersTemplate % (name, len(specs))
2619 specs, maxNumSpecsInPrefable = self.generatePrefableArrayValues(
2620 array,
2621 self.descriptor,
2622 specFormatter,
2623 specTerminator,
2624 getCondition,
2625 getDataTuple,
2626 switchToCondition,
2628 prefableSpecs.append(" { nullptr, nullptr }")
2630 specType = "const " + specType
2631 arrays = fill(
2633 static ${specType} ${name}_specs[] = {
2634 ${specs}
2637 ${disablers}
2638 static const Prefable<${specType}> ${name}[] = {
2639 ${prefableSpecs}
2642 """,
2643 specType=specType,
2644 name=name,
2645 disablers="\n".join(disablers),
2646 specs=",\n".join(specs),
2647 prefableSpecs=",\n".join(prefableSpecs),
2650 if self.usedForXrays():
2651 arrays = fill(
2653 $*{arrays}
2654 static_assert(${numPrefableSpecs} <= 1ull << NUM_BITS_PROPERTY_INFO_PREF_INDEX,
2655 "We have a prefable index that is >= (1 << NUM_BITS_PROPERTY_INFO_PREF_INDEX)");
2656 static_assert(${maxNumSpecsInPrefable} <= 1ull << NUM_BITS_PROPERTY_INFO_SPEC_INDEX,
2657 "We have a spec index that is >= (1 << NUM_BITS_PROPERTY_INFO_SPEC_INDEX)");
2659 """,
2660 arrays=arrays,
2661 # Minus 1 because there's a list terminator in prefableSpecs.
2662 numPrefableSpecs=len(prefableSpecs) - 1,
2663 maxNumSpecsInPrefable=maxNumSpecsInPrefable,
2666 return arrays
2669 # The length of a method is the minimum of the lengths of the
2670 # argument lists of all its overloads.
2671 def overloadLength(arguments):
2672 i = len(arguments)
2673 while i > 0 and arguments[i - 1].optional:
2674 i -= 1
2675 return i
2678 def methodLength(method):
2679 signatures = method.signatures()
2680 return min(overloadLength(arguments) for retType, arguments in signatures)
2683 def clearableCachedAttrs(descriptor):
2684 return (
2686 for m in descriptor.interface.members
2687 if m.isAttr() and
2688 # Constants should never need clearing!
2689 m.dependsOn != "Nothing" and m.slotIndices is not None
2693 def MakeClearCachedValueNativeName(member):
2694 return "ClearCached%sValue" % MakeNativeName(member.identifier.name)
2697 def IDLToCIdentifier(name):
2698 return name.replace("-", "_")
2701 def EnumerabilityFlags(member):
2702 if member.getExtendedAttribute("NonEnumerable"):
2703 return "0"
2704 return "JSPROP_ENUMERATE"
2707 class MethodDefiner(PropertyDefiner):
2709 A class for defining methods on a prototype object.
2712 def __init__(self, descriptor, name, crossOriginOnly, static, unforgeable=False):
2713 assert not (static and unforgeable)
2714 PropertyDefiner.__init__(self, descriptor, name)
2716 # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
2717 # We should be able to check for special operations without an
2718 # identifier. For now we check if the name starts with __
2720 # Ignore non-static methods for interfaces without a proto object
2721 if descriptor.interface.hasInterfacePrototypeObject() or static:
2722 methods = [
2724 for m in descriptor.interface.members
2725 if m.isMethod()
2726 and m.isStatic() == static
2727 and MemberIsLegacyUnforgeable(m, descriptor) == unforgeable
2728 and (
2729 not crossOriginOnly or m.getExtendedAttribute("CrossOriginCallable")
2731 and not m.isIdentifierLess()
2732 and not m.getExtendedAttribute("Unexposed")
2734 else:
2735 methods = []
2736 self.chrome = []
2737 self.regular = []
2738 for m in methods:
2739 method = self.methodData(m, descriptor)
2741 if m.isStatic():
2742 method["nativeName"] = CppKeywords.checkMethodName(
2743 IDLToCIdentifier(m.identifier.name)
2746 if isChromeOnly(m):
2747 self.chrome.append(method)
2748 else:
2749 self.regular.append(method)
2751 # TODO: Once iterable is implemented, use tiebreak rules instead of
2752 # failing. Also, may be more tiebreak rules to implement once spec bug
2753 # is resolved.
2754 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=28592
2755 def hasIterator(methods, regular):
2756 return any("@@iterator" in m.aliases for m in methods) or any(
2757 "@@iterator" == r["name"] for r in regular
2760 # Check whether we need to output an @@iterator due to having an indexed
2761 # getter. We only do this while outputting non-static and
2762 # non-unforgeable methods, since the @@iterator function will be
2763 # neither.
2764 if not static and not unforgeable and descriptor.supportsIndexedProperties():
2765 if hasIterator(methods, self.regular):
2766 raise TypeError(
2767 "Cannot have indexed getter/attr on "
2768 "interface %s with other members "
2769 "that generate @@iterator, such as "
2770 "maplike/setlike or aliased functions."
2771 % self.descriptor.interface.identifier.name
2773 self.regular.append(
2775 "name": "@@iterator",
2776 "methodInfo": False,
2777 "selfHostedName": "$ArrayValues",
2778 "length": 0,
2779 "flags": "0", # Not enumerable, per spec.
2780 "condition": MemberCondition(),
2784 # Generate the keys/values/entries aliases for value iterables.
2785 maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
2786 if (
2787 not static
2788 and not unforgeable
2789 and maplikeOrSetlikeOrIterable
2790 and maplikeOrSetlikeOrIterable.isIterable()
2791 and maplikeOrSetlikeOrIterable.isValueIterator()
2793 # Add our keys/values/entries/forEach
2794 self.regular.append(
2796 "name": "keys",
2797 "methodInfo": False,
2798 "selfHostedName": "ArrayKeys",
2799 "length": 0,
2800 "flags": "JSPROP_ENUMERATE",
2801 "condition": PropertyDefiner.getControllingCondition(
2802 maplikeOrSetlikeOrIterable, descriptor
2806 self.regular.append(
2808 "name": "values",
2809 "methodInfo": False,
2810 "selfHostedName": "$ArrayValues",
2811 "length": 0,
2812 "flags": "JSPROP_ENUMERATE",
2813 "condition": PropertyDefiner.getControllingCondition(
2814 maplikeOrSetlikeOrIterable, descriptor
2818 self.regular.append(
2820 "name": "entries",
2821 "methodInfo": False,
2822 "selfHostedName": "ArrayEntries",
2823 "length": 0,
2824 "flags": "JSPROP_ENUMERATE",
2825 "condition": PropertyDefiner.getControllingCondition(
2826 maplikeOrSetlikeOrIterable, descriptor
2830 self.regular.append(
2832 "name": "forEach",
2833 "methodInfo": False,
2834 "selfHostedName": "ArrayForEach",
2835 "length": 1,
2836 "flags": "JSPROP_ENUMERATE",
2837 "condition": PropertyDefiner.getControllingCondition(
2838 maplikeOrSetlikeOrIterable, descriptor
2843 if not static:
2844 stringifier = descriptor.operations["Stringifier"]
2845 if stringifier and unforgeable == MemberIsLegacyUnforgeable(
2846 stringifier, descriptor
2848 toStringDesc = {
2849 "name": GetWebExposedName(stringifier, descriptor),
2850 "nativeName": stringifier.identifier.name,
2851 "length": 0,
2852 "flags": "JSPROP_ENUMERATE",
2853 "condition": PropertyDefiner.getControllingCondition(
2854 stringifier, descriptor
2857 if isChromeOnly(stringifier):
2858 self.chrome.append(toStringDesc)
2859 else:
2860 self.regular.append(toStringDesc)
2861 if unforgeable and descriptor.interface.getExtendedAttribute(
2862 "LegacyUnforgeable"
2864 # Synthesize our valueOf method
2865 self.regular.append(
2867 "name": "valueOf",
2868 "selfHostedName": "Object_valueOf",
2869 "methodInfo": False,
2870 "length": 0,
2871 "flags": "0", # readonly/permanent added automatically.
2872 "condition": MemberCondition(),
2876 if descriptor.interface.isJSImplemented():
2877 if static:
2878 if descriptor.interface.hasInterfaceObject():
2879 self.chrome.append(
2881 "name": "_create",
2882 "nativeName": ("%s::_Create" % descriptor.name),
2883 "methodInfo": False,
2884 "length": 2,
2885 "flags": "0",
2886 "condition": MemberCondition(),
2890 self.unforgeable = unforgeable
2892 if static:
2893 if not descriptor.interface.hasInterfaceObject():
2894 # static methods go on the interface object
2895 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
2896 else:
2897 if not descriptor.interface.hasInterfacePrototypeObject():
2898 # non-static methods go on the interface prototype object
2899 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
2901 @staticmethod
2902 def methodData(m, descriptor, overrideFlags=None):
2903 return {
2904 "name": m.identifier.name,
2905 "methodInfo": not m.isStatic(),
2906 "length": methodLength(m),
2907 "flags": EnumerabilityFlags(m)
2908 if (overrideFlags is None)
2909 else overrideFlags,
2910 "condition": PropertyDefiner.getControllingCondition(m, descriptor),
2911 "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
2912 "returnsPromise": m.returnsPromise(),
2913 "hasIteratorAlias": "@@iterator" in m.aliases,
2916 @staticmethod
2917 def formatSpec(fields):
2918 if fields[0].startswith("@@"):
2919 fields = (fields[0][2:],) + fields[1:]
2920 return " JS_SYM_FNSPEC(%s, %s, %s, %s, %s, %s)" % fields
2921 return ' JS_FNSPEC("%s", %s, %s, %s, %s, %s)' % fields
2923 @staticmethod
2924 def specData(m, descriptor, unforgeable=False):
2925 def flags(m, unforgeable):
2926 unforgeable = " | JSPROP_PERMANENT | JSPROP_READONLY" if unforgeable else ""
2927 return m["flags"] + unforgeable
2929 if "selfHostedName" in m:
2930 selfHostedName = '"%s"' % m["selfHostedName"]
2931 assert not m.get("methodInfo", True)
2932 accessor = "nullptr"
2933 jitinfo = "nullptr"
2934 else:
2935 selfHostedName = "nullptr"
2936 # When defining symbols, function name may not match symbol name
2937 methodName = m.get("methodName", m["name"])
2938 accessor = m.get("nativeName", IDLToCIdentifier(methodName))
2939 if m.get("methodInfo", True):
2940 if m.get("returnsPromise", False):
2941 exceptionPolicy = "ConvertExceptionsToPromises"
2942 else:
2943 exceptionPolicy = "ThrowExceptions"
2945 # Cast this in case the methodInfo is a
2946 # JSTypedMethodJitInfo.
2947 jitinfo = (
2948 "reinterpret_cast<const JSJitInfo*>(&%s_methodinfo)" % accessor
2950 if m.get("allowCrossOriginThis", False):
2951 accessor = (
2952 "(GenericMethod<CrossOriginThisPolicy, %s>)" % exceptionPolicy
2954 elif descriptor.interface.hasDescendantWithCrossOriginMembers:
2955 accessor = (
2956 "(GenericMethod<MaybeCrossOriginObjectThisPolicy, %s>)"
2957 % exceptionPolicy
2959 elif descriptor.interface.isOnGlobalProtoChain():
2960 accessor = (
2961 "(GenericMethod<MaybeGlobalThisPolicy, %s>)" % exceptionPolicy
2963 else:
2964 accessor = "(GenericMethod<NormalThisPolicy, %s>)" % exceptionPolicy
2965 else:
2966 if m.get("returnsPromise", False):
2967 jitinfo = "&%s_methodinfo" % accessor
2968 accessor = "StaticMethodPromiseWrapper"
2969 else:
2970 jitinfo = "nullptr"
2972 return (
2973 m["name"],
2974 accessor,
2975 jitinfo,
2976 m["length"],
2977 flags(m, unforgeable),
2978 selfHostedName,
2981 @staticmethod
2982 def condition(m, d):
2983 return m["condition"]
2985 def generateArray(self, array, name):
2986 if len(array) == 0:
2987 return ""
2989 return self.generatePrefableArray(
2990 array,
2991 name,
2992 self.formatSpec,
2993 " JS_FS_END",
2994 "JSFunctionSpec",
2995 self.condition,
2996 functools.partial(self.specData, unforgeable=self.unforgeable),
3000 class AttrDefiner(PropertyDefiner):
3001 def __init__(self, descriptor, name, crossOriginOnly, static, unforgeable=False):
3002 assert not (static and unforgeable)
3003 PropertyDefiner.__init__(self, descriptor, name)
3004 self.name = name
3005 # Ignore non-static attributes for interfaces without a proto object
3006 if descriptor.interface.hasInterfacePrototypeObject() or static:
3007 idlAttrs = [
3009 for m in descriptor.interface.members
3010 if m.isAttr()
3011 and m.isStatic() == static
3012 and MemberIsLegacyUnforgeable(m, descriptor) == unforgeable
3013 and (
3014 not crossOriginOnly
3015 or m.getExtendedAttribute("CrossOriginReadable")
3016 or m.getExtendedAttribute("CrossOriginWritable")
3019 else:
3020 idlAttrs = []
3022 attributes = []
3023 for attr in idlAttrs:
3024 attributes.extend(self.attrData(attr, unforgeable))
3025 self.chrome = [m for m in attributes if isChromeOnly(m["attr"])]
3026 self.regular = [m for m in attributes if not isChromeOnly(m["attr"])]
3027 self.static = static
3029 if static:
3030 if not descriptor.interface.hasInterfaceObject():
3031 # static attributes go on the interface object
3032 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
3033 else:
3034 if not descriptor.interface.hasInterfacePrototypeObject():
3035 # non-static attributes go on the interface prototype object
3036 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
3038 @staticmethod
3039 def attrData(attr, unforgeable=False, overrideFlags=None):
3040 if overrideFlags is None:
3041 permanent = " | JSPROP_PERMANENT" if unforgeable else ""
3042 flags = EnumerabilityFlags(attr) + permanent
3043 else:
3044 flags = overrideFlags
3045 return (
3046 {"name": name, "attr": attr, "flags": flags}
3047 for name in [attr.identifier.name] + attr.bindingAliases
3050 @staticmethod
3051 def condition(m, d):
3052 return PropertyDefiner.getControllingCondition(m["attr"], d)
3054 @staticmethod
3055 def specData(entry, descriptor, static=False, crossOriginOnly=False):
3056 def getter(attr):
3057 if crossOriginOnly and not attr.getExtendedAttribute("CrossOriginReadable"):
3058 return "nullptr, nullptr"
3059 if static:
3060 if attr.type.isPromise():
3061 raise TypeError(
3062 "Don't know how to handle "
3063 "static Promise-returning "
3064 "attribute %s.%s" % (descriptor.name, attr.identifier.name)
3066 accessor = "get_" + IDLToCIdentifier(attr.identifier.name)
3067 jitinfo = "nullptr"
3068 else:
3069 if attr.type.isPromise():
3070 exceptionPolicy = "ConvertExceptionsToPromises"
3071 else:
3072 exceptionPolicy = "ThrowExceptions"
3074 if attr.hasLegacyLenientThis():
3075 if attr.getExtendedAttribute("CrossOriginReadable"):
3076 raise TypeError(
3077 "Can't handle lenient cross-origin "
3078 "readable attribute %s.%s"
3079 % (descriptor.name, attr.identifier.name)
3081 if descriptor.interface.hasDescendantWithCrossOriginMembers:
3082 accessor = (
3083 "GenericGetter<MaybeCrossOriginObjectLenientThisPolicy, %s>"
3084 % exceptionPolicy
3086 else:
3087 accessor = (
3088 "GenericGetter<LenientThisPolicy, %s>" % exceptionPolicy
3090 elif attr.getExtendedAttribute("CrossOriginReadable"):
3091 accessor = (
3092 "GenericGetter<CrossOriginThisPolicy, %s>" % exceptionPolicy
3094 elif descriptor.interface.hasDescendantWithCrossOriginMembers:
3095 accessor = (
3096 "GenericGetter<MaybeCrossOriginObjectThisPolicy, %s>"
3097 % exceptionPolicy
3099 elif descriptor.interface.isOnGlobalProtoChain():
3100 accessor = (
3101 "GenericGetter<MaybeGlobalThisPolicy, %s>" % exceptionPolicy
3103 else:
3104 accessor = "GenericGetter<NormalThisPolicy, %s>" % exceptionPolicy
3105 jitinfo = "&%s_getterinfo" % IDLToCIdentifier(attr.identifier.name)
3106 return "%s, %s" % (accessor, jitinfo)
3108 def setter(attr):
3109 if (
3110 attr.readonly
3111 and attr.getExtendedAttribute("PutForwards") is None
3112 and attr.getExtendedAttribute("Replaceable") is None
3113 and attr.getExtendedAttribute("LegacyLenientSetter") is None
3115 return "nullptr, nullptr"
3116 if crossOriginOnly and not attr.getExtendedAttribute("CrossOriginWritable"):
3117 return "nullptr, nullptr"
3118 if static:
3119 accessor = "set_" + IDLToCIdentifier(attr.identifier.name)
3120 jitinfo = "nullptr"
3121 else:
3122 if attr.hasLegacyLenientThis():
3123 if attr.getExtendedAttribute("CrossOriginWritable"):
3124 raise TypeError(
3125 "Can't handle lenient cross-origin "
3126 "writable attribute %s.%s"
3127 % (descriptor.name, attr.identifier.name)
3129 if descriptor.interface.hasDescendantWithCrossOriginMembers:
3130 accessor = (
3131 "GenericSetter<MaybeCrossOriginObjectLenientThisPolicy>"
3133 else:
3134 accessor = "GenericSetter<LenientThisPolicy>"
3135 elif attr.getExtendedAttribute("CrossOriginWritable"):
3136 accessor = "GenericSetter<CrossOriginThisPolicy>"
3137 elif descriptor.interface.hasDescendantWithCrossOriginMembers:
3138 accessor = "GenericSetter<MaybeCrossOriginObjectThisPolicy>"
3139 elif descriptor.interface.isOnGlobalProtoChain():
3140 accessor = "GenericSetter<MaybeGlobalThisPolicy>"
3141 else:
3142 accessor = "GenericSetter<NormalThisPolicy>"
3143 jitinfo = "&%s_setterinfo" % IDLToCIdentifier(attr.identifier.name)
3144 return "%s, %s" % (accessor, jitinfo)
3146 name, attr, flags = entry["name"], entry["attr"], entry["flags"]
3147 return (name, flags, getter(attr), setter(attr))
3149 @staticmethod
3150 def formatSpec(fields):
3151 return ' JSPropertySpec::nativeAccessors("%s", %s, %s, %s)' % fields
3153 def generateArray(self, array, name):
3154 if len(array) == 0:
3155 return ""
3157 return self.generatePrefableArray(
3158 array,
3159 name,
3160 self.formatSpec,
3161 " JS_PS_END",
3162 "JSPropertySpec",
3163 self.condition,
3164 functools.partial(self.specData, static=self.static),
3168 class ConstDefiner(PropertyDefiner):
3170 A class for definining constants on the interface object
3173 def __init__(self, descriptor, name):
3174 PropertyDefiner.__init__(self, descriptor, name)
3175 self.name = name
3176 constants = [m for m in descriptor.interface.members if m.isConst()]
3177 self.chrome = [m for m in constants if isChromeOnly(m)]
3178 self.regular = [m for m in constants if not isChromeOnly(m)]
3180 def generateArray(self, array, name):
3181 if len(array) == 0:
3182 return ""
3184 def specData(const, descriptor):
3185 return (const.identifier.name, convertConstIDLValueToJSVal(const.value))
3187 return self.generatePrefableArray(
3188 array,
3189 name,
3190 lambda fields: ' { "%s", %s }' % fields,
3191 " { 0, JS::UndefinedValue() }",
3192 "ConstantSpec",
3193 PropertyDefiner.getControllingCondition,
3194 specData,
3198 class PropertyArrays:
3199 def __init__(self, descriptor, crossOriginOnly=False):
3200 self.staticMethods = MethodDefiner(
3201 descriptor, "StaticMethods", crossOriginOnly, static=True
3203 self.staticAttrs = AttrDefiner(
3204 descriptor, "StaticAttributes", crossOriginOnly, static=True
3206 self.methods = MethodDefiner(
3207 descriptor, "Methods", crossOriginOnly, static=False
3209 self.attrs = AttrDefiner(
3210 descriptor, "Attributes", crossOriginOnly, static=False
3212 self.unforgeableMethods = MethodDefiner(
3213 descriptor,
3214 "UnforgeableMethods",
3215 crossOriginOnly,
3216 static=False,
3217 unforgeable=True,
3219 self.unforgeableAttrs = AttrDefiner(
3220 descriptor,
3221 "UnforgeableAttributes",
3222 crossOriginOnly,
3223 static=False,
3224 unforgeable=True,
3226 self.consts = ConstDefiner(descriptor, "Constants")
3228 @staticmethod
3229 def arrayNames():
3230 return [
3231 "staticMethods",
3232 "staticAttrs",
3233 "methods",
3234 "attrs",
3235 "unforgeableMethods",
3236 "unforgeableAttrs",
3237 "consts",
3240 def hasChromeOnly(self):
3241 return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())
3243 def hasNonChromeOnly(self):
3244 return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames())
3246 def __str__(self):
3247 define = ""
3248 for array in self.arrayNames():
3249 define += str(getattr(self, array))
3250 return define
3253 class CGConstDefinition(CGThing):
3255 Given a const member of an interface, return the C++ static const definition
3256 for the member. Should be part of the interface namespace in the header
3257 file.
3260 def __init__(self, member):
3261 assert (
3262 member.isConst()
3263 and member.value.type.isPrimitive()
3264 and not member.value.type.nullable()
3267 name = CppKeywords.checkMethodName(IDLToCIdentifier(member.identifier.name))
3268 tag = member.value.type.tag()
3269 value = member.value.value
3270 if tag == IDLType.Tags.bool:
3271 value = toStringBool(member.value.value)
3272 self.const = "static const %s %s = %s;" % (builtinNames[tag], name, value)
3274 def declare(self):
3275 return self.const
3277 def define(self):
3278 return ""
3280 def deps(self):
3281 return []
3284 class CGNativeProperties(CGList):
3285 def __init__(self, descriptor, properties):
3286 def generateNativeProperties(name, chrome):
3287 def check(p):
3288 return p.hasChromeOnly() if chrome else p.hasNonChromeOnly()
3290 nativePropsInts = []
3291 nativePropsPtrs = []
3292 nativePropsDuos = []
3294 duosOffset = 0
3295 idsOffset = 0
3296 for array in properties.arrayNames():
3297 propertyArray = getattr(properties, array)
3298 if check(propertyArray):
3299 varName = propertyArray.variableName(chrome)
3300 bitfields = "true, %d /* %s */" % (duosOffset, varName)
3301 duosOffset += 1
3302 nativePropsInts.append(CGGeneric(bitfields))
3304 if propertyArray.usedForXrays():
3305 ids = "&%s_propertyInfos[%d]" % (name, idsOffset)
3306 idsOffset += propertyArray.length(chrome)
3307 else:
3308 ids = "nullptr"
3309 duo = "{ %s, %s }" % (varName, ids)
3310 nativePropsDuos.append(CGGeneric(duo))
3311 else:
3312 bitfields = "false, 0"
3313 nativePropsInts.append(CGGeneric(bitfields))
3315 iteratorAliasIndex = -1
3316 for index, item in enumerate(properties.methods.regular):
3317 if item.get("hasIteratorAlias"):
3318 iteratorAliasIndex = index
3319 break
3320 nativePropsInts.append(CGGeneric(str(iteratorAliasIndex)))
3322 nativePropsDuos = [
3323 CGWrapper(
3324 CGIndenter(CGList(nativePropsDuos, ",\n")), pre="{\n", post="\n}"
3328 pre = "static const NativePropertiesN<%d> %s = {\n" % (duosOffset, name)
3329 post = "\n};\n"
3330 if descriptor.wantsXrays:
3331 pre = fill(
3333 static uint16_t ${name}_sortedPropertyIndices[${size}];
3334 static PropertyInfo ${name}_propertyInfos[${size}];
3336 $*{pre}
3337 """,
3338 name=name,
3339 size=idsOffset,
3340 pre=pre,
3342 if iteratorAliasIndex > 0:
3343 # The iteratorAliasMethodIndex is a signed integer, so the
3344 # max value it can store is 2^(nbits-1)-1.
3345 post = fill(
3347 $*{post}
3348 static_assert(${iteratorAliasIndex} < 1ull << (CHAR_BIT * sizeof(${name}.iteratorAliasMethodIndex) - 1),
3349 "We have an iterator alias index that is oversized");
3350 """,
3351 post=post,
3352 iteratorAliasIndex=iteratorAliasIndex,
3353 name=name,
3355 post = fill(
3357 $*{post}
3358 static_assert(${propertyInfoCount} < 1ull << (CHAR_BIT * sizeof(${name}.propertyInfoCount)),
3359 "We have a property info count that is oversized");
3360 """,
3361 post=post,
3362 propertyInfoCount=idsOffset,
3363 name=name,
3365 nativePropsInts.append(CGGeneric("%d" % idsOffset))
3366 nativePropsPtrs.append(CGGeneric("%s_sortedPropertyIndices" % name))
3367 else:
3368 nativePropsInts.append(CGGeneric("0"))
3369 nativePropsPtrs.append(CGGeneric("nullptr"))
3370 nativeProps = nativePropsInts + nativePropsPtrs + nativePropsDuos
3371 return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")), pre=pre, post=post)
3373 nativeProperties = []
3374 if properties.hasNonChromeOnly():
3375 nativeProperties.append(
3376 generateNativeProperties("sNativeProperties", False)
3378 if properties.hasChromeOnly():
3379 nativeProperties.append(
3380 generateNativeProperties("sChromeOnlyNativeProperties", True)
3383 CGList.__init__(self, nativeProperties, "\n")
3385 def declare(self):
3386 return ""
3388 def define(self):
3389 return CGList.define(self)
3392 class CGCollectJSONAttributesMethod(CGAbstractMethod):
3394 Generate the CollectJSONAttributes method for an interface descriptor
3397 def __init__(self, descriptor, toJSONMethod):
3398 args = [
3399 Argument("JSContext*", "cx"),
3400 Argument("JS::Handle<JSObject*>", "obj"),
3401 Argument("%s*" % descriptor.nativeType, "self"),
3402 Argument("JS::Rooted<JSObject*>&", "result"),
3404 CGAbstractMethod.__init__(
3405 self, descriptor, "CollectJSONAttributes", "bool", args, canRunScript=True
3407 self.toJSONMethod = toJSONMethod
3409 def definition_body(self):
3410 ret = ""
3411 interface = self.descriptor.interface
3412 toJSONCondition = PropertyDefiner.getControllingCondition(
3413 self.toJSONMethod, self.descriptor
3415 needUnwrappedObj = False
3416 for m in interface.members:
3417 if m.isAttr() and not m.isStatic() and m.type.isJSONType():
3418 getAndDefine = fill(
3420 JS::Rooted<JS::Value> temp(cx);
3421 if (!get_${name}(cx, obj, self, JSJitGetterCallArgs(&temp))) {
3422 return false;
3424 if (!JS_DefineProperty(cx, result, "${name}", temp, JSPROP_ENUMERATE)) {
3425 return false;
3427 """,
3428 name=IDLToCIdentifier(m.identifier.name),
3430 # Make sure we don't include things which are supposed to be
3431 # disabled. Things that either don't have disablers or whose
3432 # disablers match the disablers for our toJSON method can't
3433 # possibly be disabled, but other things might be.
3434 condition = PropertyDefiner.getControllingCondition(m, self.descriptor)
3435 if condition.hasDisablers() and condition != toJSONCondition:
3436 needUnwrappedObj = True
3437 ret += fill(
3439 // This is unfortunately a linear scan through sAttributes, but we
3440 // only do it for things which _might_ be disabled, which should
3441 // help keep the performance problems down.
3442 if (IsGetterEnabled(cx, unwrappedObj, (JSJitGetterOp)get_${name}, sAttributes)) {
3443 $*{getAndDefine}
3445 """,
3446 name=IDLToCIdentifier(m.identifier.name),
3447 getAndDefine=getAndDefine,
3449 else:
3450 ret += fill(
3452 { // scope for "temp"
3453 $*{getAndDefine}
3455 """,
3456 getAndDefine=getAndDefine,
3458 ret += "return true;\n"
3460 if needUnwrappedObj:
3461 # If we started allowing cross-origin objects here, we'd need to
3462 # use CheckedUnwrapDynamic and figure out whether it makes sense.
3463 # But in practice no one is trying to add toJSON methods to those,
3464 # so let's just guard against it.
3465 assert not self.descriptor.isMaybeCrossOriginObject()
3466 ret = fill(
3468 JS::Rooted<JSObject*> unwrappedObj(cx, js::CheckedUnwrapStatic(obj));
3469 if (!unwrappedObj) {
3470 // How did that happen? We managed to get called with that
3471 // object as "this"! Just give up on sanity.
3472 return false;
3475 $*{ret}
3476 """,
3477 ret=ret,
3480 return ret
3483 class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
3485 Generate the CreateInterfaceObjects method for an interface descriptor.
3487 properties should be a PropertyArrays instance.
3490 def __init__(
3491 self, descriptor, properties, haveUnscopables, haveLegacyWindowAliases, static
3493 args = [
3494 Argument("JSContext*", "aCx"),
3495 Argument("JS::Handle<JSObject*>", "aGlobal"),
3496 Argument("ProtoAndIfaceCache&", "aProtoAndIfaceCache"),
3497 Argument("bool", "aDefineOnGlobal"),
3499 CGAbstractMethod.__init__(
3500 self, descriptor, "CreateInterfaceObjects", "void", args, static=static
3502 self.properties = properties
3503 self.haveUnscopables = haveUnscopables
3504 self.haveLegacyWindowAliases = haveLegacyWindowAliases
3506 def definition_body(self):
3507 needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
3508 if needInterfaceObject:
3509 (protoGetter, protoHandleGetter) = InterfaceObjectProtoGetter(
3510 self.descriptor
3512 if protoHandleGetter is None:
3513 getConstructorProto = "aCx, " + protoGetter
3514 constructorProtoType = "Rooted"
3515 else:
3516 getConstructorProto = protoHandleGetter
3517 constructorProtoType = "Handle"
3519 getConstructorProto = fill(
3521 JS::${type}<JSObject*> constructorProto(${getConstructorProto}(aCx));
3522 if (!constructorProto) {
3523 return;
3525 """,
3526 type=constructorProtoType,
3527 getConstructorProto=getConstructorProto,
3530 interfaceInfo = "&sInterfaceObjectInfo"
3531 interfaceCache = (
3532 "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)"
3533 % self.descriptor.name
3535 getConstructorProto = CGGeneric(getConstructorProto)
3536 constructorProto = "constructorProto"
3537 else:
3538 # We don't have slots to store the legacy factory functions.
3539 assert len(self.descriptor.interface.legacyFactoryFunctions) == 0
3540 interfaceInfo = "nullptr"
3541 interfaceCache = "nullptr"
3542 getConstructorProto = None
3543 constructorProto = "nullptr"
3545 if self.properties.hasNonChromeOnly():
3546 properties = "sNativeProperties.Upcast()"
3547 else:
3548 properties = "nullptr"
3549 if self.properties.hasChromeOnly():
3550 chromeProperties = "sChromeOnlyNativeProperties.Upcast()"
3551 else:
3552 chromeProperties = "nullptr"
3554 # We use getClassName here. This should be the right thing to pass as
3555 # the name argument to CreateInterfaceObjects. This is generally the
3556 # interface identifier, except for the synthetic interfaces created for
3557 # the default iterator objects. If needInterfaceObject is true then
3558 # we'll use the name to install a property on the global object, so
3559 # there shouldn't be any spaces in the name.
3560 name = self.descriptor.interface.getClassName()
3561 assert not (needInterfaceObject and " " in name)
3563 if self.descriptor.interface.isNamespace():
3564 # If we don't need to create anything, why are we generating this?
3565 assert needInterfaceObject
3567 call = fill(
3569 JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
3570 dom::CreateNamespaceObject(aCx, aGlobal, ${constructorProto},
3571 sNamespaceObjectClass,
3572 interfaceCache,
3573 ${properties},
3574 ${chromeProperties},
3575 "${name}", aDefineOnGlobal);
3576 """,
3577 interfaceCache=interfaceCache,
3578 constructorProto=constructorProto,
3579 properties=properties,
3580 chromeProperties=chromeProperties,
3581 name=name,
3583 return CGList(
3585 getConstructorProto,
3586 CGGeneric(call),
3588 "\n",
3589 ).define()
3591 needInterfacePrototypeObject = (
3592 self.descriptor.interface.hasInterfacePrototypeObject()
3595 # If we don't need to create anything, why are we generating this?
3596 assert needInterfaceObject or needInterfacePrototypeObject
3598 if needInterfacePrototypeObject:
3599 (protoGetter, protoHandleGetter) = InterfacePrototypeObjectProtoGetter(
3600 self.descriptor
3602 if protoHandleGetter is None:
3603 parentProtoType = "Rooted"
3604 getParentProto = "aCx, " + protoGetter
3605 else:
3606 parentProtoType = "Handle"
3607 getParentProto = protoHandleGetter
3609 getParentProto = fill(
3611 JS::${type}<JSObject*> parentProto(${getParentProto}(aCx));
3612 if (!parentProto) {
3613 return;
3615 """,
3616 type=parentProtoType,
3617 getParentProto=getParentProto,
3620 protoClass = "&sPrototypeClass"
3621 protoCache = (
3622 "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)"
3623 % self.descriptor.name
3625 parentProto = "parentProto"
3626 getParentProto = CGGeneric(getParentProto)
3627 else:
3628 protoClass = "nullptr"
3629 protoCache = "nullptr"
3630 parentProto = "nullptr"
3631 getParentProto = None
3633 if self.descriptor.interface.ctor():
3634 constructArgs = methodLength(self.descriptor.interface.ctor())
3635 isConstructorChromeOnly = isChromeOnly(self.descriptor.interface.ctor())
3636 else:
3637 constructArgs = 0
3638 isConstructorChromeOnly = False
3639 if len(self.descriptor.interface.legacyFactoryFunctions) > 0:
3640 legacyFactoryFunctions = "Span(legacyFactoryFunctions)"
3641 else:
3642 legacyFactoryFunctions = "Span<const LegacyFactoryFunction, 0>{}"
3644 isGlobal = self.descriptor.isGlobal() is not None
3646 ensureCaches = fill(
3648 JS::Heap<JSObject*>* protoCache = ${protoCache};
3649 JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
3650 """,
3651 protoCache=protoCache,
3652 interfaceCache=interfaceCache,
3654 call = fill(
3656 dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
3657 ${protoClass}, protoCache,
3658 ${constructorProto}, ${interfaceInfo}, ${constructArgs}, ${isConstructorChromeOnly}, ${legacyFactoryFunctions},
3659 interfaceCache,
3660 ${properties},
3661 ${chromeProperties},
3662 "${name}", aDefineOnGlobal,
3663 ${unscopableNames},
3664 ${isGlobal},
3665 ${legacyWindowAliases});
3666 """,
3667 protoClass=protoClass,
3668 parentProto=parentProto,
3669 constructorProto=constructorProto,
3670 interfaceInfo=interfaceInfo,
3671 constructArgs=constructArgs,
3672 isConstructorChromeOnly=toStringBool(isConstructorChromeOnly),
3673 legacyFactoryFunctions=legacyFactoryFunctions,
3674 properties=properties,
3675 chromeProperties=chromeProperties,
3676 name=name,
3677 unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr",
3678 isGlobal=toStringBool(isGlobal),
3679 legacyWindowAliases="legacyWindowAliases"
3680 if self.haveLegacyWindowAliases
3681 else "nullptr",
3684 # If we fail after here, we must clear interface and prototype caches
3685 # using this code: intermediate failure must not expose the interface in
3686 # partially-constructed state. Note that every case after here needs an
3687 # interface prototype object.
3688 failureCode = dedent(
3690 *protoCache = nullptr;
3691 if (interfaceCache) {
3692 *interfaceCache = nullptr;
3694 return;
3698 needProtoVar = False
3700 aliasedMembers = [
3701 m for m in self.descriptor.interface.members if m.isMethod() and m.aliases
3703 if aliasedMembers:
3704 assert needInterfacePrototypeObject
3706 def defineAlias(alias):
3707 if alias == "@@iterator" or alias == "@@asyncIterator":
3708 name = alias[2:]
3710 symbolJSID = (
3711 "JS::GetWellKnownSymbolKey(aCx, JS::SymbolCode::%s)" % name
3713 prop = "%sId" % name
3714 getSymbolJSID = CGGeneric(
3715 fill(
3716 "JS::Rooted<jsid> ${prop}(aCx, ${symbolJSID});",
3717 prop=prop,
3718 symbolJSID=symbolJSID,
3721 defineFn = "JS_DefinePropertyById"
3722 enumFlags = "0" # Not enumerable, per spec.
3723 elif alias.startswith("@@"):
3724 raise TypeError(
3725 "Can't handle any well-known Symbol other than @@iterator and @@asyncIterator"
3727 else:
3728 getSymbolJSID = None
3729 defineFn = "JS_DefineProperty"
3730 prop = '"%s"' % alias
3731 # XXX If we ever create non-enumerable properties that can
3732 # be aliased, we should consider making the aliases
3733 # match the enumerability of the property being aliased.
3734 enumFlags = "JSPROP_ENUMERATE"
3735 return CGList(
3737 getSymbolJSID,
3738 CGGeneric(
3739 fill(
3741 if (!${defineFn}(aCx, proto, ${prop}, aliasedVal, ${enumFlags})) {
3742 $*{failureCode}
3744 """,
3745 defineFn=defineFn,
3746 prop=prop,
3747 enumFlags=enumFlags,
3748 failureCode=failureCode,
3752 "\n",
3755 def defineAliasesFor(m):
3756 return CGList(
3758 CGGeneric(
3759 fill(
3761 if (!JS_GetProperty(aCx, proto, \"${prop}\", &aliasedVal)) {
3762 $*{failureCode}
3764 """,
3765 failureCode=failureCode,
3766 prop=m.identifier.name,
3770 + [defineAlias(alias) for alias in sorted(m.aliases)]
3773 defineAliases = CGList(
3775 CGGeneric(
3776 dedent(
3778 // Set up aliases on the interface prototype object we just created.
3782 CGGeneric("JS::Rooted<JS::Value> aliasedVal(aCx);\n\n"),
3785 defineAliasesFor(m)
3786 for m in sorted(aliasedMembers, key=lambda m: m.identifier.name)
3789 needProtoVar = True
3790 else:
3791 defineAliases = None
3793 # Globals handle unforgeables directly in Wrap() instead of
3794 # via a holder.
3795 if (
3796 self.descriptor.hasLegacyUnforgeableMembers
3797 and not self.descriptor.isGlobal()
3799 assert needInterfacePrototypeObject
3801 # We want to use the same JSClass and prototype as the object we'll
3802 # end up defining the unforgeable properties on in the end, so that
3803 # we can use JS_InitializePropertiesFromCompatibleNativeObject to do
3804 # a fast copy. In the case of proxies that's null, because the
3805 # expando object is a vanilla object, but in the case of other DOM
3806 # objects it's whatever our class is.
3807 if self.descriptor.proxy:
3808 holderClass = "nullptr"
3809 holderProto = "nullptr"
3810 else:
3811 holderClass = "sClass.ToJSClass()"
3812 holderProto = "proto"
3813 needProtoVar = True
3814 createUnforgeableHolder = CGGeneric(
3815 fill(
3817 JS::Rooted<JSObject*> unforgeableHolder(
3818 aCx, JS_NewObjectWithoutMetadata(aCx, ${holderClass}, ${holderProto}));
3819 if (!unforgeableHolder) {
3820 $*{failureCode}
3822 """,
3823 holderProto=holderProto,
3824 holderClass=holderClass,
3825 failureCode=failureCode,
3828 defineUnforgeables = InitUnforgeablePropertiesOnHolder(
3829 self.descriptor, self.properties, failureCode
3831 createUnforgeableHolder = CGList(
3832 [createUnforgeableHolder, defineUnforgeables]
3835 installUnforgeableHolder = CGGeneric(
3836 dedent(
3838 if (*protoCache) {
3839 JS::SetReservedSlot(*protoCache, DOM_INTERFACE_PROTO_SLOTS_BASE,
3840 JS::ObjectValue(*unforgeableHolder));
3846 unforgeableHolderSetup = CGList(
3847 [createUnforgeableHolder, installUnforgeableHolder], "\n"
3849 else:
3850 unforgeableHolderSetup = None
3852 # FIXME Unclear whether this is needed for hasOrdinaryObjectPrototype
3853 if (
3854 self.descriptor.interface.isOnGlobalProtoChain()
3855 and needInterfacePrototypeObject
3856 and not self.descriptor.hasOrdinaryObjectPrototype
3858 makeProtoPrototypeImmutable = CGGeneric(
3859 fill(
3862 bool succeeded;
3863 if (!JS_SetImmutablePrototype(aCx, proto, &succeeded)) {
3864 $*{failureCode}
3867 MOZ_ASSERT(succeeded,
3868 "making a fresh prototype object's [[Prototype]] "
3869 "immutable can internally fail, but it should "
3870 "never be unsuccessful");
3872 """,
3873 protoCache=protoCache,
3874 failureCode=failureCode,
3877 needProtoVar = True
3878 else:
3879 makeProtoPrototypeImmutable = None
3881 if needProtoVar:
3882 defineProtoVar = CGGeneric(
3883 fill(
3885 JS::AssertObjectIsNotGray(*protoCache);
3886 JS::Handle<JSObject*> proto = JS::Handle<JSObject*>::fromMarkedLocation(protoCache->address());
3887 if (!proto) {
3888 $*{failureCode}
3890 """,
3891 failureCode=failureCode,
3894 else:
3895 defineProtoVar = None
3897 # ensureCaches needs to come first as it crashes on failure (like OOM).
3898 # We want to make sure that the caches do exist before we try to return
3899 # to the caller, so it can rely on that (and detect other failures by
3900 # checking for null in the caches).
3901 return CGList(
3903 CGGeneric(ensureCaches),
3904 getParentProto,
3905 getConstructorProto,
3906 CGGeneric(call),
3907 defineProtoVar,
3908 defineAliases,
3909 unforgeableHolderSetup,
3910 makeProtoPrototypeImmutable,
3912 "\n",
3913 ).define()
3916 class CGGetProtoObjectHandleMethod(CGAbstractMethod):
3918 A method for getting the interface prototype object.
3921 def __init__(self, descriptor, static, signatureOnly=False):
3922 CGAbstractMethod.__init__(
3923 self,
3924 descriptor,
3925 "GetProtoObjectHandle",
3926 "JS::Handle<JSObject*>",
3927 [Argument("JSContext*", "aCx")],
3928 inline=True,
3929 static=static,
3930 signatureOnly=signatureOnly,
3933 def definition_body(self):
3934 return fill(
3936 /* Get the interface prototype object for this class. This will create the
3937 object as needed. */
3938 return GetPerInterfaceObjectHandle(aCx, prototypes::id::${name},
3939 &CreateInterfaceObjects,
3940 /* aDefineOnGlobal = */ true);
3942 """,
3943 name=self.descriptor.name,
3947 class CGGetProtoObjectMethod(CGAbstractMethod):
3949 A method for getting the interface prototype object.
3952 def __init__(self, descriptor):
3953 CGAbstractMethod.__init__(
3954 self,
3955 descriptor,
3956 "GetProtoObject",
3957 "JSObject*",
3958 [Argument("JSContext*", "aCx")],
3961 def definition_body(self):
3962 return "return GetProtoObjectHandle(aCx);\n"
3965 class CGGetConstructorObjectHandleMethod(CGAbstractMethod):
3967 A method for getting the interface constructor object.
3970 def __init__(self, descriptor):
3971 CGAbstractMethod.__init__(
3972 self,
3973 descriptor,
3974 "GetConstructorObjectHandle",
3975 "JS::Handle<JSObject*>",
3977 Argument("JSContext*", "aCx"),
3978 Argument("bool", "aDefineOnGlobal", "true"),
3980 inline=True,
3983 def definition_body(self):
3984 return fill(
3986 /* Get the interface object for this class. This will create the object as
3987 needed. */
3989 return GetPerInterfaceObjectHandle(aCx, constructors::id::${name},
3990 &CreateInterfaceObjects,
3991 aDefineOnGlobal);
3992 """,
3993 name=self.descriptor.name,
3997 class CGGetConstructorObjectMethod(CGAbstractMethod):
3999 A method for getting the interface constructor object.
4002 def __init__(self, descriptor):
4003 CGAbstractMethod.__init__(
4004 self,
4005 descriptor,
4006 "GetConstructorObject",
4007 "JSObject*",
4008 [Argument("JSContext*", "aCx")],
4011 def definition_body(self):
4012 return "return GetConstructorObjectHandle(aCx);\n"
4015 class CGGetNamedPropertiesObjectMethod(CGAbstractStaticMethod):
4016 def __init__(self, descriptor):
4017 args = [Argument("JSContext*", "aCx")]
4018 CGAbstractStaticMethod.__init__(
4019 self, descriptor, "GetNamedPropertiesObject", "JSObject*", args
4022 def definition_body(self):
4023 parentProtoName = self.descriptor.parentPrototypeName
4024 if parentProtoName is None:
4025 getParentProto = ""
4026 parentProto = "nullptr"
4027 else:
4028 getParentProto = fill(
4030 JS::Rooted<JSObject*> parentProto(aCx, ${parent}::GetProtoObjectHandle(aCx));
4031 if (!parentProto) {
4032 return nullptr;
4034 """,
4035 parent=toBindingNamespace(parentProtoName),
4037 parentProto = "parentProto"
4038 return fill(
4040 /* Make sure our global is sane. Hopefully we can remove this sometime */
4041 JSObject* global = JS::CurrentGlobalOrNull(aCx);
4042 if (!(JS::GetClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
4043 return nullptr;
4046 /* Check to see whether the named properties object has already been created */
4047 ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
4049 JS::Heap<JSObject*>& namedPropertiesObject = protoAndIfaceCache.EntrySlotOrCreate(namedpropertiesobjects::id::${ifaceName});
4050 if (!namedPropertiesObject) {
4051 $*{getParentProto}
4052 namedPropertiesObject = ${nativeType}::CreateNamedPropertiesObject(aCx, ${parentProto});
4053 DebugOnly<const DOMIfaceAndProtoJSClass*> clasp =
4054 DOMIfaceAndProtoJSClass::FromJSClass(JS::GetClass(namedPropertiesObject));
4055 MOZ_ASSERT(clasp->mType == eNamedPropertiesObject,
4056 "Expected ${nativeType}::CreateNamedPropertiesObject to return a named properties object");
4057 MOZ_ASSERT(clasp->mNativeHooks,
4058 "The named properties object for ${nativeType} should have NativePropertyHooks.");
4059 MOZ_ASSERT(!clasp->mNativeHooks->mIndexedOrNamedNativeProperties ||
4060 !clasp->mNativeHooks->mIndexedOrNamedNativeProperties->mResolveOwnProperty,
4061 "Shouldn't resolve the properties of the named properties object for ${nativeType} for Xrays.");
4062 MOZ_ASSERT(!clasp->mNativeHooks->mIndexedOrNamedNativeProperties ||
4063 !clasp->mNativeHooks->mIndexedOrNamedNativeProperties->mEnumerateOwnProperties,
4064 "Shouldn't enumerate the properties of the named properties object for ${nativeType} for Xrays.");
4066 return namedPropertiesObject.get();
4067 """,
4068 getParentProto=getParentProto,
4069 ifaceName=self.descriptor.name,
4070 parentProto=parentProto,
4071 nativeType=self.descriptor.nativeType,
4075 def getRawConditionList(idlobj, cxName, objName, ignoreSecureContext=False):
4077 Get the list of conditions for idlobj (to be used in "is this enabled"
4078 checks). This will be returned as a CGList with " &&\n" as the separator,
4079 for readability.
4081 objName is the name of the object that we're working with, because some of
4082 our test functions want that.
4084 ignoreSecureContext is used only for constructors in which the WebIDL interface
4085 itself is already marked as [SecureContext]. There is no need to do the work twice.
4087 conditions = []
4088 pref = idlobj.getExtendedAttribute("Pref")
4089 if pref:
4090 assert isinstance(pref, list) and len(pref) == 1
4091 conditions.append("StaticPrefs::%s()" % prefIdentifier(pref[0]))
4092 if isChromeOnly(idlobj):
4093 conditions.append("nsContentUtils::ThreadsafeIsSystemCaller(%s)" % cxName)
4094 func = idlobj.getExtendedAttribute("Func")
4095 if func:
4096 assert isinstance(func, list) and len(func) == 1
4097 conditions.append("%s(%s, %s)" % (func[0], cxName, objName))
4098 trial = idlobj.getExtendedAttribute("Trial")
4099 if trial:
4100 assert isinstance(trial, list) and len(trial) == 1
4101 conditions.append(
4102 "OriginTrials::IsEnabled(%s, %s, OriginTrial::%s)"
4103 % (cxName, objName, trial[0])
4105 if not ignoreSecureContext and idlobj.getExtendedAttribute("SecureContext"):
4106 conditions.append(
4107 "mozilla::dom::IsSecureContextOrObjectIsFromSecureContext(%s, %s)"
4108 % (cxName, objName)
4110 return conditions
4113 def getConditionList(idlobj, cxName, objName, ignoreSecureContext=False):
4115 Get the list of conditions from getRawConditionList
4116 See comment on getRawConditionList above for more info about arguments.
4118 The return value is a possibly-empty conjunctive CGList of conditions.
4120 conditions = getRawConditionList(idlobj, cxName, objName, ignoreSecureContext)
4121 return CGList((CGGeneric(cond) for cond in conditions), " &&\n")
4124 class CGConstructorEnabled(CGAbstractMethod):
4126 A method for testing whether we should be exposing this interface object.
4127 This can perform various tests depending on what conditions are specified
4128 on the interface.
4131 def __init__(self, descriptor):
4132 CGAbstractMethod.__init__(
4133 self,
4134 descriptor,
4135 "ConstructorEnabled",
4136 "bool",
4137 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
4140 def definition_body(self):
4141 body = CGList([], "\n")
4143 iface = self.descriptor.interface
4145 if not iface.isExposedInWindow():
4146 exposedInWindowCheck = dedent(
4148 MOZ_ASSERT(!NS_IsMainThread(), "Why did we even get called?");
4151 body.append(CGGeneric(exposedInWindowCheck))
4153 if iface.isExposedInSomeButNotAllWorkers():
4154 workerGlobals = sorted(iface.getWorkerExposureSet())
4155 workerCondition = CGList(
4157 CGGeneric('strcmp(name, "%s")' % workerGlobal)
4158 for workerGlobal in workerGlobals
4160 " && ",
4162 exposedInWorkerCheck = fill(
4164 const char* name = JS::GetClass(aObj)->name;
4165 if (${workerCondition}) {
4166 return false;
4168 """,
4169 workerCondition=workerCondition.define(),
4171 exposedInWorkerCheck = CGGeneric(exposedInWorkerCheck)
4172 if iface.isExposedInWindow():
4173 exposedInWorkerCheck = CGIfWrapper(
4174 exposedInWorkerCheck, "!NS_IsMainThread()"
4176 body.append(exposedInWorkerCheck)
4178 conditions = getConditionList(iface, "aCx", "aObj")
4180 # We should really have some conditions
4181 assert len(body) or len(conditions)
4183 conditionsWrapper = ""
4184 if len(conditions):
4185 conditionsWrapper = CGWrapper(
4186 conditions, pre="return ", post=";\n", reindent=True
4188 else:
4189 conditionsWrapper = CGGeneric("return true;\n")
4191 body.append(conditionsWrapper)
4192 return body.define()
4195 def StructuredCloneTag(name):
4196 return "SCTAG_DOM_%s" % name.upper()
4199 class CGSerializer(CGAbstractStaticMethod):
4201 Implementation of serialization for things marked [Serializable].
4202 This gets stored in our DOMJSClass, so it can be static.
4204 The caller is expected to pass in the object whose DOMJSClass it
4205 used to get the serializer.
4208 def __init__(self, descriptor):
4209 args = [
4210 Argument("JSContext*", "aCx"),
4211 Argument("JSStructuredCloneWriter*", "aWriter"),
4212 Argument("JS::Handle<JSObject*>", "aObj"),
4214 CGAbstractStaticMethod.__init__(self, descriptor, "Serialize", "bool", args)
4216 def definition_body(self):
4217 return fill(
4219 MOZ_ASSERT(IsDOMObject(aObj), "Non-DOM object passed");
4220 MOZ_ASSERT(GetDOMClass(aObj)->mSerializer == &Serialize,
4221 "Wrong object passed");
4222 return JS_WriteUint32Pair(aWriter, ${tag}, 0) &&
4223 UnwrapDOMObject<${type}>(aObj)->WriteStructuredClone(aCx, aWriter);
4224 """,
4225 tag=StructuredCloneTag(self.descriptor.name),
4226 type=self.descriptor.nativeType,
4230 class CGDeserializer(CGAbstractMethod):
4232 Implementation of deserialization for things marked [Serializable].
4233 This will need to be accessed from WebIDLSerializable, so can't be static.
4236 def __init__(self, descriptor):
4237 args = [
4238 Argument("JSContext*", "aCx"),
4239 Argument("nsIGlobalObject*", "aGlobal"),
4240 Argument("JSStructuredCloneReader*", "aReader"),
4242 CGAbstractMethod.__init__(self, descriptor, "Deserialize", "JSObject*", args)
4244 def definition_body(self):
4245 # WrapObject has different signatures depending on whether
4246 # the object is wrappercached.
4247 if self.descriptor.wrapperCache:
4248 wrapCall = dedent(
4250 result = obj->WrapObject(aCx, nullptr);
4251 if (!result) {
4252 return nullptr;
4256 else:
4257 wrapCall = dedent(
4259 if (!obj->WrapObject(aCx, nullptr, &result)) {
4260 return nullptr;
4265 return fill(
4267 // Protect the result from a moving GC in ~RefPtr
4268 JS::Rooted<JSObject*> result(aCx);
4269 { // Scope for the RefPtr
4270 RefPtr<${type}> obj = ${type}::ReadStructuredClone(aCx, aGlobal, aReader);
4271 if (!obj) {
4272 return nullptr;
4274 $*{wrapCall}
4276 return result;
4277 """,
4278 type=self.descriptor.nativeType,
4279 wrapCall=wrapCall,
4283 def CreateBindingJSObject(descriptor):
4284 objDecl = "BindingJSObjectCreator<%s> creator(aCx);\n" % descriptor.nativeType
4286 # We don't always need to root obj, but there are a variety
4287 # of cases where we do, so for simplicity, just always root it.
4288 if descriptor.proxy:
4289 if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
4290 assert not descriptor.isMaybeCrossOriginObject()
4291 create = dedent(
4293 aObject->mExpandoAndGeneration.expando.setUndefined();
4294 JS::Rooted<JS::Value> expandoValue(aCx, JS::PrivateValue(&aObject->mExpandoAndGeneration));
4295 creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(),
4296 proto, /* aLazyProto = */ false, aObject,
4297 expandoValue, aReflector);
4300 else:
4301 if descriptor.isMaybeCrossOriginObject():
4302 proto = "nullptr"
4303 lazyProto = "true"
4304 else:
4305 proto = "proto"
4306 lazyProto = "false"
4307 create = fill(
4309 creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(),
4310 ${proto}, /* aLazyProto = */ ${lazyProto},
4311 aObject, JS::UndefinedHandleValue, aReflector);
4312 """,
4313 proto=proto,
4314 lazyProto=lazyProto,
4316 else:
4317 create = dedent(
4319 creator.CreateObject(aCx, sClass.ToJSClass(), proto, aObject, aReflector);
4322 return (
4323 objDecl
4324 + create
4325 + dedent(
4327 if (!aReflector) {
4328 return false;
4335 def InitUnforgeablePropertiesOnHolder(
4336 descriptor, properties, failureCode, holderName="unforgeableHolder"
4339 Define the unforgeable properties on the unforgeable holder for
4340 the interface represented by descriptor.
4342 properties is a PropertyArrays instance.
4345 assert (
4346 properties.unforgeableAttrs.hasNonChromeOnly()
4347 or properties.unforgeableAttrs.hasChromeOnly()
4348 or properties.unforgeableMethods.hasNonChromeOnly()
4349 or properties.unforgeableMethods.hasChromeOnly()
4352 unforgeables = []
4354 defineUnforgeableAttrs = fill(
4356 if (!DefineLegacyUnforgeableAttributes(aCx, ${holderName}, %s)) {
4357 $*{failureCode}
4359 """,
4360 failureCode=failureCode,
4361 holderName=holderName,
4363 defineUnforgeableMethods = fill(
4365 if (!DefineLegacyUnforgeableMethods(aCx, ${holderName}, %s)) {
4366 $*{failureCode}
4368 """,
4369 failureCode=failureCode,
4370 holderName=holderName,
4373 unforgeableMembers = [
4374 (defineUnforgeableAttrs, properties.unforgeableAttrs),
4375 (defineUnforgeableMethods, properties.unforgeableMethods),
4377 for template, array in unforgeableMembers:
4378 if array.hasNonChromeOnly():
4379 unforgeables.append(CGGeneric(template % array.variableName(False)))
4380 if array.hasChromeOnly():
4381 unforgeables.append(
4382 CGIfWrapper(
4383 CGGeneric(template % array.variableName(True)),
4384 "nsContentUtils::ThreadsafeIsSystemCaller(aCx)",
4388 if descriptor.interface.getExtendedAttribute("LegacyUnforgeable"):
4389 # We do our undefined toPrimitive here, not as a regular property
4390 # because we don't have a concept of value props anywhere in IDL.
4391 unforgeables.append(
4392 CGGeneric(
4393 fill(
4395 JS::Rooted<JS::PropertyKey> toPrimitive(aCx,
4396 JS::GetWellKnownSymbolKey(aCx, JS::SymbolCode::toPrimitive));
4397 if (!JS_DefinePropertyById(aCx, ${holderName}, toPrimitive,
4398 JS::UndefinedHandleValue,
4399 JSPROP_READONLY | JSPROP_PERMANENT)) {
4400 $*{failureCode}
4402 """,
4403 failureCode=failureCode,
4404 holderName=holderName,
4409 return CGWrapper(CGList(unforgeables), pre="\n")
4412 def CopyUnforgeablePropertiesToInstance(descriptor, failureCode):
4414 Copy the unforgeable properties from the unforgeable holder for
4415 this interface to the instance object we have.
4417 assert not descriptor.isGlobal()
4419 if not descriptor.hasLegacyUnforgeableMembers:
4420 return ""
4422 copyCode = [
4423 CGGeneric(
4424 dedent(
4426 // Important: do unforgeable property setup after we have handed
4427 // over ownership of the C++ object to obj as needed, so that if
4428 // we fail and it ends up GCed it won't have problems in the
4429 // finalizer trying to drop its ownership of the C++ object.
4435 # For proxies, we want to define on the expando object, not directly on the
4436 # reflector, so we can make sure we don't get confused by named getters.
4437 if descriptor.proxy:
4438 copyCode.append(
4439 CGGeneric(
4440 fill(
4442 JS::Rooted<JSObject*> expando(aCx,
4443 DOMProxyHandler::EnsureExpandoObject(aCx, aReflector));
4444 if (!expando) {
4445 $*{failureCode}
4447 """,
4448 failureCode=failureCode,
4452 obj = "expando"
4453 else:
4454 obj = "aReflector"
4456 copyCode.append(
4457 CGGeneric(
4458 fill(
4460 JS::Rooted<JSObject*> unforgeableHolder(aCx,
4461 &JS::GetReservedSlot(canonicalProto, DOM_INTERFACE_PROTO_SLOTS_BASE).toObject());
4462 if (!JS_InitializePropertiesFromCompatibleNativeObject(aCx, ${obj}, unforgeableHolder)) {
4463 $*{failureCode}
4465 """,
4466 obj=obj,
4467 failureCode=failureCode,
4472 return CGWrapper(CGList(copyCode), pre="\n").define()
4475 def AssertInheritanceChain(descriptor):
4476 # We can skip the reinterpret_cast check for the descriptor's nativeType
4477 # if aObject is a pointer of that type.
4478 asserts = fill(
4480 static_assert(std::is_same_v<decltype(aObject), ${nativeType}*>);
4481 """,
4482 nativeType=descriptor.nativeType,
4484 iface = descriptor.interface
4485 while iface.parent:
4486 iface = iface.parent
4487 desc = descriptor.getDescriptor(iface.identifier.name)
4488 asserts += (
4489 "MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
4490 " reinterpret_cast<%s*>(aObject),\n"
4491 ' "Multiple inheritance for %s is broken.");\n'
4492 % (desc.nativeType, desc.nativeType, desc.nativeType)
4494 asserts += "MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n"
4495 return asserts
4498 def InitMemberSlots(descriptor, failureCode):
4500 Initialize member slots on our JS object if we're supposed to have some.
4502 Note that this is called after the SetWrapper() call in the
4503 wrapperCache case, since that can affect how our getters behave
4504 and we plan to invoke them here. So if we fail, we need to
4505 ClearWrapper.
4507 if not descriptor.interface.hasMembersInSlots():
4508 return ""
4509 return fill(
4511 if (!UpdateMemberSlots(aCx, aReflector, aObject)) {
4512 $*{failureCode}
4514 """,
4515 failureCode=failureCode,
4519 def DeclareProto(descriptor, noGivenProto=False):
4521 Declare the canonicalProto and proto we have for our wrapping operation.
4523 getCanonical = dedent(
4525 JS::Handle<JSObject*> ${canonicalProto} = GetProtoObjectHandle(aCx);
4526 if (!${canonicalProto}) {
4527 return false;
4532 if noGivenProto:
4533 return fill(getCanonical, canonicalProto="proto")
4535 getCanonical = fill(getCanonical, canonicalProto="canonicalProto")
4537 preamble = getCanonical + dedent(
4539 JS::Rooted<JSObject*> proto(aCx);
4542 if descriptor.isMaybeCrossOriginObject():
4543 return preamble + dedent(
4545 MOZ_ASSERT(!aGivenProto,
4546 "Shouldn't have constructors on cross-origin objects");
4547 // Set proto to canonicalProto to avoid preserving our wrapper if
4548 // we don't have to.
4549 proto = canonicalProto;
4553 return preamble + dedent(
4555 if (aGivenProto) {
4556 proto = aGivenProto;
4557 // Unfortunately, while aGivenProto was in the compartment of aCx
4558 // coming in, we changed compartments to that of "parent" so may need
4559 // to wrap the proto here.
4560 if (js::GetContextCompartment(aCx) != JS::GetCompartment(proto)) {
4561 if (!JS_WrapObject(aCx, &proto)) {
4562 return false;
4565 } else {
4566 proto = canonicalProto;
4572 class CGWrapWithCacheMethod(CGAbstractMethod):
4574 Create a wrapper JSObject for a given native that implements nsWrapperCache.
4577 def __init__(self, descriptor):
4578 assert descriptor.interface.hasInterfacePrototypeObject()
4579 args = [
4580 Argument("JSContext*", "aCx"),
4581 Argument(descriptor.nativeType + "*", "aObject"),
4582 Argument("nsWrapperCache*", "aCache"),
4583 Argument("JS::Handle<JSObject*>", "aGivenProto"),
4584 Argument("JS::MutableHandle<JSObject*>", "aReflector"),
4586 CGAbstractMethod.__init__(self, descriptor, "Wrap", "bool", args)
4588 def definition_body(self):
4589 failureCode = dedent(
4591 aCache->ReleaseWrapper(aObject);
4592 aCache->ClearWrapper();
4593 return false;
4597 if self.descriptor.proxy:
4598 finalize = "DOMProxyHandler::getInstance()->finalize"
4599 else:
4600 finalize = FINALIZE_HOOK_NAME
4602 return fill(
4604 static_assert(!std::is_base_of_v<NonRefcountedDOMObject, ${nativeType}>,
4605 "Shouldn't have wrappercached things that are not refcounted.");
4606 $*{assertInheritance}
4607 MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx));
4608 MOZ_ASSERT(!aCache->GetWrapper(),
4609 "You should probably not be using Wrap() directly; use "
4610 "GetOrCreateDOMReflector instead");
4612 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
4613 "nsISupports must be on our primary inheritance chain");
4615 // If the wrapper cache contains a dead reflector then finalize that
4616 // now, ensuring that the finalizer for the old reflector always
4617 // runs before the new reflector is created and attached. This
4618 // avoids the awkward situation where there are multiple reflector
4619 // objects that contain pointers to the same native.
4621 if (JSObject* oldReflector = aCache->GetWrapperMaybeDead()) {
4622 ${finalize}(nullptr /* unused */, oldReflector);
4623 MOZ_ASSERT(!aCache->GetWrapperMaybeDead());
4626 JS::Rooted<JSObject*> global(aCx, FindAssociatedGlobal(aCx, aObject->GetParentObject()));
4627 if (!global) {
4628 return false;
4630 MOZ_ASSERT(JS_IsGlobalObject(global));
4631 JS::AssertObjectIsNotGray(global);
4633 // That might have ended up wrapping us already, due to the wonders
4634 // of XBL. Check for that, and bail out as needed.
4635 aReflector.set(aCache->GetWrapper());
4636 if (aReflector) {
4637 #ifdef DEBUG
4638 AssertReflectorHasGivenProto(aCx, aReflector, aGivenProto);
4639 #endif // DEBUG
4640 return true;
4643 JSAutoRealm ar(aCx, global);
4644 $*{declareProto}
4646 $*{createObject}
4648 aCache->SetWrapper(aReflector);
4649 $*{unforgeable}
4650 $*{slots}
4651 creator.InitializationSucceeded();
4653 MOZ_ASSERT(aCache->GetWrapperPreserveColor() &&
4654 aCache->GetWrapperPreserveColor() == aReflector);
4655 // If proto != canonicalProto, we have to preserve our wrapper;
4656 // otherwise we won't be able to properly recreate it later, since
4657 // we won't know what proto to use. Note that we don't check
4658 // aGivenProto here, since it's entirely possible (and even
4659 // somewhat common) to have a non-null aGivenProto which is the
4660 // same as canonicalProto.
4661 if (proto != canonicalProto) {
4662 PreserveWrapper(aObject);
4665 return true;
4666 """,
4667 nativeType=self.descriptor.nativeType,
4668 assertInheritance=AssertInheritanceChain(self.descriptor),
4669 declareProto=DeclareProto(self.descriptor),
4670 createObject=CreateBindingJSObject(self.descriptor),
4671 unforgeable=CopyUnforgeablePropertiesToInstance(
4672 self.descriptor, failureCode
4674 slots=InitMemberSlots(self.descriptor, failureCode),
4675 finalize=finalize,
4679 class CGWrapMethod(CGAbstractMethod):
4680 def __init__(self, descriptor):
4681 # XXX can we wrap if we don't have an interface prototype object?
4682 assert descriptor.interface.hasInterfacePrototypeObject()
4683 args = [
4684 Argument("JSContext*", "aCx"),
4685 Argument("T*", "aObject"),
4686 Argument("JS::Handle<JSObject*>", "aGivenProto"),
4688 CGAbstractMethod.__init__(
4689 self,
4690 descriptor,
4691 "Wrap",
4692 "JSObject*",
4693 args,
4694 inline=True,
4695 templateArgs=["class T"],
4698 def definition_body(self):
4699 return dedent(
4701 JS::Rooted<JSObject*> reflector(aCx);
4702 return Wrap(aCx, aObject, aObject, aGivenProto, &reflector) ? reflector.get() : nullptr;
4707 class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
4709 Create a wrapper JSObject for a given native that does not implement
4710 nsWrapperCache.
4713 def __init__(self, descriptor, static=False, signatureOnly=False):
4714 # XXX can we wrap if we don't have an interface prototype object?
4715 assert descriptor.interface.hasInterfacePrototypeObject()
4716 self.noGivenProto = (
4717 descriptor.interface.isIteratorInterface()
4718 or descriptor.interface.isAsyncIteratorInterface()
4720 args = [
4721 Argument("JSContext*", "aCx"),
4722 Argument(descriptor.nativeType + "*", "aObject"),
4724 if not self.noGivenProto:
4725 args.append(Argument("JS::Handle<JSObject*>", "aGivenProto"))
4726 args.append(Argument("JS::MutableHandle<JSObject*>", "aReflector"))
4727 CGAbstractMethod.__init__(
4728 self,
4729 descriptor,
4730 "Wrap",
4731 "bool",
4732 args,
4733 static=static,
4734 signatureOnly=signatureOnly,
4737 def definition_body(self):
4738 failureCode = "return false;\n"
4740 declareProto = DeclareProto(self.descriptor, noGivenProto=self.noGivenProto)
4741 if self.noGivenProto:
4742 assertGivenProto = ""
4743 else:
4744 assertGivenProto = dedent(
4746 MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx));
4749 return fill(
4751 $*{assertions}
4752 $*{assertGivenProto}
4754 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
4755 $*{declareProto}
4757 $*{createObject}
4759 $*{unforgeable}
4761 $*{slots}
4763 creator.InitializationSucceeded();
4764 return true;
4765 """,
4766 assertions=AssertInheritanceChain(self.descriptor),
4767 assertGivenProto=assertGivenProto,
4768 declareProto=declareProto,
4769 createObject=CreateBindingJSObject(self.descriptor),
4770 unforgeable=CopyUnforgeablePropertiesToInstance(
4771 self.descriptor, failureCode
4773 slots=InitMemberSlots(self.descriptor, failureCode),
4777 class CGWrapGlobalMethod(CGAbstractMethod):
4779 Create a wrapper JSObject for a global. The global must implement
4780 nsWrapperCache.
4782 properties should be a PropertyArrays instance.
4785 def __init__(self, descriptor, properties):
4786 assert descriptor.interface.hasInterfacePrototypeObject()
4787 args = [
4788 Argument("JSContext*", "aCx"),
4789 Argument(descriptor.nativeType + "*", "aObject"),
4790 Argument("nsWrapperCache*", "aCache"),
4791 Argument("JS::RealmOptions&", "aOptions"),
4792 Argument("JSPrincipals*", "aPrincipal"),
4793 Argument("JS::MutableHandle<JSObject*>", "aReflector"),
4795 CGAbstractMethod.__init__(self, descriptor, "Wrap", "bool", args)
4796 self.descriptor = descriptor
4797 self.properties = properties
4799 def definition_body(self):
4800 if self.properties.hasNonChromeOnly():
4801 properties = "sNativeProperties.Upcast()"
4802 else:
4803 properties = "nullptr"
4804 if self.properties.hasChromeOnly():
4805 chromeProperties = "nsContentUtils::ThreadsafeIsSystemCaller(aCx) ? sChromeOnlyNativeProperties.Upcast() : nullptr"
4806 else:
4807 chromeProperties = "nullptr"
4809 failureCode = dedent(
4811 aCache->ReleaseWrapper(aObject);
4812 aCache->ClearWrapper();
4813 return false;
4817 if self.descriptor.hasLegacyUnforgeableMembers:
4818 unforgeable = InitUnforgeablePropertiesOnHolder(
4819 self.descriptor, self.properties, failureCode, "aReflector"
4820 ).define()
4821 else:
4822 unforgeable = ""
4824 if self.descriptor.hasOrdinaryObjectPrototype:
4825 getProto = "JS::GetRealmObjectPrototypeHandle"
4826 else:
4827 getProto = "GetProtoObjectHandle"
4828 return fill(
4830 $*{assertions}
4831 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
4832 "nsISupports must be on our primary inheritance chain");
4834 if (!CreateGlobal<${nativeType}, ${getProto}>(aCx,
4835 aObject,
4836 aCache,
4837 sClass.ToJSClass(),
4838 aOptions,
4839 aPrincipal,
4840 aReflector)) {
4841 $*{failureCode}
4844 // aReflector is a new global, so has a new realm. Enter it
4845 // before doing anything with it.
4846 JSAutoRealm ar(aCx, aReflector);
4848 if (!DefineProperties(aCx, aReflector, ${properties}, ${chromeProperties})) {
4849 $*{failureCode}
4851 $*{unforgeable}
4853 $*{slots}
4855 return true;
4856 """,
4857 assertions=AssertInheritanceChain(self.descriptor),
4858 nativeType=self.descriptor.nativeType,
4859 getProto=getProto,
4860 properties=properties,
4861 chromeProperties=chromeProperties,
4862 failureCode=failureCode,
4863 unforgeable=unforgeable,
4864 slots=InitMemberSlots(self.descriptor, failureCode),
4868 class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod):
4869 def __init__(self, descriptor):
4870 args = [
4871 Argument("JSContext*", "aCx"),
4872 Argument("JS::Handle<JSObject*>", "aWrapper"),
4873 Argument(descriptor.nativeType + "*", "aObject"),
4875 CGAbstractStaticMethod.__init__(
4876 self, descriptor, "UpdateMemberSlots", "bool", args
4879 def definition_body(self):
4880 body = "JS::Rooted<JS::Value> temp(aCx);\n" "JSJitGetterCallArgs args(&temp);\n"
4881 for m in self.descriptor.interface.members:
4882 if m.isAttr() and m.getExtendedAttribute("StoreInSlot"):
4883 # Skip doing this for the "window" and "self" attributes on the
4884 # Window interface, because those can't be gotten safely until
4885 # we have hooked it up correctly to the outer window. The
4886 # window code handles doing the get itself.
4887 if self.descriptor.interface.identifier.name == "Window" and (
4888 m.identifier.name == "window" or m.identifier.name == "self"
4890 continue
4891 body += fill(
4894 static_assert(${slot} < JS::shadow::Object::MAX_FIXED_SLOTS,
4895 "Not enough fixed slots to fit '${interface}.${member}. Ion's visitGetDOMMemberV/visitGetDOMMemberT assume StoreInSlot things are all in fixed slots.");
4896 if (!get_${member}(aCx, aWrapper, aObject, args)) {
4897 return false;
4899 // Getter handled setting our reserved slots
4900 """,
4901 slot=memberReservedSlot(m, self.descriptor),
4902 interface=self.descriptor.interface.identifier.name,
4903 member=m.identifier.name,
4906 body += "\nreturn true;\n"
4907 return body
4910 class CGClearCachedValueMethod(CGAbstractMethod):
4911 def __init__(self, descriptor, member):
4912 self.member = member
4913 # If we're StoreInSlot, we'll need to call the getter
4914 if member.getExtendedAttribute("StoreInSlot"):
4915 args = [Argument("JSContext*", "aCx")]
4916 returnType = "bool"
4917 else:
4918 args = []
4919 returnType = "void"
4920 args.append(Argument(descriptor.nativeType + "*", "aObject"))
4921 name = MakeClearCachedValueNativeName(member)
4922 CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
4924 def definition_body(self):
4925 slotIndex = memberReservedSlot(self.member, self.descriptor)
4926 if self.member.getExtendedAttribute("StoreInSlot"):
4927 # We have to root things and save the old value in case
4928 # regetting fails, so we can restore it.
4929 declObj = "JS::Rooted<JSObject*> obj(aCx);\n"
4930 noopRetval = " true"
4931 saveMember = (
4932 "JS::Rooted<JS::Value> oldValue(aCx, JS::GetReservedSlot(obj, %s));\n"
4933 % slotIndex
4935 regetMember = fill(
4937 JS::Rooted<JS::Value> temp(aCx);
4938 JSJitGetterCallArgs args(&temp);
4939 JSAutoRealm ar(aCx, obj);
4940 if (!get_${name}(aCx, obj, aObject, args)) {
4941 JS::SetReservedSlot(obj, ${slotIndex}, oldValue);
4942 return false;
4944 return true;
4945 """,
4946 name=self.member.identifier.name,
4947 slotIndex=slotIndex,
4949 else:
4950 declObj = "JSObject* obj;\n"
4951 noopRetval = ""
4952 saveMember = ""
4953 regetMember = ""
4955 if self.descriptor.wantsXrays:
4956 clearXrayExpandoSlots = fill(
4958 xpc::ClearXrayExpandoSlots(obj, ${xraySlotIndex});
4959 """,
4960 xraySlotIndex=memberXrayExpandoReservedSlot(
4961 self.member, self.descriptor
4964 else:
4965 clearXrayExpandoSlots = ""
4967 return fill(
4969 $*{declObj}
4970 obj = aObject->GetWrapper();
4971 if (!obj) {
4972 return${noopRetval};
4974 $*{saveMember}
4975 JS::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue());
4976 $*{clearXrayExpandoSlots}
4977 $*{regetMember}
4978 """,
4979 declObj=declObj,
4980 noopRetval=noopRetval,
4981 saveMember=saveMember,
4982 slotIndex=slotIndex,
4983 clearXrayExpandoSlots=clearXrayExpandoSlots,
4984 regetMember=regetMember,
4988 class CGCrossOriginProperties(CGThing):
4989 def __init__(self, descriptor):
4990 attrs = []
4991 chromeOnlyAttrs = []
4992 methods = []
4993 chromeOnlyMethods = []
4994 for m in descriptor.interface.members:
4995 if m.isAttr() and (
4996 m.getExtendedAttribute("CrossOriginReadable")
4997 or m.getExtendedAttribute("CrossOriginWritable")
4999 if m.isStatic():
5000 raise TypeError(
5001 "Don't know how to deal with static method %s"
5002 % m.identifier.name
5004 if PropertyDefiner.getControllingCondition(
5005 m, descriptor
5006 ).hasDisablers():
5007 raise TypeError(
5008 "Don't know how to deal with disabler for %s"
5009 % m.identifier.name
5011 if len(m.bindingAliases) > 0:
5012 raise TypeError(
5013 "Don't know how to deal with aliases for %s" % m.identifier.name
5015 if m.getExtendedAttribute("ChromeOnly") is not None:
5016 chromeOnlyAttrs.extend(AttrDefiner.attrData(m, overrideFlags="0"))
5017 else:
5018 attrs.extend(AttrDefiner.attrData(m, overrideFlags="0"))
5019 elif m.isMethod() and m.getExtendedAttribute("CrossOriginCallable"):
5020 if m.isStatic():
5021 raise TypeError(
5022 "Don't know how to deal with static method %s"
5023 % m.identifier.name
5025 if PropertyDefiner.getControllingCondition(
5026 m, descriptor
5027 ).hasDisablers():
5028 raise TypeError(
5029 "Don't know how to deal with disabler for %s"
5030 % m.identifier.name
5032 if len(m.aliases) > 0:
5033 raise TypeError(
5034 "Don't know how to deal with aliases for %s" % m.identifier.name
5036 if m.getExtendedAttribute("ChromeOnly") is not None:
5037 chromeOnlyMethods.append(
5038 MethodDefiner.methodData(
5039 m, descriptor, overrideFlags="JSPROP_READONLY"
5042 else:
5043 methods.append(
5044 MethodDefiner.methodData(
5045 m, descriptor, overrideFlags="JSPROP_READONLY"
5049 if len(attrs) > 0:
5050 self.attributeSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
5051 attrs,
5052 descriptor,
5053 AttrDefiner.formatSpec,
5054 " JS_PS_END\n",
5055 AttrDefiner.condition,
5056 functools.partial(AttrDefiner.specData, crossOriginOnly=True),
5058 else:
5059 self.attributeSpecs = [" JS_PS_END\n"]
5060 if len(methods) > 0:
5061 self.methodSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
5062 methods,
5063 descriptor,
5064 MethodDefiner.formatSpec,
5065 " JS_FS_END\n",
5066 MethodDefiner.condition,
5067 MethodDefiner.specData,
5069 else:
5070 self.methodSpecs = [" JS_FS_END\n"]
5072 if len(chromeOnlyAttrs) > 0:
5074 self.chromeOnlyAttributeSpecs,
5076 ) = PropertyDefiner.generatePrefableArrayValues(
5077 chromeOnlyAttrs,
5078 descriptor,
5079 AttrDefiner.formatSpec,
5080 " JS_PS_END\n",
5081 AttrDefiner.condition,
5082 functools.partial(AttrDefiner.specData, crossOriginOnly=True),
5084 else:
5085 self.chromeOnlyAttributeSpecs = []
5086 if len(chromeOnlyMethods) > 0:
5087 self.chromeOnlyMethodSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
5088 chromeOnlyMethods,
5089 descriptor,
5090 MethodDefiner.formatSpec,
5091 " JS_FS_END\n",
5092 MethodDefiner.condition,
5093 MethodDefiner.specData,
5095 else:
5096 self.chromeOnlyMethodSpecs = []
5098 def declare(self):
5099 return dedent(
5101 extern const CrossOriginProperties sCrossOriginProperties;
5105 def define(self):
5106 def defineChromeOnly(name, specs, specType):
5107 if len(specs) == 0:
5108 return ("", "nullptr")
5109 name = "sChromeOnlyCrossOrigin" + name
5110 define = fill(
5112 static const ${specType} ${name}[] = {
5113 $*{specs}
5115 """,
5116 specType=specType,
5117 name=name,
5118 specs=",\n".join(specs),
5120 return (define, name)
5122 chromeOnlyAttributes = defineChromeOnly(
5123 "Attributes", self.chromeOnlyAttributeSpecs, "JSPropertySpec"
5125 chromeOnlyMethods = defineChromeOnly(
5126 "Methods", self.chromeOnlyMethodSpecs, "JSFunctionSpec"
5128 return fill(
5130 static const JSPropertySpec sCrossOriginAttributes[] = {
5131 $*{attributeSpecs}
5133 static const JSFunctionSpec sCrossOriginMethods[] = {
5134 $*{methodSpecs}
5136 $*{chromeOnlyAttributeSpecs}
5137 $*{chromeOnlyMethodSpecs}
5138 const CrossOriginProperties sCrossOriginProperties = {
5139 sCrossOriginAttributes,
5140 sCrossOriginMethods,
5141 ${chromeOnlyAttributes},
5142 ${chromeOnlyMethods}
5144 """,
5145 attributeSpecs=",\n".join(self.attributeSpecs),
5146 methodSpecs=",\n".join(self.methodSpecs),
5147 chromeOnlyAttributeSpecs=chromeOnlyAttributes[0],
5148 chromeOnlyMethodSpecs=chromeOnlyMethods[0],
5149 chromeOnlyAttributes=chromeOnlyAttributes[1],
5150 chromeOnlyMethods=chromeOnlyMethods[1],
5154 class CGCycleCollectionTraverseForOwningUnionMethod(CGAbstractMethod):
5156 ImplCycleCollectionUnlink for owning union type.
5159 def __init__(self, type):
5160 self.type = type
5161 args = [
5162 Argument("nsCycleCollectionTraversalCallback&", "aCallback"),
5163 Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion"),
5164 Argument("const char*", "aName"),
5165 Argument("uint32_t", "aFlags", "0"),
5167 CGAbstractMethod.__init__(
5168 self, None, "ImplCycleCollectionTraverse", "void", args
5171 def deps(self):
5172 return self.type.getDeps()
5174 def definition_body(self):
5175 memberNames = [
5176 getUnionMemberName(t)
5177 for t in self.type.flatMemberTypes
5178 if idlTypeNeedsCycleCollection(t)
5180 assert memberNames
5182 conditionTemplate = "aUnion.Is%s()"
5183 functionCallTemplate = (
5184 'ImplCycleCollectionTraverse(aCallback, aUnion.GetAs%s(), "m%s", aFlags);\n'
5187 ifStaments = (
5188 CGIfWrapper(CGGeneric(functionCallTemplate % (m, m)), conditionTemplate % m)
5189 for m in memberNames
5192 return CGElseChain(ifStaments).define()
5195 class CGCycleCollectionUnlinkForOwningUnionMethod(CGAbstractMethod):
5197 ImplCycleCollectionUnlink for owning union type.
5200 def __init__(self, type):
5201 self.type = type
5202 args = [Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion")]
5203 CGAbstractMethod.__init__(self, None, "ImplCycleCollectionUnlink", "void", args)
5205 def deps(self):
5206 return self.type.getDeps()
5208 def definition_body(self):
5209 return "aUnion.Uninit();\n"
5212 builtinNames = {
5213 IDLType.Tags.bool: "bool",
5214 IDLType.Tags.int8: "int8_t",
5215 IDLType.Tags.int16: "int16_t",
5216 IDLType.Tags.int32: "int32_t",
5217 IDLType.Tags.int64: "int64_t",
5218 IDLType.Tags.uint8: "uint8_t",
5219 IDLType.Tags.uint16: "uint16_t",
5220 IDLType.Tags.uint32: "uint32_t",
5221 IDLType.Tags.uint64: "uint64_t",
5222 IDLType.Tags.unrestricted_float: "float",
5223 IDLType.Tags.float: "float",
5224 IDLType.Tags.unrestricted_double: "double",
5225 IDLType.Tags.double: "double",
5228 numericSuffixes = {
5229 IDLType.Tags.int8: "",
5230 IDLType.Tags.uint8: "",
5231 IDLType.Tags.int16: "",
5232 IDLType.Tags.uint16: "",
5233 IDLType.Tags.int32: "",
5234 IDLType.Tags.uint32: "U",
5235 IDLType.Tags.int64: "LL",
5236 IDLType.Tags.uint64: "ULL",
5237 IDLType.Tags.unrestricted_float: "F",
5238 IDLType.Tags.float: "F",
5239 IDLType.Tags.unrestricted_double: "",
5240 IDLType.Tags.double: "",
5244 def numericValue(t, v):
5245 if t == IDLType.Tags.unrestricted_double or t == IDLType.Tags.unrestricted_float:
5246 typeName = builtinNames[t]
5247 if v == float("inf"):
5248 return "mozilla::PositiveInfinity<%s>()" % typeName
5249 if v == float("-inf"):
5250 return "mozilla::NegativeInfinity<%s>()" % typeName
5251 if math.isnan(v):
5252 return "mozilla::UnspecifiedNaN<%s>()" % typeName
5253 return "%s%s" % (v, numericSuffixes[t])
5256 class CastableObjectUnwrapper:
5258 A class for unwrapping an object stored in a JS Value (or
5259 MutableHandle<Value> or Handle<Value>) named by the "source" and
5260 "mutableSource" arguments based on the passed-in descriptor and storing it
5261 in a variable called by the name in the "target" argument. The "source"
5262 argument should be able to produce a Value or Handle<Value>; the
5263 "mutableSource" argument should be able to produce a MutableHandle<Value>
5265 codeOnFailure is the code to run if unwrapping fails.
5267 If isCallbackReturnValue is "JSImpl" and our descriptor is also
5268 JS-implemented, fall back to just creating the right object if what we
5269 have isn't one already.
5272 def __init__(
5273 self,
5274 descriptor,
5275 source,
5276 mutableSource,
5277 target,
5278 codeOnFailure,
5279 exceptionCode=None,
5280 isCallbackReturnValue=False,
5282 self.substitution = {
5283 "type": descriptor.nativeType,
5284 "protoID": "prototypes::id::" + descriptor.name,
5285 "target": target,
5286 "codeOnFailure": codeOnFailure,
5287 "source": source,
5288 "mutableSource": mutableSource,
5291 if isCallbackReturnValue == "JSImpl" and descriptor.interface.isJSImplemented():
5292 exceptionCode = exceptionCode or codeOnFailure
5293 self.substitution["codeOnFailure"] = fill(
5295 // Be careful to not wrap random DOM objects here, even if
5296 // they're wrapped in opaque security wrappers for some reason.
5297 // XXXbz Wish we could check for a JS-implemented object
5298 // that already has a content reflection...
5299 if (!IsDOMObject(js::UncheckedUnwrap(&${source}.toObject()))) {
5300 nsCOMPtr<nsIGlobalObject> contentGlobal;
5301 JS::Rooted<JSObject*> callback(cx, CallbackOrNull());
5302 if (!callback ||
5303 !GetContentGlobalForJSImplementedObject(cx, callback, getter_AddRefs(contentGlobal))) {
5304 $*{exceptionCode}
5306 JS::Rooted<JSObject*> jsImplSourceObj(cx, &${source}.toObject());
5307 MOZ_RELEASE_ASSERT(!js::IsWrapper(jsImplSourceObj),
5308 "Don't return JS implementations from other compartments");
5309 JS::Rooted<JSObject*> jsImplSourceGlobal(cx, JS::GetNonCCWObjectGlobal(jsImplSourceObj));
5310 ${target} = new ${type}(jsImplSourceObj, jsImplSourceGlobal, contentGlobal);
5311 } else {
5312 $*{codeOnFailure}
5314 """,
5315 exceptionCode=exceptionCode,
5316 **self.substitution,
5318 else:
5319 self.substitution["codeOnFailure"] = codeOnFailure
5321 def __str__(self):
5322 substitution = self.substitution.copy()
5323 substitution["codeOnFailure"] %= {
5324 "securityError": "rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO"
5326 return fill(
5329 // Our JSContext should be in the right global to do unwrapping in.
5330 nsresult rv = UnwrapObject<${protoID}, ${type}>(${mutableSource}, ${target}, cx);
5331 if (NS_FAILED(rv)) {
5332 $*{codeOnFailure}
5335 """,
5336 **substitution,
5340 class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
5342 As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
5345 def __init__(
5346 self,
5347 descriptor,
5348 source,
5349 mutableSource,
5350 target,
5351 exceptionCode,
5352 isCallbackReturnValue,
5353 sourceDescription,
5355 CastableObjectUnwrapper.__init__(
5356 self,
5357 descriptor,
5358 source,
5359 mutableSource,
5360 target,
5361 'cx.ThrowErrorMessage<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("%s", "%s");\n'
5362 "%s"
5363 % (sourceDescription, descriptor.interface.identifier.name, exceptionCode),
5364 exceptionCode,
5365 isCallbackReturnValue,
5369 def getCallbackConversionInfo(
5370 type, idlObject, isMember, isCallbackReturnValue, isOptional
5373 Returns a tuple containing the declType, declArgs, and basic
5374 conversion for the given callback type, with the given callback
5375 idl object in the given context (isMember/isCallbackReturnValue/isOptional).
5377 name = idlObject.identifier.name
5379 # We can't use fast callbacks if isOptional because then we get an
5380 # Optional<RootedCallback> thing, which is not transparent to consumers.
5381 useFastCallback = (
5382 (not isMember or isMember == "Union")
5383 and not isCallbackReturnValue
5384 and not isOptional
5386 if useFastCallback:
5387 name = "binding_detail::Fast%s" % name
5388 rootArgs = ""
5389 args = "&${val}.toObject(), JS::CurrentGlobalOrNull(cx)"
5390 else:
5391 rootArgs = dedent(
5393 JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject());
5394 JS::Rooted<JSObject*> tempGlobalRoot(cx, JS::CurrentGlobalOrNull(cx));
5397 args = "cx, tempRoot, tempGlobalRoot, GetIncumbentGlobal()"
5399 if type.nullable() or isCallbackReturnValue:
5400 declType = CGGeneric("RefPtr<%s>" % name)
5401 else:
5402 declType = CGGeneric("OwningNonNull<%s>" % name)
5404 if useFastCallback:
5405 declType = CGTemplatedType("RootedCallback", declType)
5406 declArgs = "cx"
5407 else:
5408 declArgs = None
5410 conversion = fill(
5412 { // scope for tempRoot and tempGlobalRoot if needed
5413 $*{rootArgs}
5414 $${declName} = new ${name}(${args});
5416 """,
5417 rootArgs=rootArgs,
5418 name=name,
5419 args=args,
5421 return (declType, declArgs, conversion)
5424 class JSToNativeConversionInfo:
5426 An object representing information about a JS-to-native conversion.
5429 def __init__(
5430 self,
5431 template,
5432 declType=None,
5433 holderType=None,
5434 dealWithOptional=False,
5435 declArgs=None,
5436 holderArgs=None,
5439 template: A string representing the conversion code. This will have
5440 template substitution performed on it as follows:
5442 ${val} is a handle to the JS::Value in question
5443 ${maybeMutableVal} May be a mutable handle to the JS::Value in
5444 question. This is only OK to use if ${val} is
5445 known to not be undefined.
5446 ${holderName} replaced by the holder's name, if any
5447 ${declName} replaced by the declaration's name
5448 ${haveValue} replaced by an expression that evaluates to a boolean
5449 for whether we have a JS::Value. Only used when
5450 defaultValue is not None or when True is passed for
5451 checkForValue to instantiateJSToNativeConversion.
5452 This expression may not be already-parenthesized, so if
5453 you use it with && or || make sure to put parens
5454 around it.
5455 ${passedToJSImpl} replaced by an expression that evaluates to a boolean
5456 for whether this value is being passed to a JS-
5457 implemented interface.
5459 declType: A CGThing representing the native C++ type we're converting
5460 to. This is allowed to be None if the conversion code is
5461 supposed to be used as-is.
5463 holderType: A CGThing representing the type of a "holder" which will
5464 hold a possible reference to the C++ thing whose type we
5465 returned in declType, or None if no such holder is needed.
5467 dealWithOptional: A boolean indicating whether the caller has to do
5468 optional-argument handling. This should only be set
5469 to true if the JS-to-native conversion is being done
5470 for an optional argument or dictionary member with no
5471 default value and if the returned template expects
5472 both declType and holderType to be wrapped in
5473 Optional<>, with ${declName} and ${holderName}
5474 adjusted to point to the Value() of the Optional, and
5475 Construct() calls to be made on the Optional<>s as
5476 needed.
5478 declArgs: If not None, the arguments to pass to the ${declName}
5479 constructor. These will have template substitution performed
5480 on them so you can use things like ${val}. This is a
5481 single string, not a list of strings.
5483 holderArgs: If not None, the arguments to pass to the ${holderName}
5484 constructor. These will have template substitution
5485 performed on them so you can use things like ${val}.
5486 This is a single string, not a list of strings.
5488 ${declName} must be in scope before the code from 'template' is entered.
5490 If holderType is not None then ${holderName} must be in scope before
5491 the code from 'template' is entered.
5493 assert isinstance(template, str)
5494 assert declType is None or isinstance(declType, CGThing)
5495 assert holderType is None or isinstance(holderType, CGThing)
5496 self.template = template
5497 self.declType = declType
5498 self.holderType = holderType
5499 self.dealWithOptional = dealWithOptional
5500 self.declArgs = declArgs
5501 self.holderArgs = holderArgs
5504 def getHandleDefault(defaultValue):
5505 tag = defaultValue.type.tag()
5506 if tag in numericSuffixes:
5507 # Some numeric literals require a suffix to compile without warnings
5508 return numericValue(tag, defaultValue.value)
5509 assert tag == IDLType.Tags.bool
5510 return toStringBool(defaultValue.value)
5513 def handleDefaultStringValue(defaultValue, method):
5515 Returns a string which ends up calling 'method' with a (char_t*, length)
5516 pair that sets this string default value. This string is suitable for
5517 passing as the second argument of handleDefault.
5519 assert (
5520 defaultValue.type.isDOMString()
5521 or defaultValue.type.isUSVString()
5522 or defaultValue.type.isUTF8String()
5523 or defaultValue.type.isByteString()
5525 # There shouldn't be any non-ASCII or embedded nulls in here; if
5526 # it ever sneaks in we will need to think about how to properly
5527 # represent that in the C++.
5528 assert all(ord(c) < 128 and ord(c) > 0 for c in defaultValue.value)
5529 if defaultValue.type.isByteString() or defaultValue.type.isUTF8String():
5530 prefix = ""
5531 else:
5532 prefix = "u"
5533 return fill(
5535 ${method}(${prefix}"${value}");
5536 """,
5537 method=method,
5538 prefix=prefix,
5539 value=defaultValue.value,
5543 def recordKeyType(recordType):
5544 assert recordType.keyType.isString()
5545 if recordType.keyType.isByteString() or recordType.keyType.isUTF8String():
5546 return "nsCString"
5547 return "nsString"
5550 def recordKeyDeclType(recordType):
5551 return CGGeneric(recordKeyType(recordType))
5554 def initializerForType(type):
5556 Get the right initializer for the given type for a data location where we
5557 plan to then initialize it from a JS::Value. Some types need to always be
5558 initialized even before we start the JS::Value-to-IDL-value conversion.
5560 Returns a string or None if no initialization is needed.
5562 if type.isObject():
5563 return "nullptr"
5564 # We could probably return CGDictionary.getNonInitializingCtorArg() for the
5565 # dictionary case, but code outside DictionaryBase subclasses can't use
5566 # that, so we can't do it across the board.
5567 return None
5570 # If this function is modified, modify CGNativeMember.getArg and
5571 # CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype
5572 # and holdertype we end up using, because it needs to be able to return the code
5573 # that will convert those to the actual return value of the callback function.
5574 def getJSToNativeConversionInfo(
5575 type,
5576 descriptorProvider,
5577 failureCode=None,
5578 isDefinitelyObject=False,
5579 isMember=False,
5580 isOptional=False,
5581 invalidEnumValueFatal=True,
5582 defaultValue=None,
5583 isNullOrUndefined=False,
5584 isKnownMissing=False,
5585 exceptionCode=None,
5586 lenientFloatCode=None,
5587 allowTreatNonCallableAsNull=False,
5588 isCallbackReturnValue=False,
5589 sourceDescription="value",
5590 nestingLevel="",
5593 Get a template for converting a JS value to a native object based on the
5594 given type and descriptor. If failureCode is given, then we're actually
5595 testing whether we can convert the argument to the desired type. That
5596 means that failures to convert due to the JS value being the wrong type of
5597 value need to use failureCode instead of throwing exceptions. Failures to
5598 convert that are due to JS exceptions (from toString or valueOf methods) or
5599 out of memory conditions need to throw exceptions no matter what
5600 failureCode is. However what actually happens when throwing an exception
5601 can be controlled by exceptionCode. The only requirement on that is that
5602 exceptionCode must end up doing a return, and every return from this
5603 function must happen via exceptionCode if exceptionCode is not None.
5605 If isDefinitelyObject is True, that means we have a value and the value
5606 tests true for isObject(), so we have no need to recheck that.
5608 If isNullOrUndefined is True, that means we have a value and the value
5609 tests true for isNullOrUndefined(), so we have no need to recheck that.
5611 If isKnownMissing is True, that means that we are known-missing, and for
5612 cases when we have a default value we only need to output the default value.
5614 if isMember is not False, we're being converted from a property of some JS
5615 object, not from an actual method argument, so we can't rely on our jsval
5616 being rooted or outliving us in any way. Callers can pass "Dictionary",
5617 "Variadic", "Sequence", "Union", or "OwningUnion" to indicate that the conversion
5618 is for something that is a dictionary member, a variadic argument, a sequence,
5619 an union, or an owning union respectively.
5620 XXX Once we swtich *Rooter to Rooted* for Record and Sequence type entirely,
5621 we could remove "Union" from isMember.
5623 If isOptional is true, then we are doing conversion of an optional
5624 argument with no default value.
5626 invalidEnumValueFatal controls whether an invalid enum value conversion
5627 attempt will throw (if true) or simply return without doing anything (if
5628 false).
5630 If defaultValue is not None, it's the IDL default value for this conversion
5632 If isEnforceRange is true, we're converting an integer and throwing if the
5633 value is out of range.
5635 If isClamp is true, we're converting an integer and clamping if the
5636 value is out of range.
5638 If isAllowShared is false, we're converting a buffer source and throwing if
5639 it is a SharedArrayBuffer or backed by a SharedArrayBuffer.
5641 If lenientFloatCode is not None, it should be used in cases when
5642 we're a non-finite float that's not unrestricted.
5644 If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] and
5645 [LegacyTreatNonObjectAsNull] extended attributes on nullable callback functions
5646 will be honored.
5648 If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be
5649 adjusted to make it easier to return from a callback. Since that type is
5650 never directly observable by any consumers of the callback code, this is OK.
5651 Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior
5652 of the FailureFatalCastableObjectUnwrapper conversion; this is used for
5653 implementing auto-wrapping of JS-implemented return values from a
5654 JS-implemented interface.
5656 sourceDescription is a description of what this JS value represents, to be
5657 used in error reporting. Callers should assume that it might get placed in
5658 the middle of a sentence. If it ends up at the beginning of a sentence, its
5659 first character will be automatically uppercased.
5661 The return value from this function is a JSToNativeConversionInfo.
5663 # If we have a defaultValue then we're not actually optional for
5664 # purposes of what we need to be declared as.
5665 assert defaultValue is None or not isOptional
5667 # Also, we should not have a defaultValue if we know we're an object
5668 assert not isDefinitelyObject or defaultValue is None
5670 # And we can't both be an object and be null or undefined
5671 assert not isDefinitelyObject or not isNullOrUndefined
5673 isClamp = type.hasClamp()
5674 isEnforceRange = type.hasEnforceRange()
5675 isAllowShared = type.hasAllowShared()
5677 # If exceptionCode is not set, we'll just rethrow the exception we got.
5678 # Note that we can't just set failureCode to exceptionCode, because setting
5679 # failureCode will prevent pending exceptions from being set in cases when
5680 # they really should be!
5681 if exceptionCode is None:
5682 exceptionCode = "return false;\n"
5684 # Unfortunately, .capitalize() on a string will lowercase things inside the
5685 # string, which we do not want.
5686 def firstCap(string):
5687 return string[0].upper() + string[1:]
5689 # Helper functions for dealing with failures due to the JS value being the
5690 # wrong type of value
5691 def onFailureNotAnObject(failureCode):
5692 return CGGeneric(
5693 failureCode
5694 or (
5695 'cx.ThrowErrorMessage<MSG_NOT_OBJECT>("%s");\n'
5696 "%s" % (firstCap(sourceDescription), exceptionCode)
5700 def onFailureBadType(failureCode, typeName):
5701 return CGGeneric(
5702 failureCode
5703 or (
5704 'cx.ThrowErrorMessage<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("%s", "%s");\n'
5705 "%s" % (firstCap(sourceDescription), typeName, exceptionCode)
5709 # It's a failure in the committed-to conversion, not a failure to match up
5710 # to a type, so we don't want to use failureCode in here. We want to just
5711 # throw an exception unconditionally.
5712 def onFailureIsShared():
5713 return CGGeneric(
5714 'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_SHARED>("%s");\n'
5715 "%s" % (firstCap(sourceDescription), exceptionCode)
5718 def onFailureIsLarge():
5719 return CGGeneric(
5720 'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_LARGE>("%s");\n'
5721 "%s" % (firstCap(sourceDescription), exceptionCode)
5724 def onFailureIsResizable():
5725 return CGGeneric(
5726 'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_RESIZABLE>("%s");\n'
5727 "%s" % (firstCap(sourceDescription), exceptionCode)
5730 def onFailureNotCallable(failureCode):
5731 return CGGeneric(
5732 failureCode
5733 or (
5734 'cx.ThrowErrorMessage<MSG_NOT_CALLABLE>("%s");\n'
5735 "%s" % (firstCap(sourceDescription), exceptionCode)
5739 # A helper function for handling default values. Takes a template
5740 # body and the C++ code to set the default value and wraps the
5741 # given template body in handling for the default value.
5742 def handleDefault(template, setDefault):
5743 if defaultValue is None:
5744 return template
5745 if isKnownMissing:
5746 return fill(
5749 // scope for any temporaries our default value setting needs.
5750 $*{setDefault}
5752 """,
5753 setDefault=setDefault,
5755 return fill(
5757 if ($${haveValue}) {
5758 $*{templateBody}
5759 } else {
5760 $*{setDefault}
5762 """,
5763 templateBody=template,
5764 setDefault=setDefault,
5767 # A helper function for wrapping up the template body for
5768 # possibly-nullable objecty stuff
5769 def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None):
5770 if isNullOrUndefined and type.nullable():
5771 # Just ignore templateBody and set ourselves to null.
5772 # Note that we don't have to worry about default values
5773 # here either, since we already examined this value.
5774 return codeToSetNull
5776 if not isDefinitelyObject:
5777 # Handle the non-object cases by wrapping up the whole
5778 # thing in an if cascade.
5779 if type.nullable():
5780 elifLine = "} else if (${val}.isNullOrUndefined()) {\n"
5781 elifBody = codeToSetNull
5782 else:
5783 elifLine = ""
5784 elifBody = ""
5786 # Note that $${val} below expands to ${val}. This string is
5787 # used as a template later, and val will be filled in then.
5788 templateBody = fill(
5790 if ($${val}.isObject()) {
5791 $*{templateBody}
5792 $*{elifLine}
5793 $*{elifBody}
5794 } else {
5795 $*{failureBody}
5797 """,
5798 templateBody=templateBody,
5799 elifLine=elifLine,
5800 elifBody=elifBody,
5801 failureBody=onFailureNotAnObject(failureCode).define(),
5804 if isinstance(defaultValue, IDLNullValue):
5805 assert type.nullable() # Parser should enforce this
5806 templateBody = handleDefault(templateBody, codeToSetNull)
5807 elif isinstance(defaultValue, IDLEmptySequenceValue):
5808 # Our caller will handle it
5809 pass
5810 else:
5811 assert defaultValue is None
5813 return templateBody
5815 # A helper function for converting things that look like a JSObject*.
5816 def handleJSObjectType(
5817 type, isMember, failureCode, exceptionCode, sourceDescription
5819 if not isMember or isMember == "Union":
5820 if isOptional:
5821 # We have a specialization of Optional that will use a
5822 # Rooted for the storage here.
5823 declType = CGGeneric("JS::Handle<JSObject*>")
5824 else:
5825 declType = CGGeneric("JS::Rooted<JSObject*>")
5826 declArgs = "cx"
5827 else:
5828 assert isMember in (
5829 "Sequence",
5830 "Variadic",
5831 "Dictionary",
5832 "OwningUnion",
5833 "Record",
5835 # We'll get traced by the sequence or dictionary or union tracer
5836 declType = CGGeneric("JSObject*")
5837 declArgs = None
5838 templateBody = "${declName} = &${val}.toObject();\n"
5840 # For JS-implemented APIs, we refuse to allow passing objects that the
5841 # API consumer does not subsume. The extra parens around
5842 # ($${passedToJSImpl}) suppress unreachable code warnings when
5843 # $${passedToJSImpl} is the literal `false`. But Apple is shipping a
5844 # buggy clang (clang 3.9) in Xcode 8.3, so there even the parens are not
5845 # enough. So we manually disable some warnings in clang.
5846 if (
5847 not isinstance(descriptorProvider, Descriptor)
5848 or descriptorProvider.interface.isJSImplemented()
5850 templateBody = (
5851 fill(
5853 #ifdef __clang__
5854 #pragma clang diagnostic push
5855 #pragma clang diagnostic ignored "-Wunreachable-code"
5856 #pragma clang diagnostic ignored "-Wunreachable-code-return"
5857 #endif // __clang__
5858 if (($${passedToJSImpl}) && !CallerSubsumes($${val})) {
5859 cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}");
5860 $*{exceptionCode}
5862 #ifdef __clang__
5863 #pragma clang diagnostic pop
5864 #endif // __clang__
5865 """,
5866 sourceDescription=sourceDescription,
5867 exceptionCode=exceptionCode,
5869 + templateBody
5872 setToNullCode = "${declName} = nullptr;\n"
5873 template = wrapObjectTemplate(templateBody, type, setToNullCode, failureCode)
5874 return JSToNativeConversionInfo(
5875 template, declType=declType, dealWithOptional=isOptional, declArgs=declArgs
5878 def incrementNestingLevel():
5879 if nestingLevel == "":
5880 return 1
5881 return nestingLevel + 1
5883 assert not (isEnforceRange and isClamp) # These are mutually exclusive
5885 if type.isSequence() or type.isObservableArray():
5886 assert not isEnforceRange and not isClamp and not isAllowShared
5888 if failureCode is None:
5889 notSequence = (
5890 'cx.ThrowErrorMessage<MSG_CONVERSION_ERROR>("%s", "%s");\n'
5891 "%s"
5893 firstCap(sourceDescription),
5894 "sequence" if type.isSequence() else "observable array",
5895 exceptionCode,
5898 else:
5899 notSequence = failureCode
5901 nullable = type.nullable()
5902 # Be very careful not to change "type": we need it later
5903 if nullable:
5904 elementType = type.inner.inner
5905 else:
5906 elementType = type.inner
5908 # We want to use auto arrays if we can, but we have to be careful with
5909 # reallocation behavior for arrays. In particular, if we use auto
5910 # arrays for sequences and have a sequence of elements which are
5911 # themselves sequences or have sequences as members, we have a problem.
5912 # In that case, resizing the outermost AutoTArray to the right size
5913 # will memmove its elements, but AutoTArrays are not memmovable and
5914 # hence will end up with pointers to bogus memory, which is bad. To
5915 # deal with this, we typically map WebIDL sequences to our Sequence
5916 # type, which is in fact memmovable. The one exception is when we're
5917 # passing in a sequence directly as an argument without any sort of
5918 # optional or nullable complexity going on. In that situation, we can
5919 # use an AutoSequence instead. We have to keep using Sequence in the
5920 # nullable and optional cases because we don't want to leak the
5921 # AutoSequence type to consumers, which would be unavoidable with
5922 # Nullable<AutoSequence> or Optional<AutoSequence>.
5923 if (
5924 (isMember and isMember != "Union")
5925 or isOptional
5926 or nullable
5927 or isCallbackReturnValue
5929 sequenceClass = "Sequence"
5930 else:
5931 sequenceClass = "binding_detail::AutoSequence"
5933 # XXXbz we can't include the index in the sourceDescription, because
5934 # we don't really have a way to pass one in dynamically at runtime...
5935 elementInfo = getJSToNativeConversionInfo(
5936 elementType,
5937 descriptorProvider,
5938 isMember="Sequence",
5939 exceptionCode=exceptionCode,
5940 lenientFloatCode=lenientFloatCode,
5941 isCallbackReturnValue=isCallbackReturnValue,
5942 sourceDescription="element of %s" % sourceDescription,
5943 nestingLevel=incrementNestingLevel(),
5945 if elementInfo.dealWithOptional:
5946 raise TypeError("Shouldn't have optional things in sequences")
5947 if elementInfo.holderType is not None:
5948 raise TypeError("Shouldn't need holders for sequences")
5950 typeName = CGTemplatedType(sequenceClass, elementInfo.declType)
5951 sequenceType = typeName.define()
5953 if isMember == "Union" and typeNeedsRooting(type):
5954 assert not nullable
5955 typeName = CGTemplatedType(
5956 "binding_detail::RootedAutoSequence", elementInfo.declType
5958 elif nullable:
5959 typeName = CGTemplatedType("Nullable", typeName)
5961 if nullable:
5962 arrayRef = "${declName}.SetValue()"
5963 else:
5964 arrayRef = "${declName}"
5966 elementConversion = string.Template(elementInfo.template).substitute(
5968 "val": "temp" + str(nestingLevel),
5969 "maybeMutableVal": "&temp" + str(nestingLevel),
5970 "declName": "slot" + str(nestingLevel),
5971 # We only need holderName here to handle isExternal()
5972 # interfaces, which use an internal holder for the
5973 # conversion even when forceOwningType ends up true.
5974 "holderName": "tempHolder" + str(nestingLevel),
5975 "passedToJSImpl": "${passedToJSImpl}",
5979 elementInitializer = initializerForType(elementType)
5980 if elementInitializer is None:
5981 elementInitializer = ""
5982 else:
5983 elementInitializer = elementInitializer + ", "
5985 # NOTE: Keep this in sync with variadic conversions as needed
5986 templateBody = fill(
5988 JS::ForOfIterator iter${nestingLevel}(cx);
5989 if (!iter${nestingLevel}.init($${val}, JS::ForOfIterator::AllowNonIterable)) {
5990 $*{exceptionCode}
5992 if (!iter${nestingLevel}.valueIsIterable()) {
5993 $*{notSequence}
5995 ${sequenceType} &arr${nestingLevel} = ${arrayRef};
5996 JS::Rooted<JS::Value> temp${nestingLevel}(cx);
5997 while (true) {
5998 bool done${nestingLevel};
5999 if (!iter${nestingLevel}.next(&temp${nestingLevel}, &done${nestingLevel})) {
6000 $*{exceptionCode}
6002 if (done${nestingLevel}) {
6003 break;
6005 ${elementType}* slotPtr${nestingLevel} = arr${nestingLevel}.AppendElement(${elementInitializer}mozilla::fallible);
6006 if (!slotPtr${nestingLevel}) {
6007 JS_ReportOutOfMemory(cx);
6008 $*{exceptionCode}
6010 ${elementType}& slot${nestingLevel} = *slotPtr${nestingLevel};
6011 $*{elementConversion}
6013 """,
6014 exceptionCode=exceptionCode,
6015 notSequence=notSequence,
6016 sequenceType=sequenceType,
6017 arrayRef=arrayRef,
6018 elementType=elementInfo.declType.define(),
6019 elementConversion=elementConversion,
6020 elementInitializer=elementInitializer,
6021 nestingLevel=str(nestingLevel),
6024 templateBody = wrapObjectTemplate(
6025 templateBody, type, "${declName}.SetNull();\n", notSequence
6027 if isinstance(defaultValue, IDLEmptySequenceValue):
6028 if type.nullable():
6029 codeToSetEmpty = "${declName}.SetValue();\n"
6030 else:
6031 codeToSetEmpty = (
6032 "/* ${declName} array is already empty; nothing to do */\n"
6034 templateBody = handleDefault(templateBody, codeToSetEmpty)
6036 declArgs = None
6037 holderType = None
6038 holderArgs = None
6039 # Sequence arguments that might contain traceable things need
6040 # to get traced
6041 if typeNeedsRooting(elementType):
6042 if not isMember:
6043 holderType = CGTemplatedType("SequenceRooter", elementInfo.declType)
6044 # If our sequence is nullable, this will set the Nullable to be
6045 # not-null, but that's ok because we make an explicit SetNull() call
6046 # on it as needed if our JS value is actually null.
6047 holderArgs = "cx, &%s" % arrayRef
6048 elif isMember == "Union":
6049 declArgs = "cx"
6051 return JSToNativeConversionInfo(
6052 templateBody,
6053 declType=typeName,
6054 declArgs=declArgs,
6055 holderType=holderType,
6056 dealWithOptional=isOptional,
6057 holderArgs=holderArgs,
6060 if type.isRecord():
6061 assert not isEnforceRange and not isClamp and not isAllowShared
6062 if failureCode is None:
6063 notRecord = 'cx.ThrowErrorMessage<MSG_NOT_OBJECT>("%s");\n' "%s" % (
6064 firstCap(sourceDescription),
6065 exceptionCode,
6067 else:
6068 notRecord = failureCode
6070 nullable = type.nullable()
6071 # Be very careful not to change "type": we need it later
6072 if nullable:
6073 recordType = type.inner
6074 else:
6075 recordType = type
6076 valueType = recordType.inner
6078 valueInfo = getJSToNativeConversionInfo(
6079 valueType,
6080 descriptorProvider,
6081 isMember="Record",
6082 exceptionCode=exceptionCode,
6083 lenientFloatCode=lenientFloatCode,
6084 isCallbackReturnValue=isCallbackReturnValue,
6085 sourceDescription="value in %s" % sourceDescription,
6086 nestingLevel=incrementNestingLevel(),
6088 if valueInfo.dealWithOptional:
6089 raise TypeError("Shouldn't have optional things in record")
6090 if valueInfo.holderType is not None:
6091 raise TypeError("Shouldn't need holders for record")
6093 declType = CGTemplatedType(
6094 "Record", [recordKeyDeclType(recordType), valueInfo.declType]
6096 typeName = declType.define()
6098 if isMember == "Union" and typeNeedsRooting(type):
6099 assert not nullable
6100 declType = CGTemplatedType(
6101 "RootedRecord", [recordKeyDeclType(recordType), valueInfo.declType]
6103 elif nullable:
6104 declType = CGTemplatedType("Nullable", declType)
6106 if nullable:
6107 recordRef = "${declName}.SetValue()"
6108 else:
6109 recordRef = "${declName}"
6111 valueConversion = string.Template(valueInfo.template).substitute(
6113 "val": "temp",
6114 "maybeMutableVal": "&temp",
6115 "declName": "slot",
6116 # We only need holderName here to handle isExternal()
6117 # interfaces, which use an internal holder for the
6118 # conversion even when forceOwningType ends up true.
6119 "holderName": "tempHolder",
6120 "passedToJSImpl": "${passedToJSImpl}",
6124 keyType = recordKeyType(recordType)
6125 if recordType.keyType.isJSString():
6126 raise TypeError(
6127 "Have do deal with JSString record type, but don't know how"
6129 if recordType.keyType.isByteString() or recordType.keyType.isUTF8String():
6130 hashKeyType = "nsCStringHashKey"
6131 if recordType.keyType.isByteString():
6132 keyConversionFunction = "ConvertJSValueToByteString"
6133 else:
6134 keyConversionFunction = "ConvertJSValueToString"
6136 else:
6137 hashKeyType = "nsStringHashKey"
6138 if recordType.keyType.isDOMString():
6139 keyConversionFunction = "ConvertJSValueToString"
6140 else:
6141 assert recordType.keyType.isUSVString()
6142 keyConversionFunction = "ConvertJSValueToUSVString"
6144 templateBody = fill(
6146 auto& recordEntries = ${recordRef}.Entries();
6148 JS::Rooted<JSObject*> recordObj(cx, &$${val}.toObject());
6149 JS::RootedVector<jsid> ids(cx);
6150 if (!js::GetPropertyKeys(cx, recordObj,
6151 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &ids)) {
6152 $*{exceptionCode}
6154 if (!recordEntries.SetCapacity(ids.length(), mozilla::fallible)) {
6155 JS_ReportOutOfMemory(cx);
6156 $*{exceptionCode}
6158 JS::Rooted<JS::Value> propNameValue(cx);
6159 JS::Rooted<JS::Value> temp(cx);
6160 JS::Rooted<jsid> curId(cx);
6161 JS::Rooted<JS::Value> idVal(cx);
6162 // Use a hashset to keep track of ids seen, to avoid
6163 // introducing nasty O(N^2) behavior scanning for them all the
6164 // time. Ideally we'd use a data structure with O(1) lookup
6165 // _and_ ordering for the MozMap, but we don't have one lying
6166 // around.
6167 nsTHashtable<${hashKeyType}> idsSeen;
6168 for (size_t i = 0; i < ids.length(); ++i) {
6169 curId = ids[i];
6171 JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> desc(cx);
6172 if (!JS_GetOwnPropertyDescriptorById(cx, recordObj, curId,
6173 &desc)) {
6174 $*{exceptionCode}
6177 if (desc.isNothing() || !desc->enumerable()) {
6178 continue;
6181 idVal = js::IdToValue(curId);
6182 ${keyType} propName;
6183 // This will just throw if idVal is a Symbol, like the spec says
6184 // to do.
6185 if (!${keyConversionFunction}(cx, idVal, "key of ${sourceDescription}", propName)) {
6186 $*{exceptionCode}
6189 if (!JS_GetPropertyById(cx, recordObj, curId, &temp)) {
6190 $*{exceptionCode}
6193 ${typeName}::EntryType* entry;
6194 if (!idsSeen.EnsureInserted(propName)) {
6195 // Find the existing entry.
6196 auto idx = recordEntries.IndexOf(propName);
6197 MOZ_ASSERT(idx != recordEntries.NoIndex,
6198 "Why is it not found?");
6199 // Now blow it away to make it look like it was just added
6200 // to the array, because it's not obvious that it's
6201 // safe to write to its already-initialized mValue via our
6202 // normal codegen conversions. For example, the value
6203 // could be a union and this would change its type, but
6204 // codegen assumes we won't do that.
6205 entry = recordEntries.ReconstructElementAt(idx);
6206 } else {
6207 // Safe to do an infallible append here, because we did a
6208 // SetCapacity above to the right capacity.
6209 entry = recordEntries.AppendElement();
6211 entry->mKey = propName;
6212 ${valueType}& slot = entry->mValue;
6213 $*{valueConversion}
6215 """,
6216 exceptionCode=exceptionCode,
6217 recordRef=recordRef,
6218 hashKeyType=hashKeyType,
6219 keyType=keyType,
6220 keyConversionFunction=keyConversionFunction,
6221 sourceDescription=sourceDescription,
6222 typeName=typeName,
6223 valueType=valueInfo.declType.define(),
6224 valueConversion=valueConversion,
6227 templateBody = wrapObjectTemplate(
6228 templateBody, type, "${declName}.SetNull();\n", notRecord
6231 declArgs = None
6232 holderType = None
6233 holderArgs = None
6234 # record arguments that might contain traceable things need
6235 # to get traced
6236 if not isMember and isCallbackReturnValue:
6237 # Go ahead and just convert directly into our actual return value
6238 declType = CGWrapper(declType, post="&")
6239 declArgs = "aRetVal"
6240 elif typeNeedsRooting(valueType):
6241 if not isMember:
6242 holderType = CGTemplatedType(
6243 "RecordRooter", [recordKeyDeclType(recordType), valueInfo.declType]
6245 # If our record is nullable, this will set the Nullable to be
6246 # not-null, but that's ok because we make an explicit SetNull() call
6247 # on it as needed if our JS value is actually null.
6248 holderArgs = "cx, &%s" % recordRef
6249 elif isMember == "Union":
6250 declArgs = "cx"
6252 return JSToNativeConversionInfo(
6253 templateBody,
6254 declType=declType,
6255 declArgs=declArgs,
6256 holderType=holderType,
6257 dealWithOptional=isOptional,
6258 holderArgs=holderArgs,
6261 if type.isUnion():
6262 nullable = type.nullable()
6263 if nullable:
6264 type = type.inner
6266 isOwningUnion = (isMember and isMember != "Union") or isCallbackReturnValue
6267 unionArgumentObj = "${declName}"
6268 if nullable:
6269 if isOptional and not isOwningUnion:
6270 unionArgumentObj += ".Value()"
6271 # If we're owning, we're a Nullable, which hasn't been told it has
6272 # a value. Otherwise we're an already-constructed Maybe.
6273 unionArgumentObj += ".SetValue()"
6275 templateBody = CGIfWrapper(
6276 CGGeneric(exceptionCode),
6277 '!%s.Init(cx, ${val}, "%s", ${passedToJSImpl})'
6278 % (unionArgumentObj, firstCap(sourceDescription)),
6281 if type.hasNullableType:
6282 assert not nullable
6283 # Make sure to handle a null default value here
6284 if defaultValue and isinstance(defaultValue, IDLNullValue):
6285 assert defaultValue.type == type
6286 templateBody = CGIfElseWrapper(
6287 "!(${haveValue})",
6288 CGGeneric("%s.SetNull();\n" % unionArgumentObj),
6289 templateBody,
6292 typeName = CGUnionStruct.unionTypeDecl(type, isOwningUnion)
6293 argumentTypeName = typeName + "Argument"
6294 if nullable:
6295 typeName = "Nullable<" + typeName + " >"
6297 declType = CGGeneric(typeName)
6298 if isOwningUnion:
6299 holderType = None
6300 else:
6301 holderType = CGGeneric(argumentTypeName)
6302 if nullable:
6303 holderType = CGTemplatedType("Maybe", holderType)
6305 # If we're isOptional and not nullable the normal optional handling will
6306 # handle lazy construction of our holder. If we're nullable and not
6307 # owning we do it all by hand because we do not want our holder
6308 # constructed if we're null. But if we're owning we don't have a
6309 # holder anyway, so we can do the normal Optional codepath.
6310 declLoc = "${declName}"
6311 constructDecl = None
6312 if nullable:
6313 if isOptional and not isOwningUnion:
6314 declType = CGTemplatedType("Optional", declType)
6315 constructDecl = CGGeneric("${declName}.Construct();\n")
6316 declLoc = "${declName}.Value()"
6318 if not isMember and isCallbackReturnValue:
6319 declType = CGWrapper(declType, post="&")
6320 declArgs = "aRetVal"
6321 else:
6322 declArgs = None
6324 if (
6325 defaultValue
6326 and not isinstance(defaultValue, IDLNullValue)
6327 and not isinstance(defaultValue, IDLDefaultDictionaryValue)
6329 tag = defaultValue.type.tag()
6331 if tag in numericSuffixes or tag is IDLType.Tags.bool:
6332 defaultStr = getHandleDefault(defaultValue)
6333 # Make sure we actually construct the thing inside the nullable.
6334 value = declLoc + (".SetValue()" if nullable else "")
6335 name = getUnionMemberName(defaultValue.type)
6336 default = CGGeneric(
6337 "%s.RawSetAs%s() = %s;\n" % (value, name, defaultStr)
6339 elif isinstance(defaultValue, IDLEmptySequenceValue):
6340 name = getUnionMemberName(defaultValue.type)
6341 # Make sure we actually construct the thing inside the nullable.
6342 value = declLoc + (".SetValue()" if nullable else "")
6343 if not isOwningUnion and typeNeedsRooting(defaultValue.type):
6344 ctorArgs = "cx"
6345 else:
6346 ctorArgs = ""
6347 # It's enough to set us to the right type; that will
6348 # create an empty array, which is all we need here.
6349 default = CGGeneric(
6350 "Unused << %s.RawSetAs%s(%s);\n" % (value, name, ctorArgs)
6352 elif defaultValue.type.isEnum():
6353 name = getUnionMemberName(defaultValue.type)
6354 # Make sure we actually construct the thing inside the nullable.
6355 value = declLoc + (".SetValue()" if nullable else "")
6356 default = CGGeneric(
6357 "%s.RawSetAs%s() = %s::%s;\n"
6359 value,
6360 name,
6361 defaultValue.type.inner.identifier.name,
6362 getEnumValueName(defaultValue.value),
6365 else:
6366 default = CGGeneric(
6367 handleDefaultStringValue(
6368 defaultValue, "%s.SetStringLiteral" % unionArgumentObj
6372 templateBody = CGIfElseWrapper("!(${haveValue})", default, templateBody)
6374 if nullable:
6375 assert not type.hasNullableType
6376 if defaultValue:
6377 if isinstance(defaultValue, IDLNullValue):
6378 extraConditionForNull = "!(${haveValue}) || "
6379 else:
6380 extraConditionForNull = "(${haveValue}) && "
6381 else:
6382 extraConditionForNull = ""
6384 hasUndefinedType = any(t.isUndefined() for t in type.flatMemberTypes)
6385 assert not hasUndefinedType or defaultValue is None
6387 nullTest = (
6388 "${val}.isNull()" if hasUndefinedType else "${val}.isNullOrUndefined()"
6390 templateBody = CGIfElseWrapper(
6391 extraConditionForNull + nullTest,
6392 CGGeneric("%s.SetNull();\n" % declLoc),
6393 templateBody,
6395 elif (
6396 not type.hasNullableType
6397 and defaultValue
6398 and isinstance(defaultValue, IDLDefaultDictionaryValue)
6400 assert type.hasDictionaryType()
6401 assert defaultValue.type.isDictionary()
6402 if not isOwningUnion and typeNeedsRooting(defaultValue.type):
6403 ctorArgs = "cx"
6404 else:
6405 ctorArgs = ""
6406 initDictionaryWithNull = CGIfWrapper(
6407 CGGeneric("return false;\n"),
6409 '!%s.RawSetAs%s(%s).Init(cx, JS::NullHandleValue, "Member of %s")'
6411 declLoc,
6412 getUnionMemberName(defaultValue.type),
6413 ctorArgs,
6414 type.prettyName(),
6418 templateBody = CGIfElseWrapper(
6419 "!(${haveValue})", initDictionaryWithNull, templateBody
6422 templateBody = CGList([constructDecl, templateBody])
6424 return JSToNativeConversionInfo(
6425 templateBody.define(),
6426 declType=declType,
6427 declArgs=declArgs,
6428 dealWithOptional=isOptional and (not nullable or isOwningUnion),
6431 if type.isPromise():
6432 assert not type.nullable()
6433 assert defaultValue is None
6435 # We always have to hold a strong ref to Promise here, because
6436 # Promise::resolve returns an addrefed thing.
6437 argIsPointer = isCallbackReturnValue
6438 if argIsPointer:
6439 declType = CGGeneric("RefPtr<Promise>")
6440 else:
6441 declType = CGGeneric("OwningNonNull<Promise>")
6443 # Per spec, what we're supposed to do is take the original
6444 # Promise.resolve and call it with the original Promise as this
6445 # value to make a Promise out of whatever value we actually have
6446 # here. The question is which global we should use. There are
6447 # several cases to consider:
6449 # 1) Normal call to API with a Promise argument. This is a case the
6450 # spec covers, and we should be using the current Realm's
6451 # Promise. That means the current compartment.
6452 # 2) Call to API with a Promise argument over Xrays. In practice,
6453 # this sort of thing seems to be used for giving an API
6454 # implementation a way to wait for conclusion of an asyc
6455 # operation, _not_ to expose the Promise to content code. So we
6456 # probably want to allow callers to use such an API in a
6457 # "natural" way, by passing chrome-side promises; indeed, that
6458 # may be all that the caller has to represent their async
6459 # operation. That means we really need to do the
6460 # Promise.resolve() in the caller (chrome) compartment: if we do
6461 # it in the content compartment, we will try to call .then() on
6462 # the chrome promise while in the content compartment, which will
6463 # throw and we'll just get a rejected Promise. Note that this is
6464 # also the reason why a caller who has a chrome Promise
6465 # representing an async operation can't itself convert it to a
6466 # content-side Promise (at least not without some serious
6467 # gyrations).
6468 # 3) Promise return value from a callback or callback interface.
6469 # Per spec, this should use the Realm of the callback object. In
6470 # our case, that's the compartment of the underlying callback,
6471 # not the current compartment (which may be the compartment of
6472 # some cross-compartment wrapper around said callback).
6473 # 4) Return value from a JS-implemented interface. In this case we
6474 # have a problem. Our current compartment is the compartment of
6475 # the JS implementation. But if the JS implementation returned
6476 # a page-side Promise (which is a totally sane thing to do, and
6477 # in fact the right thing to do given that this return value is
6478 # going right to content script) then we don't want to
6479 # Promise.resolve with our current compartment Promise, because
6480 # that will wrap it up in a chrome-side Promise, which is
6481 # decidedly _not_ what's desired here. So in that case we
6482 # should really unwrap the return value and use the global of
6483 # the result. CheckedUnwrapStatic should be good enough for that;
6484 # if it fails, then we're failing unwrap while in a
6485 # system-privileged compartment, so presumably we have a dead
6486 # object wrapper. Just error out. Do NOT fall back to using
6487 # the current compartment instead: that will return a
6488 # system-privileged rejected (because getting .then inside
6489 # resolve() failed) Promise to the caller, which they won't be
6490 # able to touch. That's not helpful. If we error out, on the
6491 # other hand, they will get a content-side rejected promise.
6492 # Same thing if the value returned is not even an object.
6493 if isCallbackReturnValue == "JSImpl":
6494 # Case 4 above. Note that globalObj defaults to the current
6495 # compartment global. Note that we don't use $*{exceptionCode}
6496 # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED)
6497 # which we don't really want here.
6498 assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n"
6499 getPromiseGlobal = fill(
6501 if (!$${val}.isObject()) {
6502 aRv.ThrowTypeError<MSG_NOT_OBJECT>("${sourceDescription}");
6503 return nullptr;
6505 JSObject* unwrappedVal = js::CheckedUnwrapStatic(&$${val}.toObject());
6506 if (!unwrappedVal) {
6507 // A slight lie, but not much of one, for a dead object wrapper.
6508 aRv.ThrowTypeError<MSG_NOT_OBJECT>("${sourceDescription}");
6509 return nullptr;
6511 globalObj = JS::GetNonCCWObjectGlobal(unwrappedVal);
6512 """,
6513 sourceDescription=sourceDescription,
6515 elif isCallbackReturnValue == "Callback":
6516 getPromiseGlobal = dedent(
6518 // We basically want our entry global here. Play it safe
6519 // and use GetEntryGlobal() to get it, with whatever
6520 // principal-clamping it ends up doing.
6521 globalObj = GetEntryGlobal()->GetGlobalJSObject();
6524 else:
6525 getPromiseGlobal = dedent(
6527 globalObj = JS::CurrentGlobalOrNull(cx);
6531 templateBody = fill(
6533 { // Scope for our GlobalObject, FastErrorResult, JSAutoRealm,
6534 // etc.
6536 JS::Rooted<JSObject*> globalObj(cx);
6537 $*{getPromiseGlobal}
6538 JSAutoRealm ar(cx, globalObj);
6539 GlobalObject promiseGlobal(cx, globalObj);
6540 if (promiseGlobal.Failed()) {
6541 $*{exceptionCode}
6544 JS::Rooted<JS::Value> valueToResolve(cx, $${val});
6545 if (!JS_WrapValue(cx, &valueToResolve)) {
6546 $*{exceptionCode}
6548 binding_detail::FastErrorResult promiseRv;
6549 nsCOMPtr<nsIGlobalObject> global =
6550 do_QueryInterface(promiseGlobal.GetAsSupports());
6551 if (!global) {
6552 promiseRv.Throw(NS_ERROR_UNEXPECTED);
6553 MOZ_ALWAYS_TRUE(promiseRv.MaybeSetPendingException(cx));
6554 $*{exceptionCode}
6556 $${declName} = Promise::Resolve(global, cx, valueToResolve,
6557 promiseRv);
6558 if (promiseRv.MaybeSetPendingException(cx)) {
6559 $*{exceptionCode}
6562 """,
6563 getPromiseGlobal=getPromiseGlobal,
6564 exceptionCode=exceptionCode,
6567 return JSToNativeConversionInfo(
6568 templateBody, declType=declType, dealWithOptional=isOptional
6571 if type.isGeckoInterface():
6572 assert not isEnforceRange and not isClamp and not isAllowShared
6574 descriptor = descriptorProvider.getDescriptor(
6575 type.unroll().inner.identifier.name
6578 assert descriptor.nativeType != "JSObject"
6580 if descriptor.interface.isCallback():
6581 (declType, declArgs, conversion) = getCallbackConversionInfo(
6582 type, descriptor.interface, isMember, isCallbackReturnValue, isOptional
6584 template = wrapObjectTemplate(
6585 conversion, type, "${declName} = nullptr;\n", failureCode
6587 return JSToNativeConversionInfo(
6588 template,
6589 declType=declType,
6590 declArgs=declArgs,
6591 dealWithOptional=isOptional,
6594 if descriptor.interface.identifier.name == "WindowProxy":
6595 declType = CGGeneric("mozilla::dom::WindowProxyHolder")
6596 if type.nullable():
6597 declType = CGTemplatedType("Nullable", declType)
6598 windowProxyHolderRef = "${declName}.SetValue()"
6599 else:
6600 windowProxyHolderRef = "${declName}"
6602 failureCode = onFailureBadType(
6603 failureCode, descriptor.interface.identifier.name
6604 ).define()
6605 templateBody = fill(
6607 JS::Rooted<JSObject*> source(cx, &$${val}.toObject());
6608 if (NS_FAILED(UnwrapWindowProxyArg(cx, source, ${windowProxyHolderRef}))) {
6609 $*{onFailure}
6611 """,
6612 windowProxyHolderRef=windowProxyHolderRef,
6613 onFailure=failureCode,
6615 templateBody = wrapObjectTemplate(
6616 templateBody, type, "${declName}.SetNull();\n", failureCode
6618 return JSToNativeConversionInfo(
6619 templateBody, declType=declType, dealWithOptional=isOptional
6622 # This is an interface that we implement as a concrete class
6623 # or an XPCOM interface.
6625 # Allow null pointers for nullable types and old-binding classes, and
6626 # use an RefPtr or raw pointer for callback return values to make
6627 # them easier to return.
6628 argIsPointer = (
6629 type.nullable() or type.unroll().inner.isExternal() or isCallbackReturnValue
6632 # Sequence and dictionary members, as well as owning unions (which can
6633 # appear here as return values in JS-implemented interfaces) have to
6634 # hold a strong ref to the thing being passed down. Those all set
6635 # isMember.
6637 # Also, callback return values always end up addrefing anyway, so there
6638 # is no point trying to avoid it here and it makes other things simpler
6639 # since we can assume the return value is a strong ref.
6640 assert not descriptor.interface.isCallback()
6641 forceOwningType = (isMember and isMember != "Union") or isCallbackReturnValue
6643 typeName = descriptor.nativeType
6644 typePtr = typeName + "*"
6646 # Compute a few things:
6647 # - declType is the type we want to return as the first element of our
6648 # tuple.
6649 # - holderType is the type we want to return as the third element
6650 # of our tuple.
6652 # Set up some sensible defaults for these things insofar as we can.
6653 holderType = None
6654 if argIsPointer:
6655 if forceOwningType:
6656 declType = "RefPtr<" + typeName + ">"
6657 else:
6658 declType = typePtr
6659 else:
6660 if forceOwningType:
6661 declType = "OwningNonNull<" + typeName + ">"
6662 else:
6663 declType = "NonNull<" + typeName + ">"
6665 templateBody = ""
6666 if forceOwningType:
6667 templateBody += fill(
6669 static_assert(IsRefcounted<${typeName}>::value, "We can only store refcounted classes.");
6670 """,
6671 typeName=typeName,
6674 if not descriptor.interface.isExternal():
6675 if failureCode is not None:
6676 templateBody += str(
6677 CastableObjectUnwrapper(
6678 descriptor,
6679 "${val}",
6680 "${maybeMutableVal}",
6681 "${declName}",
6682 failureCode,
6685 else:
6686 templateBody += str(
6687 FailureFatalCastableObjectUnwrapper(
6688 descriptor,
6689 "${val}",
6690 "${maybeMutableVal}",
6691 "${declName}",
6692 exceptionCode,
6693 isCallbackReturnValue,
6694 firstCap(sourceDescription),
6697 else:
6698 # External interface. We always have a holder for these, because we
6699 # don't actually know whether we have to addref when unwrapping or not.
6700 # So we just pass an getter_AddRefs(RefPtr) to XPConnect and if we'll
6701 # need a release it'll put a non-null pointer in there.
6702 if forceOwningType:
6703 # Don't return a holderType in this case; our declName
6704 # will just own stuff.
6705 templateBody += "RefPtr<" + typeName + "> ${holderName};\n"
6706 else:
6707 holderType = "RefPtr<" + typeName + ">"
6708 templateBody += (
6709 "JS::Rooted<JSObject*> source(cx, &${val}.toObject());\n"
6710 + "if (NS_FAILED(UnwrapArg<"
6711 + typeName
6712 + ">(cx, source, getter_AddRefs(${holderName})))) {\n"
6714 templateBody += CGIndenter(
6715 onFailureBadType(failureCode, descriptor.interface.identifier.name)
6716 ).define()
6717 templateBody += "}\n" "MOZ_ASSERT(${holderName});\n"
6719 # And store our value in ${declName}
6720 templateBody += "${declName} = ${holderName};\n"
6722 # Just pass failureCode, not onFailureBadType, here, so we'll report
6723 # the thing as not an object as opposed to not implementing whatever
6724 # our interface is.
6725 templateBody = wrapObjectTemplate(
6726 templateBody, type, "${declName} = nullptr;\n", failureCode
6729 declType = CGGeneric(declType)
6730 if holderType is not None:
6731 holderType = CGGeneric(holderType)
6732 return JSToNativeConversionInfo(
6733 templateBody,
6734 declType=declType,
6735 holderType=holderType,
6736 dealWithOptional=isOptional,
6739 if type.isSpiderMonkeyInterface():
6740 assert not isEnforceRange and not isClamp
6741 name = type.unroll().name # unroll() because it may be nullable
6742 interfaceType = CGGeneric(name)
6743 declType = interfaceType
6744 if type.nullable():
6745 declType = CGTemplatedType("Nullable", declType)
6746 objRef = "${declName}.SetValue()"
6747 else:
6748 objRef = "${declName}"
6750 # Again, this is a bit strange since we are actually building a
6751 # template string here. ${objRef} and $*{badType} below are filled in
6752 # right now; $${val} expands to ${val}, to be filled in later.
6753 template = fill(
6755 if (!${objRef}.Init(&$${val}.toObject())) {
6756 $*{badType}
6758 """,
6759 objRef=objRef,
6760 badType=onFailureBadType(failureCode, type.name).define(),
6762 if type.isBufferSource():
6763 if type.isArrayBuffer():
6764 isSharedMethod = "JS::IsSharedArrayBufferObject"
6765 isLargeMethod = "JS::IsLargeArrayBufferMaybeShared"
6766 isResizableMethod = "JS::IsResizableArrayBufferMaybeShared"
6767 else:
6768 assert type.isArrayBufferView() or type.isTypedArray()
6769 isSharedMethod = "JS::IsArrayBufferViewShared"
6770 isLargeMethod = "JS::IsLargeArrayBufferView"
6771 isResizableMethod = "JS::IsResizableArrayBufferView"
6772 if not isAllowShared:
6773 template += fill(
6775 if (${isSharedMethod}(${objRef}.Obj())) {
6776 $*{badType}
6778 """,
6779 isSharedMethod=isSharedMethod,
6780 objRef=objRef,
6781 badType=onFailureIsShared().define(),
6783 # For now reject large (> 2 GB) ArrayBuffers and ArrayBufferViews.
6784 # Supporting this will require changing dom::TypedArray and
6785 # consumers.
6786 template += fill(
6788 if (${isLargeMethod}(${objRef}.Obj())) {
6789 $*{badType}
6791 """,
6792 isLargeMethod=isLargeMethod,
6793 objRef=objRef,
6794 badType=onFailureIsLarge().define(),
6796 # For now reject resizable ArrayBuffers and growable
6797 # SharedArrayBuffers. Supporting this will require changing
6798 # dom::TypedArray and consumers.
6799 template += fill(
6801 if (${isResizableMethod}(${objRef}.Obj())) {
6802 $*{badType}
6804 """,
6805 isResizableMethod=isResizableMethod,
6806 objRef=objRef,
6807 badType=onFailureIsResizable().define(),
6809 template = wrapObjectTemplate(
6810 template, type, "${declName}.SetNull();\n", failureCode
6812 if not isMember or isMember == "Union":
6813 # This is a bit annoying. In a union we don't want to have a
6814 # holder, since unions don't support that. But if we're optional we
6815 # want to have a holder, so that the callee doesn't see
6816 # Optional<RootedSpiderMonkeyInterface<InterfaceType>>. So do a
6817 # holder if we're optional and use a RootedSpiderMonkeyInterface
6818 # otherwise.
6819 if isOptional:
6820 holderType = CGTemplatedType(
6821 "SpiderMonkeyInterfaceRooter", interfaceType
6823 # If our SpiderMonkey interface is nullable, this will set the
6824 # Nullable to be not-null, but that's ok because we make an
6825 # explicit SetNull() call on it as needed if our JS value is
6826 # actually null. XXXbz Because "Maybe" takes const refs for
6827 # constructor arguments, we can't pass a reference here; have
6828 # to pass a pointer.
6829 holderArgs = "cx, &%s" % objRef
6830 declArgs = None
6831 else:
6832 holderType = None
6833 holderArgs = None
6834 declType = CGTemplatedType("RootedSpiderMonkeyInterface", declType)
6835 declArgs = "cx"
6836 else:
6837 holderType = None
6838 holderArgs = None
6839 declArgs = None
6840 return JSToNativeConversionInfo(
6841 template,
6842 declType=declType,
6843 holderType=holderType,
6844 dealWithOptional=isOptional,
6845 declArgs=declArgs,
6846 holderArgs=holderArgs,
6849 if type.isJSString():
6850 assert not isEnforceRange and not isClamp and not isAllowShared
6851 if type.nullable():
6852 raise TypeError("Nullable JSString not supported")
6854 declArgs = "cx"
6855 if isMember:
6856 raise TypeError("JSString not supported as member")
6857 else:
6858 declType = "JS::Rooted<JSString*>"
6860 if isOptional:
6861 raise TypeError("JSString not supported as optional")
6862 templateBody = fill(
6864 if (!($${declName} = ConvertJSValueToJSString(cx, $${val}))) {
6865 $*{exceptionCode}
6867 """,
6868 exceptionCode=exceptionCode,
6871 if defaultValue is not None:
6872 assert not isinstance(defaultValue, IDLNullValue)
6873 defaultCode = fill(
6875 static const char data[] = { ${data} };
6876 $${declName} = JS_NewStringCopyN(cx, data, ArrayLength(data) - 1);
6877 if (!$${declName}) {
6878 $*{exceptionCode}
6880 """,
6881 data=", ".join(
6882 ["'" + char + "'" for char in defaultValue.value] + ["0"]
6884 exceptionCode=exceptionCode,
6887 templateBody = handleDefault(templateBody, defaultCode)
6888 return JSToNativeConversionInfo(
6889 templateBody, declType=CGGeneric(declType), declArgs=declArgs
6892 if type.isDOMString() or type.isUSVString() or type.isUTF8String():
6893 assert not isEnforceRange and not isClamp and not isAllowShared
6895 treatAs = {
6896 "Default": "eStringify",
6897 "EmptyString": "eEmpty",
6898 "Null": "eNull",
6900 if type.nullable():
6901 # For nullable strings null becomes a null string.
6902 treatNullAs = "Null"
6903 # For nullable strings undefined also becomes a null string.
6904 undefinedBehavior = "eNull"
6905 else:
6906 undefinedBehavior = "eStringify"
6907 if type.legacyNullToEmptyString:
6908 treatNullAs = "EmptyString"
6909 else:
6910 treatNullAs = "Default"
6911 nullBehavior = treatAs[treatNullAs]
6913 def getConversionCode(varName):
6914 normalizeCode = ""
6915 if type.isUSVString():
6916 normalizeCode = fill(
6918 if (!NormalizeUSVString(${var})) {
6919 JS_ReportOutOfMemory(cx);
6920 $*{exceptionCode}
6922 """,
6923 var=varName,
6924 exceptionCode=exceptionCode,
6927 conversionCode = fill(
6929 if (!ConvertJSValueToString(cx, $${val}, ${nullBehavior}, ${undefinedBehavior}, ${varName})) {
6930 $*{exceptionCode}
6932 $*{normalizeCode}
6933 """,
6934 nullBehavior=nullBehavior,
6935 undefinedBehavior=undefinedBehavior,
6936 varName=varName,
6937 exceptionCode=exceptionCode,
6938 normalizeCode=normalizeCode,
6941 if defaultValue is None:
6942 return conversionCode
6944 if isinstance(defaultValue, IDLNullValue):
6945 assert type.nullable()
6946 defaultCode = "%s.SetIsVoid(true);\n" % varName
6947 else:
6948 defaultCode = handleDefaultStringValue(
6949 defaultValue, "%s.AssignLiteral" % varName
6951 return handleDefault(conversionCode, defaultCode)
6953 if isMember and isMember != "Union":
6954 # Convert directly into the ns[C]String member we have.
6955 if type.isUTF8String():
6956 declType = "nsCString"
6957 else:
6958 declType = "nsString"
6959 return JSToNativeConversionInfo(
6960 getConversionCode("${declName}"),
6961 declType=CGGeneric(declType),
6962 dealWithOptional=isOptional,
6965 if isOptional:
6966 if type.isUTF8String():
6967 declType = "Optional<nsACString>"
6968 holderType = CGGeneric("binding_detail::FakeString<char>")
6969 else:
6970 declType = "Optional<nsAString>"
6971 holderType = CGGeneric("binding_detail::FakeString<char16_t>")
6972 conversionCode = "%s" "${declName} = &${holderName};\n" % getConversionCode(
6973 "${holderName}"
6975 else:
6976 if type.isUTF8String():
6977 declType = "binding_detail::FakeString<char>"
6978 else:
6979 declType = "binding_detail::FakeString<char16_t>"
6980 holderType = None
6981 conversionCode = getConversionCode("${declName}")
6983 # No need to deal with optional here; we handled it already
6984 return JSToNativeConversionInfo(
6985 conversionCode, declType=CGGeneric(declType), holderType=holderType
6988 if type.isByteString():
6989 assert not isEnforceRange and not isClamp and not isAllowShared
6991 nullable = toStringBool(type.nullable())
6993 conversionCode = fill(
6995 if (!ConvertJSValueToByteString(cx, $${val}, ${nullable}, "${sourceDescription}", $${declName})) {
6996 $*{exceptionCode}
6998 """,
6999 nullable=nullable,
7000 sourceDescription=sourceDescription,
7001 exceptionCode=exceptionCode,
7004 if defaultValue is not None:
7005 if isinstance(defaultValue, IDLNullValue):
7006 assert type.nullable()
7007 defaultCode = "${declName}.SetIsVoid(true);\n"
7008 else:
7009 defaultCode = handleDefaultStringValue(
7010 defaultValue, "${declName}.AssignLiteral"
7012 conversionCode = handleDefault(conversionCode, defaultCode)
7014 return JSToNativeConversionInfo(
7015 conversionCode, declType=CGGeneric("nsCString"), dealWithOptional=isOptional
7018 if type.isEnum():
7019 assert not isEnforceRange and not isClamp and not isAllowShared
7021 enumName = type.unroll().inner.identifier.name
7022 declType = CGGeneric(enumName)
7023 if type.nullable():
7024 declType = CGTemplatedType("Nullable", declType)
7025 declType = declType.define()
7026 enumLoc = "${declName}.SetValue()"
7027 else:
7028 enumLoc = "${declName}"
7029 declType = declType.define()
7031 if invalidEnumValueFatal:
7032 handleInvalidEnumValueCode = "MOZ_ASSERT(index >= 0);\n"
7033 else:
7034 # invalidEnumValueFatal is false only for attributes. So we won't
7035 # have a non-default exceptionCode here unless attribute "arg
7036 # conversion" code starts passing in an exceptionCode. At which
7037 # point we'll need to figure out what that even means.
7038 assert exceptionCode == "return false;\n"
7039 handleInvalidEnumValueCode = dedent(
7041 if (index < 0) {
7042 return true;
7047 template = fill(
7050 int index;
7051 if (!binding_detail::FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val},
7052 binding_detail::EnumStrings<${enumtype}>::Values,
7053 "${enumtype}", "${sourceDescription}",
7054 &index)) {
7055 $*{exceptionCode}
7057 $*{handleInvalidEnumValueCode}
7058 ${enumLoc} = static_cast<${enumtype}>(index);
7060 """,
7061 enumtype=enumName,
7062 invalidEnumValueFatal=toStringBool(invalidEnumValueFatal),
7063 handleInvalidEnumValueCode=handleInvalidEnumValueCode,
7064 exceptionCode=exceptionCode,
7065 enumLoc=enumLoc,
7066 sourceDescription=sourceDescription,
7069 setNull = "${declName}.SetNull();\n"
7071 if type.nullable():
7072 template = CGIfElseWrapper(
7073 "${val}.isNullOrUndefined()", CGGeneric(setNull), CGGeneric(template)
7074 ).define()
7076 if defaultValue is not None:
7077 if isinstance(defaultValue, IDLNullValue):
7078 assert type.nullable()
7079 template = handleDefault(template, setNull)
7080 else:
7081 assert defaultValue.type.tag() == IDLType.Tags.domstring
7082 template = handleDefault(
7083 template,
7085 "%s = %s::%s;\n"
7086 % (enumLoc, enumName, getEnumValueName(defaultValue.value))
7089 return JSToNativeConversionInfo(
7090 template, declType=CGGeneric(declType), dealWithOptional=isOptional
7093 if type.isCallback():
7094 assert not isEnforceRange and not isClamp and not isAllowShared
7095 assert not type.treatNonCallableAsNull() or type.nullable()
7096 assert not type.treatNonObjectAsNull() or type.nullable()
7097 assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
7099 callback = type.unroll().callback
7100 name = callback.identifier.name
7101 (declType, declArgs, conversion) = getCallbackConversionInfo(
7102 type, callback, isMember, isCallbackReturnValue, isOptional
7105 if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
7106 haveCallable = "JS::IsCallable(&${val}.toObject())"
7107 if not isDefinitelyObject:
7108 haveCallable = "${val}.isObject() && " + haveCallable
7109 if defaultValue is not None:
7110 assert isinstance(defaultValue, IDLNullValue)
7111 haveCallable = "(${haveValue}) && " + haveCallable
7112 template = (
7113 ("if (%s) {\n" % haveCallable) + conversion + "} else {\n"
7114 " ${declName} = nullptr;\n"
7115 "}\n"
7117 elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull():
7118 if not isDefinitelyObject:
7119 haveObject = "${val}.isObject()"
7120 if defaultValue is not None:
7121 assert isinstance(defaultValue, IDLNullValue)
7122 haveObject = "(${haveValue}) && " + haveObject
7123 template = CGIfElseWrapper(
7124 haveObject,
7125 CGGeneric(conversion),
7126 CGGeneric("${declName} = nullptr;\n"),
7127 ).define()
7128 else:
7129 template = conversion
7130 else:
7131 template = wrapObjectTemplate(
7132 "if (JS::IsCallable(&${val}.toObject())) {\n"
7133 + conversion
7134 + "} else {\n"
7135 + indent(onFailureNotCallable(failureCode).define())
7136 + "}\n",
7137 type,
7138 "${declName} = nullptr;\n",
7139 failureCode,
7141 return JSToNativeConversionInfo(
7142 template, declType=declType, declArgs=declArgs, dealWithOptional=isOptional
7145 if type.isAny():
7146 assert not isEnforceRange and not isClamp and not isAllowShared
7148 declArgs = None
7149 if isMember in ("Variadic", "Sequence", "Dictionary", "Record"):
7150 # Rooting is handled by the sequence and dictionary tracers.
7151 declType = "JS::Value"
7152 else:
7153 assert not isMember
7154 declType = "JS::Rooted<JS::Value>"
7155 declArgs = "cx"
7157 assert not isOptional
7158 templateBody = "${declName} = ${val};\n"
7160 # For JS-implemented APIs, we refuse to allow passing objects that the
7161 # API consumer does not subsume. The extra parens around
7162 # ($${passedToJSImpl}) suppress unreachable code warnings when
7163 # $${passedToJSImpl} is the literal `false`. But Apple is shipping a
7164 # buggy clang (clang 3.9) in Xcode 8.3, so there even the parens are not
7165 # enough. So we manually disable some warnings in clang.
7166 if (
7167 not isinstance(descriptorProvider, Descriptor)
7168 or descriptorProvider.interface.isJSImplemented()
7170 templateBody = (
7171 fill(
7173 #ifdef __clang__
7174 #pragma clang diagnostic push
7175 #pragma clang diagnostic ignored "-Wunreachable-code"
7176 #pragma clang diagnostic ignored "-Wunreachable-code-return"
7177 #endif // __clang__
7178 if (($${passedToJSImpl}) && !CallerSubsumes($${val})) {
7179 cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}");
7180 $*{exceptionCode}
7182 #ifdef __clang__
7183 #pragma clang diagnostic pop
7184 #endif // __clang__
7185 """,
7186 sourceDescription=sourceDescription,
7187 exceptionCode=exceptionCode,
7189 + templateBody
7192 # We may not have a default value if we're being converted for
7193 # a setter, say.
7194 if defaultValue:
7195 if isinstance(defaultValue, IDLNullValue):
7196 defaultHandling = "${declName} = JS::NullValue();\n"
7197 else:
7198 assert isinstance(defaultValue, IDLUndefinedValue)
7199 defaultHandling = "${declName} = JS::UndefinedValue();\n"
7200 templateBody = handleDefault(templateBody, defaultHandling)
7201 return JSToNativeConversionInfo(
7202 templateBody, declType=CGGeneric(declType), declArgs=declArgs
7205 if type.isObject():
7206 assert not isEnforceRange and not isClamp and not isAllowShared
7207 return handleJSObjectType(
7208 type, isMember, failureCode, exceptionCode, sourceDescription
7211 if type.isDictionary():
7212 # There are no nullable dictionary-typed arguments or dictionary-typed
7213 # dictionary members.
7214 assert (
7215 not type.nullable()
7216 or isCallbackReturnValue
7217 or (isMember and isMember != "Dictionary")
7219 # All optional dictionary-typed arguments always have default values,
7220 # but dictionary-typed dictionary members can be optional.
7221 assert not isOptional or isMember == "Dictionary"
7222 # In the callback return value case we never have to worry
7223 # about a default value; we always have a value.
7224 assert not isCallbackReturnValue or defaultValue is None
7226 typeName = CGDictionary.makeDictionaryName(type.unroll().inner)
7227 if (not isMember or isMember == "Union") and not isCallbackReturnValue:
7228 # Since we're not a member and not nullable or optional, no one will
7229 # see our real type, so we can do the fast version of the dictionary
7230 # that doesn't pre-initialize members.
7231 typeName = "binding_detail::Fast" + typeName
7233 declType = CGGeneric(typeName)
7235 # We do manual default value handling here, because we actually do want
7236 # a jsval, and we only handle the default-dictionary case (which we map
7237 # into initialization with the JS value `null`) anyway
7238 # NOTE: if isNullOrUndefined or isDefinitelyObject are true,
7239 # we know we have a value, so we don't have to worry about the
7240 # default value.
7241 if (
7242 not isNullOrUndefined
7243 and not isDefinitelyObject
7244 and defaultValue is not None
7246 assert isinstance(defaultValue, IDLDefaultDictionaryValue)
7247 # Initializing from JS null does the right thing to give
7248 # us a default-initialized dictionary.
7249 val = "(${haveValue}) ? ${val} : JS::NullHandleValue"
7250 else:
7251 val = "${val}"
7253 dictLoc = "${declName}"
7254 if type.nullable():
7255 dictLoc += ".SetValue()"
7257 if type.unroll().inner.needsConversionFromJS:
7258 args = "cx, %s, " % val
7259 else:
7260 # We can end up in this case if a dictionary that does not need
7261 # conversion from JS has a dictionary-typed member with a default
7262 # value of {}.
7263 args = ""
7264 conversionCode = fill(
7266 if (!${dictLoc}.Init(${args}"${desc}", $${passedToJSImpl})) {
7267 $*{exceptionCode}
7269 """,
7270 dictLoc=dictLoc,
7271 args=args,
7272 desc=firstCap(sourceDescription),
7273 exceptionCode=exceptionCode,
7276 if failureCode is not None:
7277 # This means we're part of an overload or union conversion, and
7278 # should simply skip stuff if our value is not convertible to
7279 # dictionary, instead of trying and throwing. If we're either
7280 # isDefinitelyObject or isNullOrUndefined then we're convertible to
7281 # dictionary and don't need to check here.
7282 if isDefinitelyObject or isNullOrUndefined:
7283 template = conversionCode
7284 else:
7285 template = fill(
7287 if (!IsConvertibleToDictionary(${val})) {
7288 $*{failureCode}
7290 $*{conversionCode}
7291 """,
7292 val=val,
7293 failureCode=failureCode,
7294 conversionCode=conversionCode,
7296 else:
7297 template = conversionCode
7299 if type.nullable():
7300 declType = CGTemplatedType("Nullable", declType)
7301 template = CGIfElseWrapper(
7302 "${val}.isNullOrUndefined()",
7303 CGGeneric("${declName}.SetNull();\n"),
7304 CGGeneric(template),
7305 ).define()
7307 # Dictionary arguments that might contain traceable things need to get
7308 # traced
7309 if (not isMember or isMember == "Union") and isCallbackReturnValue:
7310 # Go ahead and just convert directly into our actual return value
7311 declType = CGWrapper(declType, post="&")
7312 declArgs = "aRetVal"
7313 elif (not isMember or isMember == "Union") and typeNeedsRooting(type):
7314 declType = CGTemplatedType("RootedDictionary", declType)
7315 declArgs = "cx"
7316 else:
7317 declArgs = None
7319 return JSToNativeConversionInfo(
7320 template, declType=declType, declArgs=declArgs, dealWithOptional=isOptional
7323 if type.isUndefined():
7324 assert not isOptional
7325 # This one only happens for return values, and its easy: Just
7326 # ignore the jsval.
7327 return JSToNativeConversionInfo("")
7329 if not type.isPrimitive():
7330 raise TypeError("Need conversion for argument type '%s'" % str(type))
7332 typeName = builtinNames[type.tag()]
7334 conversionBehavior = "eDefault"
7335 if isEnforceRange:
7336 assert type.isInteger()
7337 conversionBehavior = "eEnforceRange"
7338 elif isClamp:
7339 assert type.isInteger()
7340 conversionBehavior = "eClamp"
7342 alwaysNull = False
7343 if type.nullable():
7344 declType = CGGeneric("Nullable<" + typeName + ">")
7345 writeLoc = "${declName}.SetValue()"
7346 readLoc = "${declName}.Value()"
7347 nullCondition = "${val}.isNullOrUndefined()"
7348 if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
7349 nullCondition = "!(${haveValue}) || " + nullCondition
7350 if isKnownMissing:
7351 alwaysNull = True
7352 template = dedent(
7354 ${declName}.SetNull();
7357 if not alwaysNull:
7358 template = fill(
7360 if (${nullCondition}) {
7361 $${declName}.SetNull();
7362 } else if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, "${sourceDescription}", &${writeLoc})) {
7363 $*{exceptionCode}
7365 """,
7366 nullCondition=nullCondition,
7367 typeName=typeName,
7368 conversionBehavior=conversionBehavior,
7369 sourceDescription=firstCap(sourceDescription),
7370 writeLoc=writeLoc,
7371 exceptionCode=exceptionCode,
7373 else:
7374 assert defaultValue is None or not isinstance(defaultValue, IDLNullValue)
7375 writeLoc = "${declName}"
7376 readLoc = writeLoc
7377 template = fill(
7379 if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, "${sourceDescription}", &${writeLoc})) {
7380 $*{exceptionCode}
7382 """,
7383 typeName=typeName,
7384 conversionBehavior=conversionBehavior,
7385 sourceDescription=firstCap(sourceDescription),
7386 writeLoc=writeLoc,
7387 exceptionCode=exceptionCode,
7389 declType = CGGeneric(typeName)
7391 if type.isFloat() and not type.isUnrestricted() and not alwaysNull:
7392 if lenientFloatCode is not None:
7393 nonFiniteCode = lenientFloatCode
7394 else:
7395 nonFiniteCode = 'cx.ThrowErrorMessage<MSG_NOT_FINITE>("%s");\n' "%s" % (
7396 firstCap(sourceDescription),
7397 exceptionCode,
7400 # We're appending to an if-block brace, so strip trailing whitespace
7401 # and add an extra space before the else.
7402 template = template.rstrip()
7403 template += fill(
7405 else if (!std::isfinite(${readLoc})) {
7406 $*{nonFiniteCode}
7408 """,
7409 readLoc=readLoc,
7410 nonFiniteCode=nonFiniteCode,
7413 if (
7414 defaultValue is not None
7416 # We already handled IDLNullValue, so just deal with the other ones
7417 not isinstance(defaultValue, IDLNullValue)
7419 tag = defaultValue.type.tag()
7420 defaultStr = getHandleDefault(defaultValue)
7421 template = handleDefault(template, "%s = %s;\n" % (writeLoc, defaultStr))
7423 return JSToNativeConversionInfo(
7424 template, declType=declType, dealWithOptional=isOptional
7428 def instantiateJSToNativeConversion(info, replacements, checkForValue=False):
7430 Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo
7431 and a set of replacements as required by the strings in such an object, and
7432 generate code to convert into stack C++ types.
7434 If checkForValue is True, then the conversion will get wrapped in
7435 a check for ${haveValue}.
7437 templateBody, declType, holderType, dealWithOptional = (
7438 info.template,
7439 info.declType,
7440 info.holderType,
7441 info.dealWithOptional,
7444 if dealWithOptional and not checkForValue:
7445 raise TypeError("Have to deal with optional things, but don't know how")
7446 if checkForValue and declType is None:
7447 raise TypeError(
7448 "Need to predeclare optional things, so they will be "
7449 "outside the check for big enough arg count!"
7452 # We can't precompute our holder constructor arguments, since
7453 # those might depend on ${declName}, which we change below. Just
7454 # compute arguments at the point when we need them as we go.
7455 def getArgsCGThing(args):
7456 return CGGeneric(string.Template(args).substitute(replacements))
7458 result = CGList([])
7459 # Make a copy of "replacements" since we may be about to start modifying it
7460 replacements = dict(replacements)
7461 originalDeclName = replacements["declName"]
7462 if declType is not None:
7463 if dealWithOptional:
7464 replacements["declName"] = "%s.Value()" % originalDeclName
7465 declType = CGTemplatedType("Optional", declType)
7466 declCtorArgs = None
7467 elif info.declArgs is not None:
7468 declCtorArgs = CGWrapper(getArgsCGThing(info.declArgs), pre="(", post=")")
7469 else:
7470 declCtorArgs = None
7471 result.append(
7472 CGList(
7474 declType,
7475 CGGeneric(" "),
7476 CGGeneric(originalDeclName),
7477 declCtorArgs,
7478 CGGeneric(";\n"),
7483 originalHolderName = replacements["holderName"]
7484 if holderType is not None:
7485 if dealWithOptional:
7486 replacements["holderName"] = "%s.ref()" % originalHolderName
7487 holderType = CGTemplatedType("Maybe", holderType)
7488 holderCtorArgs = None
7489 elif info.holderArgs is not None:
7490 holderCtorArgs = CGWrapper(
7491 getArgsCGThing(info.holderArgs), pre="(", post=")"
7493 else:
7494 holderCtorArgs = None
7495 result.append(
7496 CGList(
7498 holderType,
7499 CGGeneric(" "),
7500 CGGeneric(originalHolderName),
7501 holderCtorArgs,
7502 CGGeneric(";\n"),
7507 if "maybeMutableVal" not in replacements:
7508 replacements["maybeMutableVal"] = replacements["val"]
7510 conversion = CGGeneric(string.Template(templateBody).substitute(replacements))
7512 if checkForValue:
7513 if dealWithOptional:
7514 declConstruct = CGIndenter(
7515 CGGeneric(
7516 "%s.Construct(%s);\n"
7518 originalDeclName,
7519 getArgsCGThing(info.declArgs).define() if info.declArgs else "",
7523 if holderType is not None:
7524 holderConstruct = CGIndenter(
7525 CGGeneric(
7526 "%s.emplace(%s);\n"
7528 originalHolderName,
7529 getArgsCGThing(info.holderArgs).define()
7530 if info.holderArgs
7531 else "",
7535 else:
7536 holderConstruct = None
7537 else:
7538 declConstruct = None
7539 holderConstruct = None
7541 conversion = CGList(
7543 CGGeneric(
7544 string.Template("if (${haveValue}) {\n").substitute(replacements)
7546 declConstruct,
7547 holderConstruct,
7548 CGIndenter(conversion),
7549 CGGeneric("}\n"),
7553 result.append(conversion)
7554 return result
7557 def convertConstIDLValueToJSVal(value):
7558 if isinstance(value, IDLNullValue):
7559 return "JS::NullValue()"
7560 if isinstance(value, IDLUndefinedValue):
7561 return "JS::UndefinedValue()"
7562 tag = value.type.tag()
7563 if tag in [
7564 IDLType.Tags.int8,
7565 IDLType.Tags.uint8,
7566 IDLType.Tags.int16,
7567 IDLType.Tags.uint16,
7568 IDLType.Tags.int32,
7570 return "JS::Int32Value(%s)" % (value.value)
7571 if tag == IDLType.Tags.uint32:
7572 return "JS::NumberValue(%sU)" % (value.value)
7573 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
7574 return "JS::CanonicalizedDoubleValue(%s)" % numericValue(tag, value.value)
7575 if tag == IDLType.Tags.bool:
7576 return "JS::BooleanValue(%s)" % (toStringBool(value.value))
7577 if tag in [IDLType.Tags.float, IDLType.Tags.double]:
7578 return "JS::CanonicalizedDoubleValue(%s)" % (value.value)
7579 raise TypeError("Const value of unhandled type: %s" % value.type)
7582 class CGArgumentConverter(CGThing):
7584 A class that takes an IDL argument object and its index in the
7585 argument list and generates code to unwrap the argument to the
7586 right native type.
7588 argDescription is a description of the argument for error-reporting
7589 purposes. Callers should assume that it might get placed in the middle of a
7590 sentence. If it ends up at the beginning of a sentence, its first character
7591 will be automatically uppercased.
7594 def __init__(
7595 self,
7596 argument,
7597 index,
7598 descriptorProvider,
7599 argDescription,
7600 member,
7601 invalidEnumValueFatal=True,
7602 lenientFloatCode=None,
7604 CGThing.__init__(self)
7605 self.argument = argument
7606 self.argDescription = argDescription
7607 assert not argument.defaultValue or argument.optional
7609 replacer = {"index": index, "argc": "args.length()"}
7610 self.replacementVariables = {
7611 "declName": "arg%d" % index,
7612 "holderName": ("arg%d" % index) + "_holder",
7613 "obj": "obj",
7614 "passedToJSImpl": toStringBool(
7615 isJSImplementedDescriptor(descriptorProvider)
7618 # If we have a method generated by the maplike/setlike portion of an
7619 # interface, arguments can possibly be undefined, but will need to be
7620 # converted to the key/value type of the backing object. In this case,
7621 # use .get() instead of direct access to the argument. This won't
7622 # matter for iterable since generated functions for those interface
7623 # don't take arguments.
7624 if member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod():
7625 self.replacementVariables["val"] = string.Template(
7626 "args.get(${index})"
7627 ).substitute(replacer)
7628 self.replacementVariables["maybeMutableVal"] = string.Template(
7629 "args[${index}]"
7630 ).substitute(replacer)
7631 else:
7632 self.replacementVariables["val"] = string.Template(
7633 "args[${index}]"
7634 ).substitute(replacer)
7635 haveValueCheck = string.Template("args.hasDefined(${index})").substitute(
7636 replacer
7638 self.replacementVariables["haveValue"] = haveValueCheck
7639 self.descriptorProvider = descriptorProvider
7640 if self.argument.canHaveMissingValue():
7641 self.argcAndIndex = replacer
7642 else:
7643 self.argcAndIndex = None
7644 self.invalidEnumValueFatal = invalidEnumValueFatal
7645 self.lenientFloatCode = lenientFloatCode
7647 def define(self):
7648 typeConversion = getJSToNativeConversionInfo(
7649 self.argument.type,
7650 self.descriptorProvider,
7651 isOptional=(self.argcAndIndex is not None and not self.argument.variadic),
7652 invalidEnumValueFatal=self.invalidEnumValueFatal,
7653 defaultValue=self.argument.defaultValue,
7654 lenientFloatCode=self.lenientFloatCode,
7655 isMember="Variadic" if self.argument.variadic else False,
7656 allowTreatNonCallableAsNull=self.argument.allowTreatNonCallableAsNull(),
7657 sourceDescription=self.argDescription,
7660 if not self.argument.variadic:
7661 return instantiateJSToNativeConversion(
7662 typeConversion, self.replacementVariables, self.argcAndIndex is not None
7663 ).define()
7665 # Variadic arguments get turned into a sequence.
7666 if typeConversion.dealWithOptional:
7667 raise TypeError("Shouldn't have optional things in variadics")
7668 if typeConversion.holderType is not None:
7669 raise TypeError("Shouldn't need holders for variadics")
7671 replacer = dict(self.argcAndIndex, **self.replacementVariables)
7672 replacer["seqType"] = CGTemplatedType(
7673 "AutoSequence", typeConversion.declType
7674 ).define()
7675 if typeNeedsRooting(self.argument.type):
7676 rooterDecl = (
7677 "SequenceRooter<%s> ${holderName}(cx, &${declName});\n"
7678 % typeConversion.declType.define()
7680 else:
7681 rooterDecl = ""
7682 replacer["elemType"] = typeConversion.declType.define()
7684 replacer["elementInitializer"] = initializerForType(self.argument.type) or ""
7686 # NOTE: Keep this in sync with sequence conversions as needed
7687 variadicConversion = string.Template(
7688 "${seqType} ${declName};\n"
7689 + rooterDecl
7690 + dedent(
7692 if (${argc} > ${index}) {
7693 if (!${declName}.SetCapacity(${argc} - ${index}, mozilla::fallible)) {
7694 JS_ReportOutOfMemory(cx);
7695 return false;
7697 for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) {
7698 // OK to do infallible append here, since we ensured capacity already.
7699 ${elemType}& slot = *${declName}.AppendElement(${elementInitializer});
7702 ).substitute(replacer)
7704 val = string.Template("args[variadicArg]").substitute(replacer)
7705 variadicConversion += indent(
7706 string.Template(typeConversion.template).substitute(
7708 "val": val,
7709 "maybeMutableVal": val,
7710 "declName": "slot",
7711 # We only need holderName here to handle isExternal()
7712 # interfaces, which use an internal holder for the
7713 # conversion even when forceOwningType ends up true.
7714 "holderName": "tempHolder",
7715 # Use the same ${obj} as for the variadic arg itself
7716 "obj": replacer["obj"],
7717 "passedToJSImpl": toStringBool(
7718 isJSImplementedDescriptor(self.descriptorProvider)
7725 variadicConversion += " }\n" "}\n"
7726 return variadicConversion
7729 def getMaybeWrapValueFuncForType(type):
7730 if type.isJSString():
7731 return "MaybeWrapStringValue"
7732 # Callbacks might actually be DOM objects; nothing prevents a page from
7733 # doing that.
7734 if type.isCallback() or type.isCallbackInterface() or type.isObject():
7735 if type.nullable():
7736 return "MaybeWrapObjectOrNullValue"
7737 return "MaybeWrapObjectValue"
7738 # SpiderMonkey interfaces are never DOM objects. Neither are sequences or
7739 # dictionaries, since those are always plain JS objects.
7740 if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence():
7741 if type.nullable():
7742 return "MaybeWrapNonDOMObjectOrNullValue"
7743 return "MaybeWrapNonDOMObjectValue"
7744 if type.isAny():
7745 return "MaybeWrapValue"
7747 # For other types, just go ahead an fall back on MaybeWrapValue for now:
7748 # it's always safe to do, and shouldn't be particularly slow for any of
7749 # them
7750 return "MaybeWrapValue"
7753 sequenceWrapLevel = 0
7754 recordWrapLevel = 0
7757 def getWrapTemplateForType(
7758 type,
7759 descriptorProvider,
7760 result,
7761 successCode,
7762 returnsNewObject,
7763 exceptionCode,
7764 spiderMonkeyInterfacesAreStructs,
7765 isConstructorRetval=False,
7768 Reflect a C++ value stored in "result", of IDL type "type" into JS. The
7769 "successCode" is the code to run once we have successfully done the
7770 conversion and must guarantee that execution of the conversion template
7771 stops once the successCode has executed (e.g. by doing a 'return', or by
7772 doing a 'break' if the entire conversion template is inside a block that
7773 the 'break' will exit).
7775 If spiderMonkeyInterfacesAreStructs is true, then if the type is a
7776 SpiderMonkey interface, "result" is one of the
7777 dom::SpiderMonkeyInterfaceObjectStorage subclasses, not a JSObject*.
7779 The resulting string should be used with string.Template. It
7780 needs the following keys when substituting:
7782 jsvalHandle: something that can be passed to methods taking a
7783 JS::MutableHandle<JS::Value>. This can be a
7784 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
7785 jsvalRef: something that can have .address() called on it to get a
7786 JS::Value* and .set() called on it to set it to a JS::Value.
7787 This can be a JS::MutableHandle<JS::Value> or a
7788 JS::Rooted<JS::Value>.
7789 obj: a JS::Handle<JSObject*>.
7791 Returns (templateString, infallibility of conversion template)
7793 if successCode is None:
7794 successCode = "return true;\n"
7796 def setUndefined():
7797 return _setValue("", setter="setUndefined")
7799 def setNull():
7800 return _setValue("", setter="setNull")
7802 def setInt32(value):
7803 return _setValue(value, setter="setInt32")
7805 def setString(value):
7806 return _setValue(value, wrapAsType=type, setter="setString")
7808 def setObject(value, wrapAsType=None):
7809 return _setValue(value, wrapAsType=wrapAsType, setter="setObject")
7811 def setObjectOrNull(value, wrapAsType=None):
7812 return _setValue(value, wrapAsType=wrapAsType, setter="setObjectOrNull")
7814 def setUint32(value):
7815 return _setValue(value, setter="setNumber")
7817 def setDouble(value):
7818 return _setValue("JS_NumberValue(%s)" % value)
7820 def setBoolean(value):
7821 return _setValue(value, setter="setBoolean")
7823 def _setValue(value, wrapAsType=None, setter="set"):
7825 Returns the code to set the jsval to value.
7827 If wrapAsType is not None, then will wrap the resulting value using the
7828 function that getMaybeWrapValueFuncForType(wrapAsType) returns.
7829 Otherwise, no wrapping will be done.
7831 if wrapAsType is None:
7832 tail = successCode
7833 else:
7834 tail = fill(
7836 if (!${maybeWrap}(cx, $${jsvalHandle})) {
7837 $*{exceptionCode}
7839 $*{successCode}
7840 """,
7841 maybeWrap=getMaybeWrapValueFuncForType(wrapAsType),
7842 exceptionCode=exceptionCode,
7843 successCode=successCode,
7845 return ("${jsvalRef}.%s(%s);\n" % (setter, value)) + tail
7847 def wrapAndSetPtr(wrapCall, failureCode=None):
7849 Returns the code to set the jsval by calling "wrapCall". "failureCode"
7850 is the code to run if calling "wrapCall" fails
7852 if failureCode is None:
7853 failureCode = exceptionCode
7854 return fill(
7856 if (!${wrapCall}) {
7857 $*{failureCode}
7859 $*{successCode}
7860 """,
7861 wrapCall=wrapCall,
7862 failureCode=failureCode,
7863 successCode=successCode,
7866 if type is None or type.isUndefined():
7867 return (setUndefined(), True)
7869 if (type.isSequence() or type.isRecord()) and type.nullable():
7870 # These are both wrapped in Nullable<>
7871 recTemplate, recInfall = getWrapTemplateForType(
7872 type.inner,
7873 descriptorProvider,
7874 "%s.Value()" % result,
7875 successCode,
7876 returnsNewObject,
7877 exceptionCode,
7878 spiderMonkeyInterfacesAreStructs,
7880 code = fill(
7883 if (${result}.IsNull()) {
7884 $*{setNull}
7886 $*{recTemplate}
7887 """,
7888 result=result,
7889 setNull=setNull(),
7890 recTemplate=recTemplate,
7892 return code, recInfall
7894 if type.isSequence():
7895 # Now do non-nullable sequences. Our success code is just to break to
7896 # where we set the element in the array. Note that we bump the
7897 # sequenceWrapLevel around this call so that nested sequence conversions
7898 # will use different iteration variables.
7899 global sequenceWrapLevel
7900 index = "sequenceIdx%d" % sequenceWrapLevel
7901 sequenceWrapLevel += 1
7902 innerTemplate = wrapForType(
7903 type.inner,
7904 descriptorProvider,
7906 "result": "%s[%s]" % (result, index),
7907 "successCode": "break;\n",
7908 "jsvalRef": "tmp",
7909 "jsvalHandle": "&tmp",
7910 "returnsNewObject": returnsNewObject,
7911 "exceptionCode": exceptionCode,
7912 "obj": "returnArray",
7913 "spiderMonkeyInterfacesAreStructs": spiderMonkeyInterfacesAreStructs,
7916 sequenceWrapLevel -= 1
7917 code = fill(
7920 uint32_t length = ${result}.Length();
7921 JS::Rooted<JSObject*> returnArray(cx, JS::NewArrayObject(cx, length));
7922 if (!returnArray) {
7923 $*{exceptionCode}
7925 // Scope for 'tmp'
7927 JS::Rooted<JS::Value> tmp(cx);
7928 for (uint32_t ${index} = 0; ${index} < length; ++${index}) {
7929 // Control block to let us common up the JS_DefineElement calls when there
7930 // are different ways to succeed at wrapping the object.
7931 do {
7932 $*{innerTemplate}
7933 } while (false);
7934 if (!JS_DefineElement(cx, returnArray, ${index}, tmp,
7935 JSPROP_ENUMERATE)) {
7936 $*{exceptionCode}
7940 $*{set}
7941 """,
7942 result=result,
7943 exceptionCode=exceptionCode,
7944 index=index,
7945 innerTemplate=innerTemplate,
7946 set=setObject("*returnArray"),
7949 return (code, False)
7951 if type.isRecord():
7952 # Now do non-nullable record. Our success code is just to break to
7953 # where we define the property on the object. Note that we bump the
7954 # recordWrapLevel around this call so that nested record conversions
7955 # will use different temp value names.
7956 global recordWrapLevel
7957 valueName = "recordValue%d" % recordWrapLevel
7958 recordWrapLevel += 1
7959 innerTemplate = wrapForType(
7960 type.inner,
7961 descriptorProvider,
7963 "result": valueName,
7964 "successCode": "break;\n",
7965 "jsvalRef": "tmp",
7966 "jsvalHandle": "&tmp",
7967 "returnsNewObject": returnsNewObject,
7968 "exceptionCode": exceptionCode,
7969 "obj": "returnObj",
7970 "spiderMonkeyInterfacesAreStructs": spiderMonkeyInterfacesAreStructs,
7973 recordWrapLevel -= 1
7974 if type.keyType.isByteString():
7975 # There is no length-taking JS_DefineProperty. So to keep
7976 # things sane with embedded nulls, we want to byte-inflate
7977 # to an nsAString. The only byte-inflation function we
7978 # have around is AppendASCIItoUTF16, which luckily doesn't
7979 # assert anything about the input being ASCII.
7980 expandedKeyDecl = "NS_ConvertASCIItoUTF16 expandedKey(entry.mKey);\n"
7981 keyName = "expandedKey"
7982 elif type.keyType.isUTF8String():
7983 # We do the same as above for utf8 strings. We could do better if
7984 # we had a DefineProperty API that takes utf-8 property names.
7985 expandedKeyDecl = "NS_ConvertUTF8toUTF16 expandedKey(entry.mKey);\n"
7986 keyName = "expandedKey"
7987 else:
7988 expandedKeyDecl = ""
7989 keyName = "entry.mKey"
7991 code = fill(
7994 JS::Rooted<JSObject*> returnObj(cx, JS_NewPlainObject(cx));
7995 if (!returnObj) {
7996 $*{exceptionCode}
7998 // Scope for 'tmp'
8000 JS::Rooted<JS::Value> tmp(cx);
8001 for (auto& entry : ${result}.Entries()) {
8002 auto& ${valueName} = entry.mValue;
8003 // Control block to let us common up the JS_DefineUCProperty calls when there
8004 // are different ways to succeed at wrapping the value.
8005 do {
8006 $*{innerTemplate}
8007 } while (false);
8008 $*{expandedKeyDecl}
8009 if (!JS_DefineUCProperty(cx, returnObj,
8010 ${keyName}.BeginReading(),
8011 ${keyName}.Length(), tmp,
8012 JSPROP_ENUMERATE)) {
8013 $*{exceptionCode}
8017 $*{set}
8018 """,
8019 result=result,
8020 exceptionCode=exceptionCode,
8021 valueName=valueName,
8022 innerTemplate=innerTemplate,
8023 expandedKeyDecl=expandedKeyDecl,
8024 keyName=keyName,
8025 set=setObject("*returnObj"),
8028 return (code, False)
8030 if type.isPromise():
8031 assert not type.nullable()
8032 # The use of ToJSValue here is a bit annoying because the Promise
8033 # version is not inlined. But we can't put an inline version in either
8034 # ToJSValue.h or BindingUtils.h, because Promise.h includes ToJSValue.h
8035 # and that includes BindingUtils.h, so we'd get an include loop if
8036 # either of those headers included Promise.h. And trying to write the
8037 # conversion by hand here is pretty annoying because we have to handle
8038 # the various RefPtr, rawptr, NonNull, etc cases, which ToJSValue will
8039 # handle for us. So just eat the cost of the function call.
8040 return (wrapAndSetPtr("ToJSValue(cx, %s, ${jsvalHandle})" % result), False)
8042 if type.isGeckoInterface() and not type.isCallbackInterface():
8043 descriptor = descriptorProvider.getDescriptor(
8044 type.unroll().inner.identifier.name
8046 if type.nullable():
8047 if descriptor.interface.identifier.name == "WindowProxy":
8048 template, infal = getWrapTemplateForType(
8049 type.inner,
8050 descriptorProvider,
8051 "%s.Value()" % result,
8052 successCode,
8053 returnsNewObject,
8054 exceptionCode,
8055 spiderMonkeyInterfacesAreStructs,
8057 return (
8058 "if (%s.IsNull()) {\n" % result
8059 + indent(setNull())
8060 + "}\n"
8061 + template,
8062 infal,
8065 wrappingCode = "if (!%s) {\n" % (result) + indent(setNull()) + "}\n"
8066 else:
8067 wrappingCode = ""
8069 if not descriptor.interface.isExternal():
8070 if descriptor.wrapperCache:
8071 wrapMethod = "GetOrCreateDOMReflector"
8072 wrapArgs = "cx, %s, ${jsvalHandle}" % result
8073 else:
8074 wrapMethod = "WrapNewBindingNonWrapperCachedObject"
8075 wrapArgs = "cx, ${obj}, %s, ${jsvalHandle}" % result
8076 if isConstructorRetval:
8077 wrapArgs += ", desiredProto"
8078 wrap = "%s(%s)" % (wrapMethod, wrapArgs)
8079 # Can only fail to wrap as a new-binding object if they already
8080 # threw an exception.
8081 failed = "MOZ_ASSERT(JS_IsExceptionPending(cx));\n" + exceptionCode
8082 else:
8083 if descriptor.notflattened:
8084 getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
8085 else:
8086 getIID = ""
8087 wrap = "WrapObject(cx, %s, %s${jsvalHandle})" % (result, getIID)
8088 failed = None
8090 wrappingCode += wrapAndSetPtr(wrap, failed)
8091 return (wrappingCode, False)
8093 if type.isJSString():
8094 return (setString(result), False)
8096 if type.isDOMString() or type.isUSVString():
8097 if type.nullable():
8098 return (
8099 wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result),
8100 False,
8102 else:
8103 return (
8104 wrapAndSetPtr(
8105 "xpc::NonVoidStringToJsval(cx, %s, ${jsvalHandle})" % result
8107 False,
8110 if type.isByteString():
8111 if type.nullable():
8112 return (
8113 wrapAndSetPtr("ByteStringToJsval(cx, %s, ${jsvalHandle})" % result),
8114 False,
8116 else:
8117 return (
8118 wrapAndSetPtr(
8119 "NonVoidByteStringToJsval(cx, %s, ${jsvalHandle})" % result
8121 False,
8124 if type.isUTF8String():
8125 if type.nullable():
8126 return (
8127 wrapAndSetPtr("UTF8StringToJsval(cx, %s, ${jsvalHandle})" % result),
8128 False,
8130 else:
8131 return (
8132 wrapAndSetPtr(
8133 "NonVoidUTF8StringToJsval(cx, %s, ${jsvalHandle})" % result
8135 False,
8138 if type.isEnum():
8139 if type.nullable():
8140 resultLoc = "%s.Value()" % result
8141 else:
8142 resultLoc = result
8143 conversion = fill(
8145 if (!ToJSValue(cx, ${result}, $${jsvalHandle})) {
8146 $*{exceptionCode}
8148 $*{successCode}
8149 """,
8150 result=resultLoc,
8151 exceptionCode=exceptionCode,
8152 successCode=successCode,
8155 if type.nullable():
8156 conversion = CGIfElseWrapper(
8157 "%s.IsNull()" % result, CGGeneric(setNull()), CGGeneric(conversion)
8158 ).define()
8159 return conversion, False
8161 if type.isCallback() or type.isCallbackInterface():
8162 # Callbacks can store null if we nuked the compartments their
8163 # objects lived in.
8164 wrapCode = setObjectOrNull(
8165 "GetCallbackFromCallbackObject(cx, %(result)s)", wrapAsType=type
8167 if type.nullable():
8168 wrapCode = (
8169 "if (%(result)s) {\n"
8170 + indent(wrapCode)
8171 + "} else {\n"
8172 + indent(setNull())
8173 + "}\n"
8175 wrapCode = wrapCode % {"result": result}
8176 return wrapCode, False
8178 if type.isAny():
8179 # See comments in GetOrCreateDOMReflector explaining why we need
8180 # to wrap here.
8181 # NB: _setValue(..., type-that-is-any) calls JS_WrapValue(), so is fallible
8182 head = "JS::ExposeValueToActiveJS(%s);\n" % result
8183 return (head + _setValue(result, wrapAsType=type), False)
8185 if type.isObject() or (
8186 type.isSpiderMonkeyInterface() and not spiderMonkeyInterfacesAreStructs
8188 # See comments in GetOrCreateDOMReflector explaining why we need
8189 # to wrap here.
8190 if type.nullable():
8191 toValue = "%s"
8192 setter = setObjectOrNull
8193 head = """if (%s) {
8194 JS::ExposeObjectToActiveJS(%s);
8196 """ % (
8197 result,
8198 result,
8200 else:
8201 toValue = "*%s"
8202 setter = setObject
8203 head = "JS::ExposeObjectToActiveJS(%s);\n" % result
8204 # NB: setObject{,OrNull}(..., some-object-type) calls JS_WrapValue(), so is fallible
8205 return (head + setter(toValue % result, wrapAsType=type), False)
8207 if type.isObservableArray():
8208 # This first argument isn't used at all for now, the attribute getter
8209 # for ObservableArray type are generated in getObservableArrayGetterBody
8210 # instead.
8211 return "", False
8213 if not (
8214 type.isUnion()
8215 or type.isPrimitive()
8216 or type.isDictionary()
8217 or (type.isSpiderMonkeyInterface() and spiderMonkeyInterfacesAreStructs)
8219 raise TypeError("Need to learn to wrap %s" % type)
8221 if type.nullable():
8222 recTemplate, recInfal = getWrapTemplateForType(
8223 type.inner,
8224 descriptorProvider,
8225 "%s.Value()" % result,
8226 successCode,
8227 returnsNewObject,
8228 exceptionCode,
8229 spiderMonkeyInterfacesAreStructs,
8231 return (
8232 "if (%s.IsNull()) {\n" % result + indent(setNull()) + "}\n" + recTemplate,
8233 recInfal,
8236 if type.isSpiderMonkeyInterface():
8237 assert spiderMonkeyInterfacesAreStructs
8238 # See comments in GetOrCreateDOMReflector explaining why we need
8239 # to wrap here.
8240 # NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible
8241 return (setObject("*%s.Obj()" % result, wrapAsType=type), False)
8243 if type.isUnion():
8244 return (wrapAndSetPtr("%s.ToJSVal(cx, ${obj}, ${jsvalHandle})" % result), False)
8246 if type.isDictionary():
8247 return (
8248 wrapAndSetPtr("%s.ToObjectInternal(cx, ${jsvalHandle})" % result),
8249 False,
8252 tag = type.tag()
8254 if tag in [
8255 IDLType.Tags.int8,
8256 IDLType.Tags.uint8,
8257 IDLType.Tags.int16,
8258 IDLType.Tags.uint16,
8259 IDLType.Tags.int32,
8261 return (setInt32("int32_t(%s)" % result), True)
8263 elif tag in [
8264 IDLType.Tags.int64,
8265 IDLType.Tags.uint64,
8266 IDLType.Tags.unrestricted_float,
8267 IDLType.Tags.float,
8268 IDLType.Tags.unrestricted_double,
8269 IDLType.Tags.double,
8271 # XXXbz will cast to double do the "even significand" thing that webidl
8272 # calls for for 64-bit ints? Do we care?
8273 return (setDouble("double(%s)" % result), True)
8275 elif tag == IDLType.Tags.uint32:
8276 return (setUint32(result), True)
8278 elif tag == IDLType.Tags.bool:
8279 return (setBoolean(result), True)
8281 else:
8282 raise TypeError("Need to learn to wrap primitive: %s" % type)
8285 def wrapForType(type, descriptorProvider, templateValues):
8287 Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict
8288 that should contain:
8290 * 'jsvalRef': something that can have .address() called on it to get a
8291 JS::Value* and .set() called on it to set it to a JS::Value.
8292 This can be a JS::MutableHandle<JS::Value> or a
8293 JS::Rooted<JS::Value>.
8294 * 'jsvalHandle': something that can be passed to methods taking a
8295 JS::MutableHandle<JS::Value>. This can be a
8296 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
8297 * 'obj' (optional): the name of the variable that contains the JSObject to
8298 use as a scope when wrapping, if not supplied 'obj'
8299 will be used as the name
8300 * 'result' (optional): the name of the variable in which the C++ value is
8301 stored, if not supplied 'result' will be used as
8302 the name
8303 * 'successCode' (optional): the code to run once we have successfully
8304 done the conversion, if not supplied 'return
8305 true;' will be used as the code. The
8306 successCode must ensure that once it runs no
8307 more of the conversion template will be
8308 executed (e.g. by doing a 'return' or 'break'
8309 as appropriate).
8310 * 'returnsNewObject' (optional): If true, we're wrapping for the return
8311 value of a [NewObject] method. Assumed
8312 false if not set.
8313 * 'exceptionCode' (optional): Code to run when a JS exception is thrown.
8314 The default is "return false;". The code
8315 passed here must return.
8316 * 'isConstructorRetval' (optional): If true, we're wrapping a constructor
8317 return value.
8319 wrap = getWrapTemplateForType(
8320 type,
8321 descriptorProvider,
8322 templateValues.get("result", "result"),
8323 templateValues.get("successCode", None),
8324 templateValues.get("returnsNewObject", False),
8325 templateValues.get("exceptionCode", "return false;\n"),
8326 templateValues.get("spiderMonkeyInterfacesAreStructs", False),
8327 isConstructorRetval=templateValues.get("isConstructorRetval", False),
8328 )[0]
8330 defaultValues = {"obj": "obj"}
8331 return string.Template(wrap).substitute(defaultValues, **templateValues)
8334 def infallibleForMember(member, type, descriptorProvider):
8336 Determine the fallibility of changing a C++ value of IDL type "type" into
8337 JS for the given attribute. Apart from returnsNewObject, all the defaults
8338 are used, since the fallbility does not change based on the boolean values,
8339 and the template will be discarded.
8341 CURRENT ASSUMPTIONS:
8342 We assume that successCode for wrapping up return values cannot contain
8343 failure conditions.
8345 return getWrapTemplateForType(
8346 type,
8347 descriptorProvider,
8348 "result",
8349 None,
8350 memberReturnsNewObject(member),
8351 "return false;\n",
8352 False,
8353 )[1]
8356 def leafTypeNeedsCx(type, retVal):
8357 return (
8358 type.isAny()
8359 or type.isObject()
8360 or type.isJSString()
8361 or (retVal and type.isSpiderMonkeyInterface())
8365 def leafTypeNeedsScopeObject(type, retVal):
8366 return retVal and type.isSpiderMonkeyInterface()
8369 def leafTypeNeedsRooting(type):
8370 return leafTypeNeedsCx(type, False) or type.isSpiderMonkeyInterface()
8373 def typeNeedsRooting(type):
8374 return typeMatchesLambda(type, lambda t: leafTypeNeedsRooting(t))
8377 def typeNeedsCx(type, retVal=False):
8378 return typeMatchesLambda(type, lambda t: leafTypeNeedsCx(t, retVal))
8381 def typeNeedsScopeObject(type, retVal=False):
8382 return typeMatchesLambda(type, lambda t: leafTypeNeedsScopeObject(t, retVal))
8385 def typeMatchesLambda(type, func):
8386 if type is None:
8387 return False
8388 if type.nullable():
8389 return typeMatchesLambda(type.inner, func)
8390 if type.isSequence() or type.isRecord():
8391 return typeMatchesLambda(type.inner, func)
8392 if type.isUnion():
8393 return any(typeMatchesLambda(t, func) for t in type.unroll().flatMemberTypes)
8394 if type.isDictionary():
8395 return dictionaryMatchesLambda(type.inner, func)
8396 return func(type)
8399 def dictionaryMatchesLambda(dictionary, func):
8400 return any(typeMatchesLambda(m.type, func) for m in dictionary.members) or (
8401 dictionary.parent and dictionaryMatchesLambda(dictionary.parent, func)
8405 # Whenever this is modified, please update CGNativeMember.getRetvalInfo as
8406 # needed to keep the types compatible.
8407 def getRetvalDeclarationForType(returnType, descriptorProvider, isMember=False):
8409 Returns a tuple containing five things:
8411 1) A CGThing for the type of the return value, or None if there is no need
8412 for a return value.
8414 2) A value indicating the kind of ourparam to pass the value as. Valid
8415 options are None to not pass as an out param at all, "ref" (to pass a
8416 reference as an out param), and "ptr" (to pass a pointer as an out
8417 param).
8419 3) A CGThing for a tracer for the return value, or None if no tracing is
8420 needed.
8422 4) An argument string to pass to the retval declaration
8423 constructor or None if there are no arguments.
8425 5) The name of a function that needs to be called with the return value
8426 before using it, or None if no function needs to be called.
8428 if returnType is None or returnType.isUndefined():
8429 # Nothing to declare
8430 return None, None, None, None, None
8431 if returnType.isPrimitive() and returnType.tag() in builtinNames:
8432 result = CGGeneric(builtinNames[returnType.tag()])
8433 if returnType.nullable():
8434 result = CGTemplatedType("Nullable", result)
8435 return result, None, None, None, None
8436 if returnType.isJSString():
8437 if isMember:
8438 raise TypeError("JSString not supported as return type member")
8439 return CGGeneric("JS::Rooted<JSString*>"), "ptr", None, "cx", None
8440 if returnType.isDOMString() or returnType.isUSVString():
8441 if isMember:
8442 return CGGeneric("nsString"), "ref", None, None, None
8443 return CGGeneric("DOMString"), "ref", None, None, None
8444 if returnType.isByteString() or returnType.isUTF8String():
8445 if isMember:
8446 return CGGeneric("nsCString"), "ref", None, None, None
8447 return CGGeneric("nsAutoCString"), "ref", None, None, None
8448 if returnType.isEnum():
8449 result = CGGeneric(returnType.unroll().inner.identifier.name)
8450 if returnType.nullable():
8451 result = CGTemplatedType("Nullable", result)
8452 return result, None, None, None, None
8453 if returnType.isGeckoInterface() or returnType.isPromise():
8454 if returnType.isGeckoInterface():
8455 typeName = returnType.unroll().inner.identifier.name
8456 if typeName == "WindowProxy":
8457 result = CGGeneric("WindowProxyHolder")
8458 if returnType.nullable():
8459 result = CGTemplatedType("Nullable", result)
8460 return result, None, None, None, None
8462 typeName = descriptorProvider.getDescriptor(typeName).nativeType
8463 else:
8464 typeName = "Promise"
8465 if isMember:
8466 conversion = None
8467 result = CGGeneric("StrongPtrForMember<%s>" % typeName)
8468 else:
8469 conversion = CGGeneric("StrongOrRawPtr<%s>" % typeName)
8470 result = CGGeneric("auto")
8471 return result, None, None, None, conversion
8472 if returnType.isCallback():
8473 name = returnType.unroll().callback.identifier.name
8474 return CGGeneric("RefPtr<%s>" % name), None, None, None, None
8475 if returnType.isAny():
8476 if isMember:
8477 return CGGeneric("JS::Value"), None, None, None, None
8478 return CGGeneric("JS::Rooted<JS::Value>"), "ptr", None, "cx", None
8479 if returnType.isObject() or returnType.isSpiderMonkeyInterface():
8480 if isMember:
8481 return CGGeneric("JSObject*"), None, None, None, None
8482 return CGGeneric("JS::Rooted<JSObject*>"), "ptr", None, "cx", None
8483 if returnType.isSequence():
8484 nullable = returnType.nullable()
8485 if nullable:
8486 returnType = returnType.inner
8487 result, _, _, _, _ = getRetvalDeclarationForType(
8488 returnType.inner, descriptorProvider, isMember="Sequence"
8490 # While we have our inner type, set up our rooter, if needed
8491 if not isMember and typeNeedsRooting(returnType):
8492 rooter = CGGeneric(
8493 "SequenceRooter<%s > resultRooter(cx, &result);\n" % result.define()
8495 else:
8496 rooter = None
8497 result = CGTemplatedType("nsTArray", result)
8498 if nullable:
8499 result = CGTemplatedType("Nullable", result)
8500 return result, "ref", rooter, None, None
8501 if returnType.isRecord():
8502 nullable = returnType.nullable()
8503 if nullable:
8504 returnType = returnType.inner
8505 result, _, _, _, _ = getRetvalDeclarationForType(
8506 returnType.inner, descriptorProvider, isMember="Record"
8508 # While we have our inner type, set up our rooter, if needed
8509 if not isMember and typeNeedsRooting(returnType):
8510 rooter = CGGeneric(
8511 "RecordRooter<%s> resultRooter(cx, &result);\n"
8512 % ("nsString, " + result.define())
8514 else:
8515 rooter = None
8516 result = CGTemplatedType("Record", [recordKeyDeclType(returnType), result])
8517 if nullable:
8518 result = CGTemplatedType("Nullable", result)
8519 return result, "ref", rooter, None, None
8520 if returnType.isDictionary():
8521 nullable = returnType.nullable()
8522 dictName = CGDictionary.makeDictionaryName(returnType.unroll().inner)
8523 result = CGGeneric(dictName)
8524 if not isMember and typeNeedsRooting(returnType):
8525 if nullable:
8526 result = CGTemplatedType("NullableRootedDictionary", result)
8527 else:
8528 result = CGTemplatedType("RootedDictionary", result)
8529 resultArgs = "cx"
8530 else:
8531 if nullable:
8532 result = CGTemplatedType("Nullable", result)
8533 resultArgs = None
8534 return result, "ref", None, resultArgs, None
8535 if returnType.isUnion():
8536 result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True))
8537 if not isMember and typeNeedsRooting(returnType):
8538 if returnType.nullable():
8539 result = CGTemplatedType("NullableRootedUnion", result)
8540 else:
8541 result = CGTemplatedType("RootedUnion", result)
8542 resultArgs = "cx"
8543 else:
8544 if returnType.nullable():
8545 result = CGTemplatedType("Nullable", result)
8546 resultArgs = None
8547 return result, "ref", None, resultArgs, None
8548 raise TypeError("Don't know how to declare return value for %s" % returnType)
8551 def needCx(returnType, arguments, extendedAttributes, considerTypes, static=False):
8552 return (
8553 not static
8554 and considerTypes
8555 and (
8556 typeNeedsCx(returnType, True) or any(typeNeedsCx(a.type) for a in arguments)
8558 or "implicitJSContext" in extendedAttributes
8562 def needScopeObject(
8563 returnType, arguments, extendedAttributes, isWrapperCached, considerTypes, isMember
8566 isMember should be true if we're dealing with an attribute
8567 annotated as [StoreInSlot].
8569 return (
8570 considerTypes
8571 and not isWrapperCached
8572 and (
8573 (not isMember and typeNeedsScopeObject(returnType, True))
8574 or any(typeNeedsScopeObject(a.type) for a in arguments)
8579 def callerTypeGetterForDescriptor(descriptor):
8580 if descriptor.interface.isExposedInAnyWorker():
8581 systemCallerGetter = "nsContentUtils::ThreadsafeIsSystemCaller"
8582 else:
8583 systemCallerGetter = "nsContentUtils::IsSystemCaller"
8584 return "%s(cx) ? CallerType::System : CallerType::NonSystem" % systemCallerGetter
8587 class CGCallGenerator(CGThing):
8589 A class to generate an actual call to a C++ object. Assumes that the C++
8590 object is stored in a variable whose name is given by the |object| argument.
8592 needsCallerType is a boolean indicating whether the call should receive
8593 a PrincipalType for the caller.
8595 needsErrorResult is a boolean indicating whether the call should be
8596 fallible and thus needs ErrorResult parameter.
8598 resultVar: If the returnType is not void, then the result of the call is
8599 stored in a C++ variable named by resultVar. The caller is responsible for
8600 declaring the result variable. If the caller doesn't care about the result
8601 value, resultVar can be omitted.
8603 context: The context string to pass to MaybeSetPendingException.
8606 def __init__(
8607 self,
8608 needsErrorResult,
8609 needsCallerType,
8610 isChromeOnly,
8611 arguments,
8612 argsPre,
8613 returnType,
8614 extendedAttributes,
8615 descriptor,
8616 nativeMethodName,
8617 static,
8618 object="self",
8619 argsPost=[],
8620 resultVar=None,
8621 context="nullptr",
8623 CGThing.__init__(self)
8626 result,
8627 resultOutParam,
8628 resultRooter,
8629 resultArgs,
8630 resultConversion,
8631 ) = getRetvalDeclarationForType(returnType, descriptor)
8633 args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
8634 for a, name in arguments:
8635 arg = CGGeneric(name)
8637 # Now constify the things that need it
8638 def needsConst(a):
8639 if a.type.isDictionary():
8640 return True
8641 if a.type.isSequence():
8642 return True
8643 if a.type.isRecord():
8644 return True
8645 # isObject() types are always a JS::Rooted, whether
8646 # nullable or not, and it turns out a const JS::Rooted
8647 # is not very helpful at all (in particular, it won't
8648 # even convert to a JS::Handle).
8649 # XXX bz Well, why not???
8650 if a.type.nullable() and not a.type.isObject():
8651 return True
8652 if a.type.isString():
8653 return True
8654 if a.canHaveMissingValue():
8655 # This will need an Optional or it's a variadic;
8656 # in both cases it should be const.
8657 return True
8658 if a.type.isUnion():
8659 return True
8660 if a.type.isSpiderMonkeyInterface():
8661 return True
8662 return False
8664 if needsConst(a):
8665 arg = CGWrapper(arg, pre="Constify(", post=")")
8666 # And convert NonNull<T> to T&
8667 if (
8668 (a.type.isGeckoInterface() or a.type.isCallback() or a.type.isPromise())
8669 and not a.type.nullable()
8670 ) or a.type.isDOMString():
8671 arg = CGWrapper(arg, pre="NonNullHelper(", post=")")
8673 # If it's a refcounted object, let the static analysis know it's
8674 # alive for the duration of the call.
8675 if a.type.isGeckoInterface() or a.type.isCallback():
8676 arg = CGWrapper(arg, pre="MOZ_KnownLive(", post=")")
8678 args.append(arg)
8680 needResultDecl = False
8682 # Build up our actual call
8683 self.cgRoot = CGList([])
8685 # Return values that go in outparams go here
8686 if resultOutParam is not None:
8687 if resultVar is None:
8688 needResultDecl = True
8689 resultVar = "result"
8690 if resultOutParam == "ref":
8691 args.append(CGGeneric(resultVar))
8692 else:
8693 assert resultOutParam == "ptr"
8694 args.append(CGGeneric("&" + resultVar))
8696 needsSubjectPrincipal = "needsSubjectPrincipal" in extendedAttributes
8697 if needsSubjectPrincipal:
8698 needsNonSystemPrincipal = (
8699 "needsNonSystemSubjectPrincipal" in extendedAttributes
8701 if needsNonSystemPrincipal:
8702 principalType = "nsIPrincipal*"
8703 subjectPrincipalArg = "subjectPrincipal"
8704 checkPrincipal = dedent(
8706 if (principal->IsSystemPrincipal()) {
8707 principal = nullptr;
8711 else:
8712 principalType = "NonNull<nsIPrincipal>"
8713 subjectPrincipalArg = "NonNullHelper(subjectPrincipal)"
8714 checkPrincipal = ""
8716 self.cgRoot.append(
8717 CGGeneric(
8718 fill(
8720 ${principalType} subjectPrincipal;
8722 JS::Realm* realm = js::GetContextRealm(cx);
8723 MOZ_ASSERT(realm);
8724 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
8725 nsIPrincipal* principal = nsJSPrincipals::get(principals);
8726 ${checkPrincipal}
8727 subjectPrincipal = principal;
8729 """,
8730 principalType=principalType,
8731 checkPrincipal=checkPrincipal,
8736 args.append(CGGeneric("MOZ_KnownLive(%s)" % subjectPrincipalArg))
8738 if needsCallerType:
8739 if isChromeOnly:
8740 args.append(CGGeneric("SystemCallerGuarantee()"))
8741 else:
8742 args.append(CGGeneric(callerTypeGetterForDescriptor(descriptor)))
8744 canOOM = "canOOM" in extendedAttributes
8745 if needsErrorResult:
8746 args.append(CGGeneric("rv"))
8747 elif canOOM:
8748 args.append(CGGeneric("OOMReporter::From(rv)"))
8749 args.extend(CGGeneric(arg) for arg in argsPost)
8751 call = CGGeneric(nativeMethodName)
8752 if not static:
8753 call = CGWrapper(call, pre="%s->" % object)
8754 call = CGList([call, CGWrapper(args, pre="(", post=")")])
8755 if returnType is None or returnType.isUndefined() or resultOutParam is not None:
8756 assert resultConversion is None
8757 call = CGList(
8759 CGWrapper(
8760 call,
8761 pre=(
8762 "// NOTE: This assert does NOT call the function.\n"
8763 "static_assert(std::is_void_v<decltype("
8765 post=')>, "Should be returning void here");',
8767 call,
8769 "\n",
8771 elif resultConversion is not None:
8772 call = CGList([resultConversion, CGWrapper(call, pre="(", post=")")])
8773 if resultVar is None and result is not None:
8774 needResultDecl = True
8775 resultVar = "result"
8777 if needResultDecl:
8778 if resultArgs is not None:
8779 resultArgsStr = "(%s)" % resultArgs
8780 else:
8781 resultArgsStr = ""
8782 result = CGWrapper(result, post=(" %s%s" % (resultVar, resultArgsStr)))
8783 if resultOutParam is None and resultArgs is None:
8784 call = CGList([result, CGWrapper(call, pre="(", post=")")])
8785 else:
8786 self.cgRoot.append(CGWrapper(result, post=";\n"))
8787 if resultOutParam is None:
8788 call = CGWrapper(call, pre=resultVar + " = ")
8789 if resultRooter is not None:
8790 self.cgRoot.append(resultRooter)
8791 elif result is not None:
8792 assert resultOutParam is None
8793 call = CGWrapper(call, pre=resultVar + " = ")
8795 call = CGWrapper(call, post=";\n")
8796 self.cgRoot.append(call)
8798 if needsErrorResult or canOOM:
8799 self.cgRoot.prepend(CGGeneric("FastErrorResult rv;\n"))
8800 self.cgRoot.append(
8801 CGGeneric(
8802 fill(
8804 if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx, ${context}))) {
8805 return false;
8807 """,
8808 context=context,
8813 self.cgRoot.append(CGGeneric("MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"))
8815 def define(self):
8816 return self.cgRoot.define()
8819 def getUnionMemberName(type):
8820 # Promises can't be in unions, because they're not distinguishable
8821 # from anything else.
8822 assert not type.isPromise()
8823 if type.isGeckoInterface():
8824 return type.inner.identifier.name
8825 if type.isEnum():
8826 return type.inner.identifier.name
8827 return type.name
8830 # A counter for making sure that when we're wrapping up things in
8831 # nested sequences we don't use the same variable name to iterate over
8832 # different sequences.
8833 sequenceWrapLevel = 0
8834 recordWrapLevel = 0
8837 def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
8839 Take the thing named by "value" and if it contains "any",
8840 "object", or spidermonkey-interface types inside return a CGThing
8841 that will wrap them into the current compartment.
8843 if type.isAny():
8844 assert not type.nullable()
8845 if isMember:
8846 value = "JS::MutableHandle<JS::Value>::fromMarkedLocation(&%s)" % value
8847 else:
8848 value = "&" + value
8849 return CGGeneric(
8850 "if (!JS_WrapValue(cx, %s)) {\n" " return false;\n" "}\n" % value
8853 if type.isObject():
8854 if isMember:
8855 value = "JS::MutableHandle<JSObject*>::fromMarkedLocation(&%s)" % value
8856 else:
8857 value = "&" + value
8858 return CGGeneric(
8859 "if (!JS_WrapObject(cx, %s)) {\n" " return false;\n" "}\n" % value
8862 if type.isSpiderMonkeyInterface():
8863 origValue = value
8864 if type.nullable():
8865 value = "%s.Value()" % value
8866 wrapCode = CGGeneric(
8867 "if (!%s.WrapIntoNewCompartment(cx)) {\n" " return false;\n" "}\n" % value
8869 if type.nullable():
8870 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
8871 return wrapCode
8873 if type.isSequence():
8874 origValue = value
8875 origType = type
8876 if type.nullable():
8877 type = type.inner
8878 value = "%s.Value()" % value
8879 global sequenceWrapLevel
8880 index = "indexName%d" % sequenceWrapLevel
8881 sequenceWrapLevel += 1
8882 wrapElement = wrapTypeIntoCurrentCompartment(
8883 type.inner, "%s[%s]" % (value, index)
8885 sequenceWrapLevel -= 1
8886 if not wrapElement:
8887 return None
8888 wrapCode = CGWrapper(
8889 CGIndenter(wrapElement),
8890 pre=(
8891 "for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n"
8892 % (index, index, value, index)
8894 post="}\n",
8896 if origType.nullable():
8897 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
8898 return wrapCode
8900 if type.isRecord():
8901 origType = type
8902 if type.nullable():
8903 type = type.inner
8904 recordRef = "%s.Value()" % value
8905 else:
8906 recordRef = value
8907 global recordWrapLevel
8908 entryRef = "mapEntry%d" % recordWrapLevel
8909 recordWrapLevel += 1
8910 wrapElement = wrapTypeIntoCurrentCompartment(type.inner, "%s.mValue" % entryRef)
8911 recordWrapLevel -= 1
8912 if not wrapElement:
8913 return None
8914 wrapCode = CGWrapper(
8915 CGIndenter(wrapElement),
8916 pre=("for (auto& %s : %s.Entries()) {\n" % (entryRef, recordRef)),
8917 post="}\n",
8919 if origType.nullable():
8920 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % value)
8921 return wrapCode
8923 if type.isDictionary():
8924 assert not type.nullable()
8925 myDict = type.inner
8926 memberWraps = []
8927 while myDict:
8928 for member in myDict.members:
8929 memberWrap = wrapArgIntoCurrentCompartment(
8930 member,
8931 "%s.%s"
8932 % (value, CGDictionary.makeMemberName(member.identifier.name)),
8934 if memberWrap:
8935 memberWraps.append(memberWrap)
8936 myDict = myDict.parent
8937 return CGList(memberWraps) if len(memberWraps) != 0 else None
8939 if type.isUnion():
8940 memberWraps = []
8941 if type.nullable():
8942 type = type.inner
8943 value = "%s.Value()" % value
8944 for member in type.flatMemberTypes:
8945 memberName = getUnionMemberName(member)
8946 memberWrap = wrapTypeIntoCurrentCompartment(
8947 member, "%s.GetAs%s()" % (value, memberName)
8949 if memberWrap:
8950 memberWrap = CGIfWrapper(memberWrap, "%s.Is%s()" % (value, memberName))
8951 memberWraps.append(memberWrap)
8952 return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None
8954 if (
8955 type.isUndefined()
8956 or type.isString()
8957 or type.isPrimitive()
8958 or type.isEnum()
8959 or type.isGeckoInterface()
8960 or type.isCallback()
8961 or type.isPromise()
8963 # All of these don't need wrapping.
8964 return None
8966 raise TypeError(
8967 "Unknown type; we don't know how to wrap it in constructor "
8968 "arguments: %s" % type
8972 def wrapArgIntoCurrentCompartment(arg, value, isMember=True):
8974 As wrapTypeIntoCurrentCompartment but handles things being optional
8976 origValue = value
8977 isOptional = arg.canHaveMissingValue()
8978 if isOptional:
8979 value = value + ".Value()"
8980 wrap = wrapTypeIntoCurrentCompartment(arg.type, value, isMember)
8981 if wrap and isOptional:
8982 wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue)
8983 return wrap
8986 def needsCallerType(m):
8987 return m.getExtendedAttribute("NeedsCallerType")
8990 class CGPerSignatureCall(CGThing):
8992 This class handles the guts of generating code for a particular
8993 call signature. A call signature consists of four things:
8995 1) A return type, which can be None to indicate that there is no
8996 actual return value (e.g. this is an attribute setter) or an
8997 IDLType if there's an IDL type involved (including |void|).
8998 2) An argument list, which is allowed to be empty.
8999 3) A name of a native method to call. It is ignored for methods
9000 annotated with the "[WebExtensionStub=...]" extended attribute.
9001 4) Whether or not this method is static. Note that this only controls how
9002 the method is called (|self->nativeMethodName(...)| vs
9003 |nativeMethodName(...)|).
9005 We also need to know whether this is a method or a getter/setter
9006 to do error reporting correctly.
9008 The idlNode parameter can be either a method or an attr. We can query
9009 |idlNode.identifier| in both cases, so we can be agnostic between the two.
9011 dontSetSlot should be set to True if the value should not be cached in a
9012 slot (even if the attribute is marked as StoreInSlot or Cached in the
9013 WebIDL).
9015 errorReportingLabel can contain a custom label to use for error reporting.
9016 It will be inserted as is in the code, so if it needs to be a literal
9017 string in C++ it should be quoted.
9019 additionalArgsPre contains additional arguments that are added after the
9020 arguments that CGPerSignatureCall itself adds (JSContext, global, …), and
9021 before the actual arguments.
9024 # XXXbz For now each entry in the argument list is either an
9025 # IDLArgument or a FakeArgument, but longer-term we may want to
9026 # have ways of flagging things like JSContext* or optional_argc in
9027 # there.
9029 def __init__(
9030 self,
9031 returnType,
9032 arguments,
9033 nativeMethodName,
9034 static,
9035 descriptor,
9036 idlNode,
9037 argConversionStartsAt=0,
9038 getter=False,
9039 setter=False,
9040 isConstructor=False,
9041 useCounterName=None,
9042 resultVar=None,
9043 objectName="obj",
9044 dontSetSlot=False,
9045 extendedAttributes=None,
9046 errorReportingLabel=None,
9047 additionalArgsPre=[],
9049 assert idlNode.isMethod() == (not getter and not setter)
9050 assert idlNode.isAttr() == (getter or setter)
9051 # Constructors are always static
9052 assert not isConstructor or static
9054 CGThing.__init__(self)
9055 self.returnType = returnType
9056 self.descriptor = descriptor
9057 self.idlNode = idlNode
9058 if extendedAttributes is None:
9059 extendedAttributes = descriptor.getExtendedAttributes(
9060 idlNode, getter=getter, setter=setter
9062 self.extendedAttributes = extendedAttributes
9063 self.arguments = arguments
9064 self.argCount = len(arguments)
9065 self.isConstructor = isConstructor
9066 self.setSlot = (
9067 not dontSetSlot and idlNode.isAttr() and idlNode.slotIndices is not None
9069 cgThings = []
9071 deprecated = idlNode.getExtendedAttribute("Deprecated") or (
9072 idlNode.isStatic()
9073 and descriptor.interface.getExtendedAttribute("Deprecated")
9075 if deprecated:
9076 cgThings.append(
9077 CGGeneric(
9078 dedent(
9080 DeprecationWarning(cx, obj, DeprecatedOperations::e%s);
9082 % deprecated[0]
9087 lenientFloatCode = None
9088 if idlNode.getExtendedAttribute("LenientFloat") is not None and (
9089 setter or idlNode.isMethod()
9091 cgThings.append(
9092 CGGeneric(
9093 dedent(
9095 bool foundNonFiniteFloat = false;
9100 lenientFloatCode = "foundNonFiniteFloat = true;\n"
9102 argsPre = []
9103 if idlNode.isStatic():
9104 # If we're a constructor, "obj" may not be a function, so calling
9105 # XrayAwareCalleeGlobal() on it is not safe. Of course in the
9106 # constructor case either "obj" is an Xray or we're already in the
9107 # content compartment, not the Xray compartment, so just
9108 # constructing the GlobalObject from "obj" is fine.
9109 if isConstructor:
9110 objForGlobalObject = "obj"
9111 else:
9112 objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)"
9113 cgThings.append(
9114 CGGeneric(
9115 fill(
9117 GlobalObject global(cx, ${obj});
9118 if (global.Failed()) {
9119 return false;
9122 """,
9123 obj=objForGlobalObject,
9127 argsPre.append("global")
9129 # For JS-implemented interfaces we do not want to base the
9130 # needsCx decision on the types involved, just on our extended
9131 # attributes. Also, JSContext is not needed for the static case
9132 # since GlobalObject already contains the context.
9133 needsCx = needCx(
9134 returnType,
9135 arguments,
9136 self.extendedAttributes,
9137 not descriptor.interface.isJSImplemented(),
9138 static,
9140 if needsCx:
9141 argsPre.append("cx")
9143 needsUnwrap = False
9144 argsPost = []
9145 runConstructorInCallerCompartment = descriptor.interface.getExtendedAttribute(
9146 "RunConstructorInCallerCompartment"
9148 if isConstructor and not runConstructorInCallerCompartment:
9149 needsUnwrap = True
9150 needsUnwrappedVar = False
9151 unwrappedVar = "obj"
9152 if descriptor.interface.isJSImplemented():
9153 # We need the desired proto in our constructor, because the
9154 # constructor will actually construct our reflector.
9155 argsPost.append("desiredProto")
9156 elif descriptor.interface.isJSImplemented():
9157 if not idlNode.isStatic():
9158 needsUnwrap = True
9159 needsUnwrappedVar = True
9160 argsPost.append(
9161 "(unwrappedObj ? js::GetNonCCWObjectRealm(*unwrappedObj) : js::GetContextRealm(cx))"
9163 elif needScopeObject(
9164 returnType,
9165 arguments,
9166 self.extendedAttributes,
9167 descriptor.wrapperCache,
9168 True,
9169 idlNode.getExtendedAttribute("StoreInSlot"),
9171 # If we ever end up with APIs like this on cross-origin objects,
9172 # figure out how the CheckedUnwrapDynamic bits should work. Chances
9173 # are, just calling it with "cx" is fine... For now, though, just
9174 # assert that it does not matter.
9175 assert not descriptor.isMaybeCrossOriginObject()
9176 # The scope object should always be from the relevant
9177 # global. Make sure to unwrap it as needed.
9178 cgThings.append(
9179 CGGeneric(
9180 dedent(
9182 JS::Rooted<JSObject*> unwrappedObj(cx, js::CheckedUnwrapStatic(obj));
9183 // Caller should have ensured that "obj" can be unwrapped already.
9184 MOZ_DIAGNOSTIC_ASSERT(unwrappedObj);
9189 argsPre.append("unwrappedObj")
9191 if needsUnwrap and needsUnwrappedVar:
9192 # We cannot assign into obj because it's a Handle, not a
9193 # MutableHandle, so we need a separate Rooted.
9194 cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;\n"))
9195 unwrappedVar = "unwrappedObj.ref()"
9197 if idlNode.isMethod() and idlNode.isLegacycaller():
9198 # If we can have legacycaller with identifier, we can't
9199 # just use the idlNode to determine whether we're
9200 # generating code for the legacycaller or not.
9201 assert idlNode.isIdentifierLess()
9202 # Pass in our thisVal
9203 argsPre.append("args.thisv()")
9205 if idlNode.isMethod():
9206 argDescription = "argument %(index)d"
9207 elif setter:
9208 argDescription = "value being assigned"
9209 else:
9210 assert self.argCount == 0
9212 if needsUnwrap:
9213 # It's very important that we construct our unwrappedObj, if we need
9214 # to do it, before we might start setting up Rooted things for our
9215 # arguments, so that we don't violate the stack discipline Rooted
9216 # depends on.
9217 cgThings.append(
9218 CGGeneric("bool objIsXray = xpc::WrapperFactory::IsXrayWrapper(obj);\n")
9220 if needsUnwrappedVar:
9221 cgThings.append(
9222 CGIfWrapper(
9223 CGGeneric("unwrappedObj.emplace(cx, obj);\n"), "objIsXray"
9227 for i in range(argConversionStartsAt, self.argCount):
9228 cgThings.append(
9229 CGArgumentConverter(
9230 arguments[i],
9232 self.descriptor,
9233 argDescription % {"index": i + 1},
9234 idlNode,
9235 invalidEnumValueFatal=not setter,
9236 lenientFloatCode=lenientFloatCode,
9240 # Now that argument processing is done, enforce the LenientFloat stuff
9241 if lenientFloatCode:
9242 if setter:
9243 foundNonFiniteFloatBehavior = "return true;\n"
9244 else:
9245 assert idlNode.isMethod()
9246 foundNonFiniteFloatBehavior = dedent(
9248 args.rval().setUndefined();
9249 return true;
9252 cgThings.append(
9253 CGGeneric(
9254 fill(
9256 if (foundNonFiniteFloat) {
9257 $*{returnSteps}
9259 """,
9260 returnSteps=foundNonFiniteFloatBehavior,
9265 if needsUnwrap:
9266 # Something depends on having the unwrapped object, so unwrap it now.
9267 xraySteps = []
9268 # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is
9269 # not null.
9270 xraySteps.append(
9271 CGGeneric(
9272 fill(
9274 // Since our object is an Xray, we can just CheckedUnwrapStatic:
9275 // we know Xrays have no dynamic unwrap behavior.
9276 ${obj} = js::CheckedUnwrapStatic(${obj});
9277 if (!${obj}) {
9278 return false;
9280 """,
9281 obj=unwrappedVar,
9285 if isConstructor:
9286 # If we're called via an xray, we need to enter the underlying
9287 # object's compartment and then wrap up all of our arguments into
9288 # that compartment as needed. This is all happening after we've
9289 # already done the conversions from JS values to WebIDL (C++)
9290 # values, so we only need to worry about cases where there are 'any'
9291 # or 'object' types, or other things that we represent as actual
9292 # JSAPI types, present. Effectively, we're emulating a
9293 # CrossCompartmentWrapper, but working with the C++ types, not the
9294 # original list of JS::Values.
9295 cgThings.append(CGGeneric("Maybe<JSAutoRealm> ar;\n"))
9296 xraySteps.append(CGGeneric("ar.emplace(cx, obj);\n"))
9297 xraySteps.append(
9298 CGGeneric(
9299 dedent(
9301 if (!JS_WrapObject(cx, &desiredProto)) {
9302 return false;
9308 xraySteps.extend(
9309 wrapArgIntoCurrentCompartment(arg, argname, isMember=False)
9310 for arg, argname in self.getArguments()
9313 cgThings.append(CGIfWrapper(CGList(xraySteps), "objIsXray"))
9315 if idlNode.getExtendedAttribute("CEReactions") is not None and not getter:
9316 cgThings.append(
9317 CGGeneric(
9318 dedent(
9320 Maybe<AutoCEReaction> ceReaction;
9321 DocGroup* docGroup = self->GetDocGroup();
9322 if (docGroup) {
9323 ceReaction.emplace(docGroup->CustomElementReactionsStack(), cx);
9330 # If this is a method that was generated by a maplike/setlike
9331 # interface, use the maplike/setlike generator to fill in the body.
9332 # Otherwise, use CGCallGenerator to call the native method.
9333 if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod():
9334 if (
9335 idlNode.maplikeOrSetlikeOrIterable.isMaplike()
9336 or idlNode.maplikeOrSetlikeOrIterable.isSetlike()
9338 cgThings.append(
9339 CGMaplikeOrSetlikeMethodGenerator(
9340 descriptor,
9341 idlNode.maplikeOrSetlikeOrIterable,
9342 idlNode.identifier.name,
9345 else:
9346 cgThings.append(
9347 CGIterableMethodGenerator(
9348 descriptor,
9349 idlNode.identifier.name,
9350 self.getArgumentNames(),
9353 elif idlNode.isAttr() and idlNode.type.isObservableArray():
9354 assert setter
9355 cgThings.append(CGObservableArraySetterGenerator(descriptor, idlNode))
9356 else:
9357 if errorReportingLabel is None:
9358 context = GetLabelForErrorReporting(descriptor, idlNode, isConstructor)
9359 if getter:
9360 context = context + " getter"
9361 elif setter:
9362 context = context + " setter"
9363 # Callee expects a quoted string for the context if
9364 # there's a context.
9365 context = '"%s"' % context
9366 else:
9367 context = errorReportingLabel
9369 if idlNode.isMethod() and idlNode.getExtendedAttribute("WebExtensionStub"):
9371 nativeMethodName,
9372 argsPre,
9373 args,
9374 ] = self.processWebExtensionStubAttribute(cgThings)
9375 else:
9376 args = self.getArguments()
9378 cgThings.append(
9379 CGCallGenerator(
9380 self.needsErrorResult(),
9381 needsCallerType(idlNode),
9382 isChromeOnly(idlNode),
9383 args,
9384 argsPre + additionalArgsPre,
9385 returnType,
9386 self.extendedAttributes,
9387 descriptor,
9388 nativeMethodName,
9389 static,
9390 # We know our "self" must be being kept alive; otherwise we have
9391 # a serious problem. In common cases it's just an argument and
9392 # we're MOZ_CAN_RUN_SCRIPT, but in some cases it's on the stack
9393 # and being kept alive via references from JS.
9394 object="MOZ_KnownLive(self)",
9395 argsPost=argsPost,
9396 resultVar=resultVar,
9397 context=context,
9401 if useCounterName:
9402 # Generate a telemetry call for when [UseCounter] is used.
9403 windowCode = fill(
9405 SetUseCounter(obj, eUseCounter_${useCounterName});
9406 """,
9407 useCounterName=useCounterName,
9409 workerCode = fill(
9411 SetUseCounter(UseCounterWorker::${useCounterName});
9412 """,
9413 useCounterName=useCounterName,
9415 code = ""
9416 if idlNode.isExposedInWindow() and idlNode.isExposedInAnyWorker():
9417 code += fill(
9419 if (NS_IsMainThread()) {
9420 ${windowCode}
9421 } else {
9422 ${workerCode}
9424 """,
9425 windowCode=windowCode,
9426 workerCode=workerCode,
9428 elif idlNode.isExposedInWindow():
9429 code += windowCode
9430 elif idlNode.isExposedInAnyWorker():
9431 code += workerCode
9433 cgThings.append(CGGeneric(code))
9435 self.cgRoot = CGList(cgThings)
9437 def getArgumentNames(self):
9438 return ["arg" + str(i) for i in range(len(self.arguments))]
9440 def getArguments(self):
9441 return list(zip(self.arguments, self.getArgumentNames()))
9443 def processWebExtensionStubAttribute(self, cgThings):
9444 nativeMethodName = "CallWebExtMethod"
9445 stubNameSuffix = self.idlNode.getExtendedAttribute("WebExtensionStub")
9446 if isinstance(stubNameSuffix, list):
9447 nativeMethodName += stubNameSuffix[0]
9449 argsLength = len(self.getArguments())
9450 singleVariadicArg = argsLength == 1 and self.getArguments()[0][0].variadic
9452 # If the method signature does only include a single variadic arguments,
9453 # then `arg0` is already a Sequence of JS values and we can pass that
9454 # to the WebExtensions Stub method as is.
9455 if singleVariadicArg:
9456 argsPre = [
9457 "cx",
9458 'u"%s"_ns' % self.idlNode.identifier.name,
9459 "Constify(%s)" % "arg0",
9461 args = []
9462 return [nativeMethodName, argsPre, args]
9464 argsPre = [
9465 "cx",
9466 'u"%s"_ns' % self.idlNode.identifier.name,
9467 "Constify(%s)" % "args_sequence",
9469 args = []
9471 # Determine the maximum number of elements of the js values sequence argument,
9472 # skipping the last optional callback argument if any:
9474 # if this WebExtensions API method does expect a last optional callback argument,
9475 # then it is the callback parameter supported for chrome-compatibility
9476 # reasons, and we want it as a separate argument passed to the WebExtension
9477 # stub method and skip it from the js values sequence including all other
9478 # arguments.
9479 maxArgsSequenceLen = argsLength
9480 if argsLength > 0:
9481 lastArg = self.getArguments()[argsLength - 1]
9482 isCallback = lastArg[0].type.tag() == IDLType.Tags.callback
9483 if isCallback and lastArg[0].optional:
9484 argsPre.append(
9485 "MOZ_KnownLive(NonNullHelper(Constify(%s)))" % lastArg[1]
9487 maxArgsSequenceLen = argsLength - 1
9489 cgThings.append(
9490 CGGeneric(
9491 dedent(
9492 fill(
9494 // Collecting all args js values into the single sequence argument
9495 // passed to the webextensions stub method.
9497 // NOTE: The stub method will receive the original non-normalized js values,
9498 // but those arguments will still be normalized on the main thread by the
9499 // WebExtensions API request handler using the same JSONSchema defnition
9500 // used by the non-webIDL webextensions API bindings.
9501 AutoSequence<JS::Value> args_sequence;
9502 SequenceRooter<JS::Value> args_sequence_holder(cx, &args_sequence);
9504 // maximum number of arguments expected by the WebExtensions API method
9505 // excluding the last optional chrome-compatible callback argument (which
9506 // is being passed to the stub method as a separate additional argument).
9507 uint32_t maxArgsSequenceLen = ${maxArgsSequenceLen};
9509 uint32_t sequenceArgsLen = args.length() <= maxArgsSequenceLen ?
9510 args.length() : maxArgsSequenceLen;
9512 if (sequenceArgsLen > 0) {
9513 if (!args_sequence.SetCapacity(sequenceArgsLen, mozilla::fallible)) {
9514 JS_ReportOutOfMemory(cx);
9515 return false;
9517 for (uint32_t argIdx = 0; argIdx < sequenceArgsLen; ++argIdx) {
9518 // OK to do infallible append here, since we ensured capacity already.
9519 JS::Value& slot = *args_sequence.AppendElement();
9520 slot = args[argIdx];
9523 """,
9524 maxArgsSequenceLen=maxArgsSequenceLen,
9530 return [nativeMethodName, argsPre, args]
9532 def needsErrorResult(self):
9533 return "needsErrorResult" in self.extendedAttributes
9535 def wrap_return_value(self):
9536 wrapCode = ""
9538 returnsNewObject = memberReturnsNewObject(self.idlNode)
9539 if returnsNewObject and (
9540 self.returnType.isGeckoInterface() or self.returnType.isPromise()
9542 wrapCode += dedent(
9544 static_assert(!std::is_pointer_v<decltype(result)>,
9545 "NewObject implies that we need to keep the object alive with a strong reference.");
9549 if self.setSlot:
9550 # For attributes in slots, we want to do some
9551 # post-processing once we've wrapped them.
9552 successCode = "break;\n"
9553 else:
9554 successCode = None
9556 resultTemplateValues = {
9557 "jsvalRef": "args.rval()",
9558 "jsvalHandle": "args.rval()",
9559 "returnsNewObject": returnsNewObject,
9560 "isConstructorRetval": self.isConstructor,
9561 "successCode": successCode,
9562 # 'obj' in this dictionary is the thing whose compartment we are
9563 # trying to do the to-JS conversion in. We're going to put that
9564 # thing in a variable named "conversionScope" if setSlot is true.
9565 # Otherwise, just use "obj" for lack of anything better.
9566 "obj": "conversionScope" if self.setSlot else "obj",
9569 wrapCode += wrapForType(self.returnType, self.descriptor, resultTemplateValues)
9571 if self.setSlot:
9572 if self.idlNode.isStatic():
9573 raise TypeError(
9574 "Attribute %s.%s is static, so we don't have a useful slot "
9575 "to cache it in, because we don't have support for that on "
9576 "interface objects. See "
9577 "https://bugzilla.mozilla.org/show_bug.cgi?id=1363870"
9579 self.descriptor.interface.identifier.name,
9580 self.idlNode.identifier.name,
9584 # When using a slot on the Xray expando, we need to make sure that
9585 # our initial conversion to a JS::Value is done in the caller
9586 # compartment. When using a slot on our reflector, we want to do
9587 # the conversion in the compartment of that reflector (that is,
9588 # slotStorage). In both cases we want to make sure that we finally
9589 # set up args.rval() to be in the caller compartment. We also need
9590 # to make sure that the conversion steps happen inside a do/while
9591 # that they can break out of on success.
9593 # Of course we always have to wrap the value into the slotStorage
9594 # compartment before we store it in slotStorage.
9596 # postConversionSteps are the steps that run while we're still in
9597 # the compartment we do our conversion in but after we've finished
9598 # the initial conversion into args.rval().
9599 postConversionSteps = ""
9600 if self.idlNode.getExtendedAttribute("Frozen"):
9601 assert (
9602 self.idlNode.type.isSequence() or self.idlNode.type.isDictionary()
9604 freezeValue = CGGeneric(
9605 "JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());\n"
9606 "if (!JS_FreezeObject(cx, rvalObj)) {\n"
9607 " return false;\n"
9608 "}\n"
9610 if self.idlNode.type.nullable():
9611 freezeValue = CGIfWrapper(freezeValue, "args.rval().isObject()")
9612 postConversionSteps += freezeValue.define()
9614 # slotStorageSteps are steps that run once we have entered the
9615 # slotStorage compartment.
9616 slotStorageSteps = fill(
9618 // Make a copy so that we don't do unnecessary wrapping on args.rval().
9619 JS::Rooted<JS::Value> storedVal(cx, args.rval());
9620 if (!${maybeWrap}(cx, &storedVal)) {
9621 return false;
9623 JS::SetReservedSlot(slotStorage, slotIndex, storedVal);
9624 """,
9625 maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type),
9628 checkForXray = mayUseXrayExpandoSlots(self.descriptor, self.idlNode)
9630 # For the case of Cached attributes, go ahead and preserve our
9631 # wrapper if needed. We need to do this because otherwise the
9632 # wrapper could get garbage-collected and the cached value would
9633 # suddenly disappear, but the whole premise of cached values is that
9634 # they never change without explicit action on someone's part. We
9635 # don't do this for StoreInSlot, since those get dealt with during
9636 # wrapper setup, and failure would involve us trying to clear an
9637 # already-preserved wrapper.
9638 if (
9639 self.idlNode.getExtendedAttribute("Cached")
9640 and self.descriptor.wrapperCache
9642 preserveWrapper = dedent(
9644 PreserveWrapper(self);
9647 if checkForXray:
9648 preserveWrapper = fill(
9650 if (!isXray) {
9651 // In the Xray case we don't need to do this, because getting the
9652 // expando object already preserved our wrapper.
9653 $*{preserveWrapper}
9655 """,
9656 preserveWrapper=preserveWrapper,
9658 slotStorageSteps += preserveWrapper
9660 if checkForXray:
9661 # In the Xray case we use the current global as conversion
9662 # scope, as explained in the big compartment/conversion comment
9663 # above.
9664 conversionScope = "isXray ? JS::CurrentGlobalOrNull(cx) : slotStorage"
9665 else:
9666 conversionScope = "slotStorage"
9668 wrapCode = fill(
9671 JS::Rooted<JSObject*> conversionScope(cx, ${conversionScope});
9672 JSAutoRealm ar(cx, conversionScope);
9673 do { // block we break out of when done wrapping
9674 $*{wrapCode}
9675 } while (false);
9676 $*{postConversionSteps}
9678 { // And now store things in the realm of our slotStorage.
9679 JSAutoRealm ar(cx, slotStorage);
9680 $*{slotStorageSteps}
9682 // And now make sure args.rval() is in the caller realm.
9683 return ${maybeWrap}(cx, args.rval());
9684 """,
9685 conversionScope=conversionScope,
9686 wrapCode=wrapCode,
9687 postConversionSteps=postConversionSteps,
9688 slotStorageSteps=slotStorageSteps,
9689 maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type),
9691 return wrapCode
9693 def define(self):
9694 return self.cgRoot.define() + self.wrap_return_value()
9697 class CGSwitch(CGList):
9699 A class to generate code for a switch statement.
9701 Takes three constructor arguments: an expression, a list of cases,
9702 and an optional default.
9704 Each case is a CGCase. The default is a CGThing for the body of
9705 the default case, if any.
9708 def __init__(self, expression, cases, default=None):
9709 CGList.__init__(self, [CGIndenter(c) for c in cases])
9710 self.prepend(CGGeneric("switch (" + expression + ") {\n"))
9711 if default is not None:
9712 self.append(
9713 CGIndenter(
9714 CGWrapper(CGIndenter(default), pre="default: {\n", post="}\n")
9718 self.append(CGGeneric("}\n"))
9721 class CGCase(CGList):
9723 A class to generate code for a case statement.
9725 Takes three constructor arguments: an expression, a CGThing for
9726 the body (allowed to be None if there is no body), and an optional
9727 argument for whether add a break, add fallthrough annotation or add nothing
9728 (defaulting to add a break).
9731 ADD_BREAK = 0
9732 ADD_FALLTHROUGH = 1
9733 DONT_ADD_BREAK = 2
9735 def __init__(self, expression, body, breakOrFallthrough=ADD_BREAK):
9736 CGList.__init__(self, [])
9738 assert (
9739 breakOrFallthrough == CGCase.ADD_BREAK
9740 or breakOrFallthrough == CGCase.ADD_FALLTHROUGH
9741 or breakOrFallthrough == CGCase.DONT_ADD_BREAK
9744 self.append(CGGeneric("case " + expression + ": {\n"))
9745 bodyList = CGList([body])
9746 if breakOrFallthrough == CGCase.ADD_FALLTHROUGH:
9747 bodyList.append(CGGeneric("[[fallthrough]];\n"))
9748 elif breakOrFallthrough == CGCase.ADD_BREAK:
9749 bodyList.append(CGGeneric("break;\n"))
9750 self.append(CGIndenter(bodyList))
9751 self.append(CGGeneric("}\n"))
9754 class CGMethodCall(CGThing):
9756 A class to generate selection of a method signature from a set of
9757 signatures and generation of a call to that signature.
9760 def __init__(
9761 self, nativeMethodName, static, descriptor, method, isConstructor=False
9763 CGThing.__init__(self)
9765 methodName = GetLabelForErrorReporting(descriptor, method, isConstructor)
9766 argDesc = "argument %d"
9768 if method.getExtendedAttribute("UseCounter"):
9769 useCounterName = methodName.replace(".", "_").replace(" ", "_")
9770 else:
9771 useCounterName = None
9773 if method.isStatic():
9774 nativeType = descriptor.nativeType
9775 staticTypeOverride = PropertyDefiner.getStringAttr(
9776 method, "StaticClassOverride"
9778 if staticTypeOverride:
9779 nativeType = staticTypeOverride
9780 nativeMethodName = "%s::%s" % (nativeType, nativeMethodName)
9782 def requiredArgCount(signature):
9783 arguments = signature[1]
9784 if len(arguments) == 0:
9785 return 0
9786 requiredArgs = len(arguments)
9787 while requiredArgs and arguments[requiredArgs - 1].optional:
9788 requiredArgs -= 1
9789 return requiredArgs
9791 def getPerSignatureCall(signature, argConversionStartsAt=0):
9792 return CGPerSignatureCall(
9793 signature[0],
9794 signature[1],
9795 nativeMethodName,
9796 static,
9797 descriptor,
9798 method,
9799 argConversionStartsAt=argConversionStartsAt,
9800 isConstructor=isConstructor,
9801 useCounterName=useCounterName,
9804 signatures = method.signatures()
9805 if len(signatures) == 1:
9806 # Special case: we can just do a per-signature method call
9807 # here for our one signature and not worry about switching
9808 # on anything.
9809 signature = signatures[0]
9810 self.cgRoot = CGList([getPerSignatureCall(signature)])
9811 requiredArgs = requiredArgCount(signature)
9813 # Skip required arguments check for maplike/setlike interfaces, as
9814 # they can have arguments which are not passed, and are treated as
9815 # if undefined had been explicitly passed.
9816 if requiredArgs > 0 and not method.isMaplikeOrSetlikeOrIterableMethod():
9817 code = fill(
9819 if (!args.requireAtLeast(cx, "${methodName}", ${requiredArgs})) {
9820 return false;
9822 """,
9823 requiredArgs=requiredArgs,
9824 methodName=methodName,
9826 self.cgRoot.prepend(CGGeneric(code))
9827 return
9829 # Need to find the right overload
9830 maxArgCount = method.maxArgCount
9831 allowedArgCounts = method.allowedArgCounts
9833 argCountCases = []
9834 for argCountIdx, argCount in enumerate(allowedArgCounts):
9835 possibleSignatures = method.signaturesForArgCount(argCount)
9837 # Try to optimize away cases when the next argCount in the list
9838 # will have the same code as us; if it does, we can fall through to
9839 # that case.
9840 if argCountIdx + 1 < len(allowedArgCounts):
9841 nextPossibleSignatures = method.signaturesForArgCount(
9842 allowedArgCounts[argCountIdx + 1]
9844 else:
9845 nextPossibleSignatures = None
9846 if possibleSignatures == nextPossibleSignatures:
9847 # Same set of signatures means we better have the same
9848 # distinguishing index. So we can in fact just fall through to
9849 # the next case here.
9850 assert len(possibleSignatures) == 1 or (
9851 method.distinguishingIndexForArgCount(argCount)
9852 == method.distinguishingIndexForArgCount(
9853 allowedArgCounts[argCountIdx + 1]
9856 argCountCases.append(
9857 CGCase(str(argCount), None, CGCase.ADD_FALLTHROUGH)
9859 continue
9861 if len(possibleSignatures) == 1:
9862 # easy case!
9863 signature = possibleSignatures[0]
9864 argCountCases.append(
9865 CGCase(str(argCount), getPerSignatureCall(signature))
9867 continue
9869 distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
9871 def distinguishingArgument(signature):
9872 args = signature[1]
9873 if distinguishingIndex < len(args):
9874 return args[distinguishingIndex]
9875 assert args[-1].variadic
9876 return args[-1]
9878 def distinguishingType(signature):
9879 return distinguishingArgument(signature).type
9881 for sig in possibleSignatures:
9882 # We should not have "any" args at distinguishingIndex,
9883 # since we have multiple possible signatures remaining,
9884 # but "any" is never distinguishable from anything else.
9885 assert not distinguishingType(sig).isAny()
9886 # We can't handle unions at the distinguishing index.
9887 if distinguishingType(sig).isUnion():
9888 raise TypeError(
9889 "No support for unions as distinguishing "
9890 "arguments yet: %s" % distinguishingArgument(sig).location
9892 # We don't support variadics as the distinguishingArgument yet.
9893 # If you want to add support, consider this case:
9895 # undefined(long... foo);
9896 # undefined(long bar, Int32Array baz);
9898 # in which we have to convert argument 0 to long before picking
9899 # an overload... but all the variadic stuff needs to go into a
9900 # single array in case we pick that overload, so we have to have
9901 # machinery for converting argument 0 to long and then either
9902 # placing it in the variadic bit or not. Or something. We may
9903 # be able to loosen this restriction if the variadic arg is in
9904 # fact at distinguishingIndex, perhaps. Would need to
9905 # double-check.
9906 if distinguishingArgument(sig).variadic:
9907 raise TypeError(
9908 "No support for variadics as distinguishing "
9909 "arguments yet: %s" % distinguishingArgument(sig).location
9912 # Convert all our arguments up to the distinguishing index.
9913 # Doesn't matter which of the possible signatures we use, since
9914 # they all have the same types up to that point; just use
9915 # possibleSignatures[0]
9916 caseBody = [
9917 CGArgumentConverter(
9918 possibleSignatures[0][1][i],
9920 descriptor,
9921 argDesc % (i + 1),
9922 method,
9924 for i in range(0, distinguishingIndex)
9927 # Select the right overload from our set.
9928 distinguishingArg = "args[%d]" % distinguishingIndex
9930 def tryCall(
9931 signature, indent, isDefinitelyObject=False, isNullOrUndefined=False
9933 assert not isDefinitelyObject or not isNullOrUndefined
9934 assert isDefinitelyObject or isNullOrUndefined
9935 if isDefinitelyObject:
9936 failureCode = "break;\n"
9937 else:
9938 failureCode = None
9939 type = distinguishingType(signature)
9940 # The argument at index distinguishingIndex can't possibly be
9941 # unset here, because we've already checked that argc is large
9942 # enough that we can examine this argument. But note that we
9943 # still want to claim that optional arguments are optional, in
9944 # case undefined was passed in.
9945 argIsOptional = distinguishingArgument(signature).canHaveMissingValue()
9946 testCode = instantiateJSToNativeConversion(
9947 getJSToNativeConversionInfo(
9948 type,
9949 descriptor,
9950 failureCode=failureCode,
9951 isDefinitelyObject=isDefinitelyObject,
9952 isNullOrUndefined=isNullOrUndefined,
9953 isOptional=argIsOptional,
9954 sourceDescription=(argDesc % (distinguishingIndex + 1)),
9957 "declName": "arg%d" % distinguishingIndex,
9958 "holderName": ("arg%d" % distinguishingIndex) + "_holder",
9959 "val": distinguishingArg,
9960 "obj": "obj",
9961 "haveValue": "args.hasDefined(%d)" % distinguishingIndex,
9962 "passedToJSImpl": toStringBool(
9963 isJSImplementedDescriptor(descriptor)
9966 checkForValue=argIsOptional,
9968 caseBody.append(CGIndenter(testCode, indent))
9970 # If we got this far, we know we unwrapped to the right
9971 # C++ type, so just do the call. Start conversion with
9972 # distinguishingIndex + 1, since we already converted
9973 # distinguishingIndex.
9974 caseBody.append(
9975 CGIndenter(
9976 getPerSignatureCall(signature, distinguishingIndex + 1), indent
9980 def hasConditionalConversion(type):
9982 Return whether the argument conversion for this type will be
9983 conditional on the type of incoming JS value. For example, for
9984 interface types the conversion is conditional on the incoming
9985 value being isObject().
9987 For the types for which this returns false, we do not have to
9988 output extra isUndefined() or isNullOrUndefined() cases, because
9989 null/undefined values will just fall through into our
9990 unconditional conversion.
9992 if type.isString() or type.isEnum():
9993 return False
9994 if type.isBoolean():
9995 distinguishingTypes = (
9996 distinguishingType(s) for s in possibleSignatures
9998 return any(
9999 t.isString() or t.isEnum() or t.isNumeric()
10000 for t in distinguishingTypes
10002 if type.isNumeric():
10003 distinguishingTypes = (
10004 distinguishingType(s) for s in possibleSignatures
10006 return any(t.isString() or t.isEnum() for t in distinguishingTypes)
10007 return True
10009 def needsNullOrUndefinedCase(type):
10011 Return true if the type needs a special isNullOrUndefined() case
10013 return (
10014 type.nullable() and hasConditionalConversion(type)
10015 ) or type.isDictionary()
10017 # First check for undefined and optional distinguishing arguments
10018 # and output a special branch for that case. Note that we don't
10019 # use distinguishingArgument here because we actualy want to
10020 # exclude variadic arguments. Also note that we skip this check if
10021 # we plan to output a isNullOrUndefined() special case for this
10022 # argument anyway, since that will subsume our isUndefined() check.
10023 # This is safe, because there can be at most one nullable
10024 # distinguishing argument, so if we're it we'll definitely get
10025 # picked up by the nullable handling. Also, we can skip this check
10026 # if the argument has an unconditional conversion later on.
10027 undefSigs = [
10029 for s in possibleSignatures
10030 if distinguishingIndex < len(s[1])
10031 and s[1][distinguishingIndex].optional
10032 and hasConditionalConversion(s[1][distinguishingIndex].type)
10033 and not needsNullOrUndefinedCase(s[1][distinguishingIndex].type)
10035 # Can't have multiple signatures with an optional argument at the
10036 # same index.
10037 assert len(undefSigs) < 2
10038 if len(undefSigs) > 0:
10039 caseBody.append(
10040 CGGeneric("if (%s.isUndefined()) {\n" % distinguishingArg)
10042 tryCall(undefSigs[0], 2, isNullOrUndefined=True)
10043 caseBody.append(CGGeneric("}\n"))
10045 # Next, check for null or undefined. That means looking for
10046 # nullable arguments at the distinguishing index and outputting a
10047 # separate branch for them. But if the nullable argument has an
10048 # unconditional conversion, we don't need to do that. The reason
10049 # for that is that at most one argument at the distinguishing index
10050 # is nullable (since two nullable arguments are not
10051 # distinguishable), and null/undefined values will always fall
10052 # through to the unconditional conversion we have, if any, since
10053 # they will fail whatever the conditions on the input value are for
10054 # our other conversions.
10055 nullOrUndefSigs = [
10057 for s in possibleSignatures
10058 if needsNullOrUndefinedCase(distinguishingType(s))
10060 # Can't have multiple nullable types here
10061 assert len(nullOrUndefSigs) < 2
10062 if len(nullOrUndefSigs) > 0:
10063 caseBody.append(
10064 CGGeneric("if (%s.isNullOrUndefined()) {\n" % distinguishingArg)
10066 tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True)
10067 caseBody.append(CGGeneric("}\n"))
10069 # Now check for distinguishingArg being various kinds of objects.
10070 # The spec says to check for the following things in order:
10071 # 1) A platform object that's not a platform array object, being
10072 # passed to an interface or "object" arg.
10073 # 2) A callable object being passed to a callback or "object" arg.
10074 # 3) An iterable object being passed to a sequence arg.
10075 # 4) Any object being passed to a array or callback interface or
10076 # dictionary or "object" arg.
10078 # First grab all the overloads that have a non-callback interface
10079 # (which includes SpiderMonkey interfaces) at the distinguishing
10080 # index. We can also include the ones that have an "object" here,
10081 # since if those are present no other object-typed argument will
10082 # be.
10083 objectSigs = [
10085 for s in possibleSignatures
10086 if (
10087 distinguishingType(s).isObject()
10088 or distinguishingType(s).isNonCallbackInterface()
10092 # And all the overloads that take callbacks
10093 objectSigs.extend(
10094 s for s in possibleSignatures if distinguishingType(s).isCallback()
10097 # And all the overloads that take sequences
10098 objectSigs.extend(
10099 s for s in possibleSignatures if distinguishingType(s).isSequence()
10102 # Now append all the overloads that take a dictionary or callback
10103 # interface or record. There should be only one of these!
10104 genericObjectSigs = [
10106 for s in possibleSignatures
10107 if (
10108 distinguishingType(s).isDictionary()
10109 or distinguishingType(s).isRecord()
10110 or distinguishingType(s).isCallbackInterface()
10113 assert len(genericObjectSigs) <= 1
10114 objectSigs.extend(genericObjectSigs)
10116 # There might be more than one thing in objectSigs; we need to check
10117 # which ones we unwrap to.
10118 if len(objectSigs) > 0:
10119 # Here it's enough to guard on our argument being an object.
10120 # The code for unwrapping non-callback interfaces, spiderMonkey
10121 # interfaces, and sequences will just bail out and move
10122 # on to the next overload if the object fails to unwrap
10123 # correctly, while "object" accepts any object anyway. We
10124 # could even not do the isObject() check up front here, but in
10125 # cases where we have multiple object overloads it makes sense
10126 # to do it only once instead of for each overload. That will
10127 # also allow the unwrapping test to skip having to do codegen
10128 # for the null-or-undefined case, which we already handled
10129 # above.
10130 caseBody.append(CGGeneric("if (%s.isObject()) {\n" % distinguishingArg))
10131 for sig in objectSigs:
10132 caseBody.append(CGIndenter(CGGeneric("do {\n")))
10133 # Indent by 4, since we need to indent further
10134 # than our "do" statement
10135 tryCall(sig, 4, isDefinitelyObject=True)
10136 caseBody.append(CGIndenter(CGGeneric("} while (false);\n")))
10138 caseBody.append(CGGeneric("}\n"))
10140 # Now we only have to consider booleans, numerics, and strings. If
10141 # we only have one of them, then we can just output it. But if not,
10142 # then we need to output some of the cases conditionally: if we have
10143 # a string overload, then boolean and numeric are conditional, and
10144 # if not then boolean is conditional if we have a numeric overload.
10145 def findUniqueSignature(filterLambda):
10146 sigs = [s for s in possibleSignatures if filterLambda(s)]
10147 assert len(sigs) < 2
10148 if len(sigs) > 0:
10149 return sigs[0]
10150 return None
10152 stringSignature = findUniqueSignature(
10153 lambda s: (
10154 distinguishingType(s).isString() or distinguishingType(s).isEnum()
10157 numericSignature = findUniqueSignature(
10158 lambda s: distinguishingType(s).isNumeric()
10160 booleanSignature = findUniqueSignature(
10161 lambda s: distinguishingType(s).isBoolean()
10164 if stringSignature or numericSignature:
10165 booleanCondition = "%s.isBoolean()"
10166 else:
10167 booleanCondition = None
10169 if stringSignature:
10170 numericCondition = "%s.isNumber()"
10171 else:
10172 numericCondition = None
10174 def addCase(sig, condition):
10175 sigCode = getPerSignatureCall(sig, distinguishingIndex)
10176 if condition:
10177 sigCode = CGIfWrapper(sigCode, condition % distinguishingArg)
10178 caseBody.append(sigCode)
10180 if booleanSignature:
10181 addCase(booleanSignature, booleanCondition)
10182 if numericSignature:
10183 addCase(numericSignature, numericCondition)
10184 if stringSignature:
10185 addCase(stringSignature, None)
10187 if not booleanSignature and not numericSignature and not stringSignature:
10188 # Just throw; we have no idea what we're supposed to
10189 # do with this.
10190 caseBody.append(
10191 CGGeneric(
10192 'return cx.ThrowErrorMessage<MSG_OVERLOAD_RESOLUTION_FAILED>("%d", "%d");\n'
10193 % (distinguishingIndex + 1, argCount)
10197 argCountCases.append(CGCase(str(argCount), CGList(caseBody)))
10199 overloadCGThings = []
10200 overloadCGThings.append(
10201 CGGeneric(
10202 "unsigned argcount = std::min(args.length(), %du);\n" % maxArgCount
10205 overloadCGThings.append(
10206 CGSwitch(
10207 "argcount",
10208 argCountCases,
10209 CGGeneric(
10210 dedent(
10212 // Using nsPrintfCString here would require including that
10213 // header. Let's not worry about it.
10214 nsAutoCString argCountStr;
10215 argCountStr.AppendPrintf("%u", args.length());
10216 return cx.ThrowErrorMessage<MSG_INVALID_OVERLOAD_ARGCOUNT>(argCountStr.get());
10222 overloadCGThings.append(
10223 CGGeneric(
10224 'MOZ_CRASH("We have an always-returning default case");\n'
10225 "return false;\n"
10228 self.cgRoot = CGList(overloadCGThings)
10230 def define(self):
10231 return self.cgRoot.define()
10234 class CGGetterCall(CGPerSignatureCall):
10236 A class to generate a native object getter call for a particular IDL
10237 getter.
10240 def __init__(
10241 self,
10242 returnType,
10243 nativeMethodName,
10244 descriptor,
10245 attr,
10246 errorReportingLabel=None,
10247 argsPre=[],
10248 dontSetSlot=False,
10249 extendedAttributes=None,
10251 if attr.getExtendedAttribute("UseCounter"):
10252 useCounterName = "%s_%s_getter" % (
10253 descriptor.interface.identifier.name,
10254 attr.identifier.name,
10256 else:
10257 useCounterName = None
10258 if attr.isStatic():
10259 nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName)
10260 CGPerSignatureCall.__init__(
10261 self,
10262 returnType,
10264 nativeMethodName,
10265 attr.isStatic(),
10266 descriptor,
10267 attr,
10268 getter=True,
10269 useCounterName=useCounterName,
10270 dontSetSlot=dontSetSlot,
10271 extendedAttributes=extendedAttributes,
10272 errorReportingLabel=errorReportingLabel,
10273 additionalArgsPre=argsPre,
10277 class FakeIdentifier:
10278 def __init__(self, name):
10279 self.name = name
10282 class FakeArgument:
10284 A class that quacks like an IDLArgument. This is used to make
10285 setters look like method calls or for special operations.
10288 def __init__(self, type, name="arg", allowTreatNonCallableAsNull=False):
10289 self.type = type
10290 self.optional = False
10291 self.variadic = False
10292 self.defaultValue = None
10293 self._allowTreatNonCallableAsNull = allowTreatNonCallableAsNull
10295 self.identifier = FakeIdentifier(name)
10297 def allowTreatNonCallableAsNull(self):
10298 return self._allowTreatNonCallableAsNull
10300 def canHaveMissingValue(self):
10301 return False
10304 class CGSetterCall(CGPerSignatureCall):
10306 A class to generate a native object setter call for a particular IDL
10307 setter.
10310 def __init__(
10311 self,
10312 argType,
10313 nativeMethodName,
10314 descriptor,
10315 attr,
10316 errorReportingLabel=None,
10317 argsPre=[],
10319 if attr.getExtendedAttribute("UseCounter"):
10320 useCounterName = "%s_%s_setter" % (
10321 descriptor.interface.identifier.name,
10322 attr.identifier.name,
10324 else:
10325 useCounterName = None
10326 if attr.isStatic():
10327 nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName)
10328 CGPerSignatureCall.__init__(
10329 self,
10330 None,
10331 [FakeArgument(argType, allowTreatNonCallableAsNull=True)],
10332 nativeMethodName,
10333 attr.isStatic(),
10334 descriptor,
10335 attr,
10336 setter=True,
10337 useCounterName=useCounterName,
10338 errorReportingLabel=errorReportingLabel,
10339 additionalArgsPre=argsPre,
10342 def wrap_return_value(self):
10343 attr = self.idlNode
10344 clearSlot = ""
10345 if self.descriptor.wrapperCache and attr.slotIndices is not None:
10346 if attr.getExtendedAttribute("StoreInSlot"):
10347 clearSlot = "%s(cx, self);\n" % MakeClearCachedValueNativeName(
10348 self.idlNode
10350 elif attr.getExtendedAttribute("Cached"):
10351 clearSlot = "%s(self);\n" % MakeClearCachedValueNativeName(self.idlNode)
10353 # We have no return value
10354 return "\n" "%s" "return true;\n" % clearSlot
10357 class CGAbstractBindingMethod(CGAbstractStaticMethod):
10359 Common class to generate some of our class hooks. This will generate the
10360 function declaration, get a reference to the JS object for our binding
10361 object (which might be an argument of the class hook or something we get
10362 from a JS::CallArgs), and unwrap into the right C++ type. Subclasses are
10363 expected to override the generate_code function to do the rest of the work.
10364 This function should return a CGThing which is already properly indented.
10366 getThisObj should be code for getting a JSObject* for the binding
10367 object. "" can be passed in if the binding object is already stored in
10368 'obj'.
10370 callArgs should be code for getting a JS::CallArgs into a variable
10371 called 'args'. This can be "" if there is already such a variable
10372 around or if the body does not need a JS::CallArgs.
10376 def __init__(
10377 self,
10378 descriptor,
10379 name,
10380 args,
10381 getThisObj,
10382 callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n",
10384 CGAbstractStaticMethod.__init__(
10385 self, descriptor, name, "bool", args, canRunScript=True
10388 # This can't ever happen, because we only use this for class hooks.
10389 self.unwrapFailureCode = fill(
10391 MOZ_CRASH("Unexpected object in '${name}' hook");
10392 return false;
10393 """,
10394 name=name,
10397 if getThisObj == "":
10398 self.getThisObj = None
10399 else:
10400 self.getThisObj = CGGeneric(
10401 "JS::Rooted<JSObject*> obj(cx, %s);\n" % getThisObj
10403 self.callArgs = callArgs
10405 def definition_body(self):
10406 body = self.callArgs
10407 if self.getThisObj is not None:
10408 body += self.getThisObj.define() + "\n"
10409 body += "%s* self;\n" % self.descriptor.nativeType
10410 body += dedent(
10412 JS::Rooted<JS::Value> rootSelf(cx, JS::ObjectValue(*obj));
10416 body += str(
10417 CastableObjectUnwrapper(
10418 self.descriptor, "rootSelf", "&rootSelf", "self", self.unwrapFailureCode
10422 return body + self.generate_code().define()
10424 def generate_code(self):
10425 assert False # Override me
10428 class CGAbstractStaticBindingMethod(CGAbstractStaticMethod):
10430 Common class to generate the JSNatives for all our static methods, getters
10431 and setters. This will generate the function declaration and unwrap the
10432 global object. Subclasses are expected to override the generate_code
10433 function to do the rest of the work. This function should return a
10434 CGThing which is already properly indented.
10437 def __init__(self, descriptor, name):
10438 CGAbstractStaticMethod.__init__(
10439 self, descriptor, name, "bool", JSNativeArguments(), canRunScript=True
10442 def definition_body(self):
10443 # Make sure that "obj" is in the same compartment as "cx", since we'll
10444 # later use it to wrap return values.
10445 unwrap = dedent(
10447 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
10448 JS::Rooted<JSObject*> obj(cx, &args.callee());
10452 return unwrap + self.generate_code().define()
10454 def generate_code(self):
10455 assert False # Override me
10458 def MakeNativeName(name):
10459 return name[0].upper() + IDLToCIdentifier(name[1:])
10462 def GetWebExposedName(idlObject, descriptor):
10463 if idlObject == descriptor.operations["Stringifier"]:
10464 return "toString"
10465 name = idlObject.identifier.name
10466 if name == "__namedsetter":
10467 return "named setter"
10468 if name == "__namedgetter":
10469 return "named getter"
10470 if name == "__indexedsetter":
10471 return "indexed setter"
10472 if name == "__indexedgetter":
10473 return "indexed getter"
10474 if name == "__legacycaller":
10475 return "legacy caller"
10476 return name
10479 def GetConstructorNameForReporting(descriptor, ctor):
10480 # Figure out the name of our constructor for reporting purposes.
10481 # For unnamed webidl constructors, identifier.name is "constructor" but
10482 # the name JS sees is the interface name; for legacy factory functions
10483 # identifier.name is the actual name.
10484 ctorName = ctor.identifier.name
10485 if ctorName == "constructor":
10486 return descriptor.interface.identifier.name
10487 return ctorName
10490 def GetLabelForErrorReporting(descriptor, idlObject, isConstructor):
10492 descriptor is the descriptor for the interface involved
10494 idlObject is the method (regular or static), attribute (regular or
10495 static), or constructor (named or not) involved.
10497 isConstructor is true if idlObject is a constructor and false otherwise.
10499 if isConstructor:
10500 return "%s constructor" % GetConstructorNameForReporting(descriptor, idlObject)
10502 namePrefix = descriptor.interface.identifier.name
10503 name = GetWebExposedName(idlObject, descriptor)
10504 if " " in name:
10505 # It's got a space already, so just space-separate.
10506 return "%s %s" % (namePrefix, name)
10508 return "%s.%s" % (namePrefix, name)
10511 class CGSpecializedMethod(CGAbstractStaticMethod):
10513 A class for generating the C++ code for a specialized method that the JIT
10514 can call with lower overhead.
10517 def __init__(self, descriptor, method):
10518 self.method = method
10519 name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name))
10520 args = [
10521 Argument("JSContext*", "cx"),
10522 Argument("JS::Handle<JSObject*>", "obj"),
10523 Argument("void*", "void_self"),
10524 Argument("const JSJitMethodCallArgs&", "args"),
10526 CGAbstractStaticMethod.__init__(
10527 self, descriptor, name, "bool", args, canRunScript=True
10530 def definition_body(self):
10531 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method)
10532 call = CGMethodCall(
10533 nativeName, self.method.isStatic(), self.descriptor, self.method
10534 ).define()
10535 prefix = ""
10536 if self.method.getExtendedAttribute("CrossOriginCallable"):
10537 for signature in self.method.signatures():
10538 # non-undefined signatures would require us to deal with remote proxies for the
10539 # return value here.
10540 if not signature[0].isUndefined():
10541 raise TypeError(
10542 "We don't support a method marked as CrossOriginCallable "
10543 "with non-undefined return type"
10545 prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
10546 prefix = fill(
10548 // CrossOriginThisPolicy::UnwrapThisObject stores a ${nativeType}::RemoteProxy in void_self
10549 // if obj is a proxy with a RemoteObjectProxy handler for the right type, or else it stores
10550 // a ${nativeType}. If we get here from the JIT (without going through UnwrapThisObject) we
10551 // know void_self contains a ${nativeType}; we don't have special cases in the JIT to deal
10552 // with remote object proxies.
10553 if (IsRemoteObjectProxy(obj, ${prototypeID})) {
10554 auto* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
10555 $*{call}
10557 """,
10558 prototypeID=prototypeID,
10559 nativeType=self.descriptor.nativeType,
10560 call=call,
10562 return prefix + fill(
10564 auto* self = static_cast<${nativeType}*>(void_self);
10565 $*{call}
10566 """,
10567 nativeType=self.descriptor.nativeType,
10568 call=call,
10571 def auto_profiler_label(self):
10572 interface_name = self.descriptor.interface.identifier.name
10573 method_name = self.method.identifier.name
10574 return fill(
10576 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
10577 "${interface_name}", "${method_name}", DOM, cx,
10578 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_METHOD) |
10579 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
10580 """,
10581 interface_name=interface_name,
10582 method_name=method_name,
10585 @staticmethod
10586 def should_have_method_description(descriptor, idlMethod):
10588 Returns whether the given IDL method (static, non-static, constructor)
10589 should have a method description declaration, for use in error
10590 reporting.
10592 # If a method has overloads, it needs a method description, because it
10593 # can throw MSG_INVALID_OVERLOAD_ARGCOUNT at the very least.
10594 if len(idlMethod.signatures()) != 1:
10595 return True
10597 # Methods with only one signature need a method description if one of
10598 # their args needs it.
10599 sig = idlMethod.signatures()[0]
10600 args = sig[1]
10601 return any(
10602 idlTypeNeedsCallContext(
10603 arg.type,
10604 descriptor,
10605 allowTreatNonCallableAsNull=arg.allowTreatNonCallableAsNull(),
10607 for arg in args
10610 @staticmethod
10611 def error_reporting_label_helper(descriptor, idlMethod, isConstructor):
10613 Returns the method description to use for error reporting for the given
10614 IDL method. Used to implement common error_reporting_label() functions
10615 across different classes.
10617 if not CGSpecializedMethod.should_have_method_description(
10618 descriptor, idlMethod
10620 return None
10621 return '"%s"' % GetLabelForErrorReporting(descriptor, idlMethod, isConstructor)
10623 def error_reporting_label(self):
10624 return CGSpecializedMethod.error_reporting_label_helper(
10625 self.descriptor, self.method, isConstructor=False
10628 @staticmethod
10629 def makeNativeName(descriptor, method):
10630 if method.underlyingAttr:
10631 return CGSpecializedGetterCommon.makeNativeName(
10632 descriptor, method.underlyingAttr
10634 name = method.identifier.name
10635 return MakeNativeName(descriptor.binaryNameFor(name, method.isStatic()))
10638 class CGMethodPromiseWrapper(CGAbstractStaticMethod):
10640 A class for generating a wrapper around another method that will
10641 convert exceptions to promises.
10644 def __init__(self, descriptor, methodToWrap):
10645 self.method = methodToWrap
10646 name = self.makeName(methodToWrap.name)
10647 args = list(methodToWrap.args)
10648 CGAbstractStaticMethod.__init__(
10649 self, descriptor, name, "bool", args, canRunScript=True
10652 def definition_body(self):
10653 return fill(
10655 bool ok = ${methodName}(${args});
10656 if (ok) {
10657 return true;
10659 return ConvertExceptionToPromise(cx, args.rval());
10660 """,
10661 methodName=self.method.name,
10662 args=", ".join(arg.name for arg in self.args),
10665 @staticmethod
10666 def makeName(methodName):
10667 return methodName + "_promiseWrapper"
10670 class CGDefaultToJSONMethod(CGSpecializedMethod):
10671 def __init__(self, descriptor, method):
10672 assert method.isDefaultToJSON()
10673 CGSpecializedMethod.__init__(self, descriptor, method)
10675 def definition_body(self):
10676 ret = fill(
10678 auto* self = static_cast<${nativeType}*>(void_self);
10679 JS::Rooted<JSObject*> result(cx, JS_NewPlainObject(cx));
10680 if (!result) {
10681 return false;
10683 """,
10684 nativeType=self.descriptor.nativeType,
10687 jsonDescriptors = [self.descriptor]
10688 interface = self.descriptor.interface.parent
10689 while interface:
10690 descriptor = self.descriptor.getDescriptor(interface.identifier.name)
10691 if descriptor.hasDefaultToJSON:
10692 jsonDescriptors.append(descriptor)
10693 interface = interface.parent
10695 # Iterate the array in reverse: oldest ancestor first
10696 for descriptor in jsonDescriptors[::-1]:
10697 ret += fill(
10699 if (!${parentclass}::CollectJSONAttributes(cx, obj, MOZ_KnownLive(self), result)) {
10700 return false;
10702 """,
10703 parentclass=toBindingNamespace(descriptor.name),
10705 ret += "args.rval().setObject(*result);\n" "return true;\n"
10706 return ret
10709 class CGLegacyCallHook(CGAbstractBindingMethod):
10711 Call hook for our object
10714 def __init__(self, descriptor):
10715 self._legacycaller = descriptor.operations["LegacyCaller"]
10716 # Our "self" is actually the callee in this case, not the thisval.
10717 CGAbstractBindingMethod.__init__(
10718 self,
10719 descriptor,
10720 LEGACYCALLER_HOOK_NAME,
10721 JSNativeArguments(),
10722 getThisObj="&args.callee()",
10725 def define(self):
10726 if not self._legacycaller:
10727 return ""
10728 return CGAbstractBindingMethod.define(self)
10730 def generate_code(self):
10731 name = self._legacycaller.identifier.name
10732 nativeName = MakeNativeName(self.descriptor.binaryNameFor(name, False))
10733 return CGMethodCall(nativeName, False, self.descriptor, self._legacycaller)
10735 def error_reporting_label(self):
10736 # Should act like methods.
10737 return CGSpecializedMethod.error_reporting_label_helper(
10738 self.descriptor, self._legacycaller, isConstructor=False
10742 class CGResolveHook(CGAbstractClassHook):
10744 Resolve hook for objects that have the NeedResolve extended attribute.
10747 def __init__(self, descriptor):
10748 assert descriptor.interface.getExtendedAttribute("NeedResolve")
10750 args = [
10751 Argument("JSContext*", "cx"),
10752 Argument("JS::Handle<JSObject*>", "obj"),
10753 Argument("JS::Handle<jsid>", "id"),
10754 Argument("bool*", "resolvedp"),
10756 CGAbstractClassHook.__init__(self, descriptor, RESOLVE_HOOK_NAME, "bool", args)
10758 def generate_code(self):
10759 return dedent(
10761 JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> desc(cx);
10762 if (!self->DoResolve(cx, obj, id, &desc)) {
10763 return false;
10765 if (desc.isNothing()) {
10766 return true;
10768 // If desc.value() is undefined, then the DoResolve call
10769 // has already defined it on the object. Don't try to also
10770 // define it.
10771 MOZ_ASSERT(desc->isDataDescriptor());
10772 if (!desc->value().isUndefined()) {
10773 JS::Rooted<JS::PropertyDescriptor> defineDesc(cx, *desc);
10774 defineDesc.setResolving(true);
10775 if (!JS_DefinePropertyById(cx, obj, id, defineDesc)) {
10776 return false;
10779 *resolvedp = true;
10780 return true;
10784 def definition_body(self):
10785 if self.descriptor.isGlobal():
10786 # Resolve standard classes
10787 prefix = dedent(
10789 if (!ResolveGlobal(cx, obj, id, resolvedp)) {
10790 return false;
10792 if (*resolvedp) {
10793 return true;
10798 else:
10799 prefix = ""
10800 return prefix + CGAbstractClassHook.definition_body(self)
10803 class CGMayResolveHook(CGAbstractStaticMethod):
10805 Resolve hook for objects that have the NeedResolve extended attribute.
10808 def __init__(self, descriptor):
10809 assert descriptor.interface.getExtendedAttribute("NeedResolve")
10811 args = [
10812 Argument("const JSAtomState&", "names"),
10813 Argument("jsid", "id"),
10814 Argument("JSObject*", "maybeObj"),
10816 CGAbstractStaticMethod.__init__(
10817 self, descriptor, MAY_RESOLVE_HOOK_NAME, "bool", args
10820 def definition_body(self):
10821 if self.descriptor.isGlobal():
10822 # Check whether this would resolve as a standard class.
10823 prefix = dedent(
10825 if (MayResolveGlobal(names, id, maybeObj)) {
10826 return true;
10831 else:
10832 prefix = ""
10833 return prefix + "return %s::MayResolve(id);\n" % self.descriptor.nativeType
10836 class CGEnumerateHook(CGAbstractBindingMethod):
10838 Enumerate hook for objects with custom hooks.
10841 def __init__(self, descriptor):
10842 assert descriptor.interface.getExtendedAttribute("NeedResolve")
10844 args = [
10845 Argument("JSContext*", "cx"),
10846 Argument("JS::Handle<JSObject*>", "obj"),
10847 Argument("JS::MutableHandleVector<jsid>", "properties"),
10848 Argument("bool", "enumerableOnly"),
10850 # Our "self" is actually the "obj" argument in this case, not the thisval.
10851 CGAbstractBindingMethod.__init__(
10852 self, descriptor, NEW_ENUMERATE_HOOK_NAME, args, getThisObj="", callArgs=""
10855 def generate_code(self):
10856 return CGGeneric(
10857 dedent(
10859 FastErrorResult rv;
10860 self->GetOwnPropertyNames(cx, properties, enumerableOnly, rv);
10861 if (rv.MaybeSetPendingException(cx)) {
10862 return false;
10864 return true;
10869 def definition_body(self):
10870 if self.descriptor.isGlobal():
10871 # Enumerate standard classes
10872 prefix = dedent(
10874 if (!EnumerateGlobal(cx, obj, properties, enumerableOnly)) {
10875 return false;
10880 else:
10881 prefix = ""
10882 return prefix + CGAbstractBindingMethod.definition_body(self)
10885 class CppKeywords:
10887 A class for checking if method names declared in webidl
10888 are not in conflict with C++ keywords.
10891 keywords = frozenset(
10893 "alignas",
10894 "alignof",
10895 "and",
10896 "and_eq",
10897 "asm",
10898 "assert",
10899 "auto",
10900 "bitand",
10901 "bitor",
10902 "bool",
10903 "break",
10904 "case",
10905 "catch",
10906 "char",
10907 "char16_t",
10908 "char32_t",
10909 "class",
10910 "compl",
10911 "const",
10912 "constexpr",
10913 "const_cast",
10914 "continue",
10915 "decltype",
10916 "default",
10917 "delete",
10918 "do",
10919 "double",
10920 "dynamic_cast",
10921 "else",
10922 "enum",
10923 "explicit",
10924 "export",
10925 "extern",
10926 "false",
10927 "final",
10928 "float",
10929 "for",
10930 "friend",
10931 "goto",
10932 "if",
10933 "inline",
10934 "int",
10935 "long",
10936 "mutable",
10937 "namespace",
10938 "new",
10939 "noexcept",
10940 "not",
10941 "not_eq",
10942 "nullptr",
10943 "operator",
10944 "or",
10945 "or_eq",
10946 "override",
10947 "private",
10948 "protected",
10949 "public",
10950 "register",
10951 "reinterpret_cast",
10952 "return",
10953 "short",
10954 "signed",
10955 "sizeof",
10956 "static",
10957 "static_assert",
10958 "static_cast",
10959 "struct",
10960 "switch",
10961 "template",
10962 "this",
10963 "thread_local",
10964 "throw",
10965 "true",
10966 "try",
10967 "typedef",
10968 "typeid",
10969 "typename",
10970 "union",
10971 "unsigned",
10972 "using",
10973 "virtual",
10974 "void",
10975 "volatile",
10976 "wchar_t",
10977 "while",
10978 "xor",
10979 "xor_eq",
10983 @staticmethod
10984 def checkMethodName(name):
10985 # Double '_' because 'assert' and '_assert' cannot be used in MS2013 compiler.
10986 # Bug 964892 and bug 963560.
10987 if name in CppKeywords.keywords:
10988 name = "_" + name + "_"
10989 return name
10992 class CGStaticMethod(CGAbstractStaticBindingMethod):
10994 A class for generating the C++ code for an IDL static method.
10997 def __init__(self, descriptor, method):
10998 self.method = method
10999 name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name))
11000 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
11002 def generate_code(self):
11003 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method)
11004 return CGMethodCall(nativeName, True, self.descriptor, self.method)
11006 def auto_profiler_label(self):
11007 interface_name = self.descriptor.interface.identifier.name
11008 method_name = self.method.identifier.name
11009 return fill(
11011 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11012 "${interface_name}", "${method_name}", DOM, cx,
11013 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_METHOD) |
11014 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11015 """,
11016 interface_name=interface_name,
11017 method_name=method_name,
11020 def error_reporting_label(self):
11021 return CGSpecializedMethod.error_reporting_label_helper(
11022 self.descriptor, self.method, isConstructor=False
11026 class CGSpecializedGetterCommon(CGAbstractStaticMethod):
11028 A class for generating the code for a specialized attribute getter
11029 that the JIT can call with lower overhead.
11032 def __init__(
11033 self,
11034 descriptor,
11035 name,
11036 nativeName,
11037 attr,
11038 args,
11039 errorReportingLabel=None,
11040 additionalArg=None,
11042 self.nativeName = nativeName
11043 self.errorReportingLabel = errorReportingLabel
11044 self.additionalArgs = [] if additionalArg is None else [additionalArg]
11045 # StoreInSlot attributes have their getters called from Wrap(). We
11046 # really hope they can't run script, and don't want to annotate Wrap()
11047 # methods as doing that anyway, so let's not annotate them as
11048 # MOZ_CAN_RUN_SCRIPT.
11049 CGAbstractStaticMethod.__init__(
11050 self,
11051 descriptor,
11052 name,
11053 "bool",
11054 args + self.additionalArgs,
11055 canRunScript=not attr.getExtendedAttribute("StoreInSlot"),
11058 def definition_body(self):
11059 prefix = fill(
11061 auto* self = static_cast<${nativeType}*>(void_self);
11062 """,
11063 nativeType=self.descriptor.nativeType,
11066 if self.attr.isMaplikeOrSetlikeAttr():
11067 assert not self.attr.getExtendedAttribute("CrossOriginReadable")
11068 # If the interface is maplike/setlike, there will be one getter
11069 # method for the size property of the backing object. Due to having
11070 # to unpack the backing object from the slot, this requires its own
11071 # generator.
11072 return prefix + getMaplikeOrSetlikeSizeGetterBody(
11073 self.descriptor, self.attr
11076 if self.attr.type.isObservableArray():
11077 assert not self.attr.getExtendedAttribute("CrossOriginReadable")
11078 # If the attribute is observableArray, due to having to unpack the
11079 # backing object from the slot, this requires its own generator.
11080 return prefix + getObservableArrayGetterBody(self.descriptor, self.attr)
11082 if self.nativeName is None:
11083 nativeName = CGSpecializedGetterCommon.makeNativeName(
11084 self.descriptor, self.attr
11086 else:
11087 nativeName = self.nativeName
11089 type = self.attr.type
11090 if self.attr.getExtendedAttribute("CrossOriginReadable"):
11091 remoteType = type
11092 extendedAttributes = self.descriptor.getExtendedAttributes(
11093 self.attr, getter=True
11095 if (
11096 remoteType.isGeckoInterface()
11097 and not remoteType.unroll().inner.isExternal()
11098 and remoteType.unroll().inner.getExtendedAttribute("ChromeOnly") is None
11100 # We'll use a JSObject. It might make more sense to use remoteType's
11101 # RemoteProxy, but it's not easy to construct a type for that from here.
11102 remoteType = BuiltinTypes[IDLBuiltinType.Types.object]
11103 if "needsErrorResult" not in extendedAttributes:
11104 extendedAttributes.append("needsErrorResult")
11105 prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
11106 prefix = (
11107 fill(
11109 if (IsRemoteObjectProxy(obj, ${prototypeID})) {
11110 ${nativeType}::RemoteProxy* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
11111 $*{call}
11113 """,
11114 prototypeID=prototypeID,
11115 nativeType=self.descriptor.nativeType,
11116 call=CGGetterCall(
11117 remoteType,
11118 nativeName,
11119 self.descriptor,
11120 self.attr,
11121 self.errorReportingLabel,
11122 argsPre=[a.name for a in self.additionalArgs],
11123 dontSetSlot=True,
11124 extendedAttributes=extendedAttributes,
11125 ).define(),
11127 + prefix
11130 if self.attr.slotIndices is not None:
11131 # We're going to store this return value in a slot on some object,
11132 # to cache it. The question is, which object? For dictionary and
11133 # sequence return values, we want to use a slot on the Xray expando
11134 # if we're called via Xrays, and a slot on our reflector otherwise.
11135 # On the other hand, when dealing with some interfacce types
11136 # (e.g. window.document) we want to avoid calling the getter more
11137 # than once. In the case of window.document, it's because the
11138 # getter can start returning null, which would get hidden in the
11139 # non-Xray case by the fact that it's [StoreOnSlot], so the cached
11140 # version is always around.
11142 # The upshot is that we use the reflector slot for any getter whose
11143 # type is a gecko interface, whether we're called via Xrays or not.
11144 # Since [Cached] and [StoreInSlot] cannot be used with "NewObject",
11145 # we know that in the interface type case the returned object is
11146 # wrappercached. So creating Xrays to it is reasonable.
11147 if mayUseXrayExpandoSlots(self.descriptor, self.attr):
11148 prefix += fill(
11150 // Have to either root across the getter call or reget after.
11151 bool isXray;
11152 JS::Rooted<JSObject*> slotStorage(cx, GetCachedSlotStorageObject(cx, obj, &isXray));
11153 if (!slotStorage) {
11154 return false;
11156 const size_t slotIndex = isXray ? ${xraySlotIndex} : ${slotIndex};
11157 """,
11158 xraySlotIndex=memberXrayExpandoReservedSlot(
11159 self.attr, self.descriptor
11161 slotIndex=memberReservedSlot(self.attr, self.descriptor),
11163 else:
11164 prefix += fill(
11166 // Have to either root across the getter call or reget after.
11167 JS::Rooted<JSObject*> slotStorage(cx, js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false));
11168 MOZ_ASSERT(IsDOMObject(slotStorage));
11169 const size_t slotIndex = ${slotIndex};
11170 """,
11171 slotIndex=memberReservedSlot(self.attr, self.descriptor),
11174 prefix += fill(
11176 MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(JS::GetClass(slotStorage)) > slotIndex);
11178 // Scope for cachedVal
11179 JS::Value cachedVal = JS::GetReservedSlot(slotStorage, slotIndex);
11180 if (!cachedVal.isUndefined()) {
11181 args.rval().set(cachedVal);
11182 // The cached value is in the compartment of slotStorage,
11183 // so wrap into the caller compartment as needed.
11184 return ${maybeWrap}(cx, args.rval());
11188 """,
11189 maybeWrap=getMaybeWrapValueFuncForType(self.attr.type),
11192 return (
11193 prefix
11194 + CGGetterCall(
11195 type,
11196 nativeName,
11197 self.descriptor,
11198 self.attr,
11199 self.errorReportingLabel,
11200 argsPre=[a.name for a in self.additionalArgs],
11201 ).define()
11204 def auto_profiler_label(self, profilerLabel=None):
11205 if profilerLabel is None:
11206 profilerLabel = '"' + self.attr.identifier.name + '"'
11207 interface_name = self.descriptor.interface.identifier.name
11208 return fill(
11210 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11211 "${interface_name}", ${attr_name}, DOM, cx,
11212 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER) |
11213 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11214 """,
11215 interface_name=interface_name,
11216 attr_name=profilerLabel,
11219 def error_reporting_label(self):
11220 # Getters never need a BindingCallContext.
11221 return None
11223 @staticmethod
11224 def makeNativeName(descriptor, attr):
11225 name = attr.identifier.name
11226 nativeName = MakeNativeName(descriptor.binaryNameFor(name, attr.isStatic()))
11227 _, resultOutParam, _, _, _ = getRetvalDeclarationForType(attr.type, descriptor)
11228 extendedAttrs = descriptor.getExtendedAttributes(attr, getter=True)
11229 canFail = "needsErrorResult" in extendedAttrs or "canOOM" in extendedAttrs
11230 if resultOutParam or attr.type.nullable() or canFail:
11231 nativeName = "Get" + nativeName
11232 return nativeName
11235 class CGSpecializedGetter(CGSpecializedGetterCommon):
11237 A class for generating the code for a specialized attribute getter
11238 that the JIT can call with lower overhead.
11241 def __init__(self, descriptor, attr):
11242 self.attr = attr
11243 name = "get_" + IDLToCIdentifier(attr.identifier.name)
11244 args = [
11245 Argument("JSContext*", "cx"),
11246 Argument("JS::Handle<JSObject*>", "obj"),
11247 Argument("void*", "void_self"),
11248 Argument("JSJitGetterCallArgs", "args"),
11250 CGSpecializedGetterCommon.__init__(self, descriptor, name, None, attr, args)
11253 class CGTemplateForSpecializedGetter(CGSpecializedGetterCommon):
11255 A class for generating the code for a specialized attribute getter
11256 that can be used as the common getter that templated attribute
11257 getters can forward to.
11260 def __init__(self, descriptor, template):
11261 self.attr = template.attr
11262 self.attrNameString = template.attrNameString
11263 args = [
11264 Argument("JSContext*", "cx"),
11265 Argument("JS::Handle<JSObject*>", "obj"),
11266 Argument("void*", "void_self"),
11267 Argument("JSJitGetterCallArgs", "args"),
11269 errorDescription = (
11270 'ErrorDescriptionFor<ErrorFor::getter>{ "%s", attrName }'
11271 % descriptor.interface.identifier.name
11273 CGSpecializedGetterCommon.__init__(
11274 self,
11275 descriptor,
11276 template.getter,
11277 template.getter,
11278 self.attr,
11279 args,
11280 errorReportingLabel=errorDescription,
11281 additionalArg=Argument(template.argument.type, template.argument.name),
11284 def auto_profiler_label(self):
11285 return (
11286 fill(
11288 const char* attrName = ${attrNameString};
11289 """,
11290 attrNameString=self.attrNameString,
11292 + CGSpecializedGetterCommon.auto_profiler_label(self, "attrName")
11296 class CGSpecializedTemplatedGetter(CGAbstractStaticMethod):
11298 A class for generating the code for a specialized templated attribute
11299 getter that forwards to a common template getter.
11302 def __init__(self, descriptor, attr, template, additionalArg):
11303 self.attr = attr
11304 self.template = template
11305 self.additionalArg = additionalArg
11306 name = "get_" + IDLToCIdentifier(attr.identifier.name)
11307 args = [
11308 Argument("JSContext*", "cx"),
11309 Argument("JS::Handle<JSObject*>", "obj"),
11310 Argument("void*", "void_self"),
11311 Argument("JSJitGetterCallArgs", "args"),
11313 assert not attr.getExtendedAttribute("StoreInSlot")
11314 CGAbstractStaticMethod.__init__(
11315 self,
11316 descriptor,
11317 name,
11318 "bool",
11319 args,
11320 canRunScript=True,
11323 def definition_body(self):
11324 if self.additionalArg is None:
11325 additionalArg = self.attr.identifier.name
11326 else:
11327 additionalArg = self.additionalArg
11329 return fill(
11331 return ${namespace}::${getter}(cx, obj, void_self, args, ${additionalArg});
11332 """,
11333 namespace=toBindingNamespace(
11334 self.template.descriptor.interface.identifier.name
11336 getter=self.template.getter,
11337 additionalArg=additionalArg,
11341 class CGGetterPromiseWrapper(CGAbstractStaticMethod):
11343 A class for generating a wrapper around another getter that will
11344 convert exceptions to promises.
11347 def __init__(self, descriptor, getterToWrap):
11348 self.getter = getterToWrap
11349 name = self.makeName(getterToWrap.name)
11350 args = list(getterToWrap.args)
11351 CGAbstractStaticMethod.__init__(
11352 self, descriptor, name, "bool", args, canRunScript=True
11355 def definition_body(self):
11356 return fill(
11358 bool ok = ${getterName}(${args});
11359 if (ok) {
11360 return true;
11362 return ConvertExceptionToPromise(cx, args.rval());
11363 """,
11364 getterName=self.getter.name,
11365 args=", ".join(arg.name for arg in self.args),
11368 @staticmethod
11369 def makeName(getterName):
11370 return getterName + "_promiseWrapper"
11373 class CGStaticGetter(CGAbstractStaticBindingMethod):
11375 A class for generating the C++ code for an IDL static attribute getter.
11378 def __init__(self, descriptor, attr):
11379 self.attr = attr
11380 name = "get_" + IDLToCIdentifier(attr.identifier.name)
11381 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
11383 def generate_code(self):
11384 nativeName = CGSpecializedGetterCommon.makeNativeName(
11385 self.descriptor, self.attr
11387 return CGGetterCall(self.attr.type, nativeName, self.descriptor, self.attr)
11389 def auto_profiler_label(self):
11390 interface_name = self.descriptor.interface.identifier.name
11391 attr_name = self.attr.identifier.name
11392 return fill(
11394 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11395 "${interface_name}", "${attr_name}", DOM, cx,
11396 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER) |
11397 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11398 """,
11399 interface_name=interface_name,
11400 attr_name=attr_name,
11403 def error_reporting_label(self):
11404 # Getters never need a BindingCallContext.
11405 return None
11408 class CGSpecializedSetterCommon(CGAbstractStaticMethod):
11410 A class for generating the code for a specialized attribute setter
11411 that the JIT can call with lower overhead.
11414 def __init__(
11415 self,
11416 descriptor,
11417 name,
11418 nativeName,
11419 attr,
11420 args,
11421 errorReportingLabel=None,
11422 additionalArg=None,
11424 self.nativeName = nativeName
11425 self.errorReportingLabel = errorReportingLabel
11426 self.additionalArgs = [] if additionalArg is None else [additionalArg]
11427 CGAbstractStaticMethod.__init__(
11428 self,
11429 descriptor,
11430 name,
11431 "bool",
11432 args + self.additionalArgs,
11433 canRunScript=True,
11436 def definition_body(self):
11437 type = self.attr.type
11438 call = CGSetterCall(
11439 type,
11440 self.nativeName,
11441 self.descriptor,
11442 self.attr,
11443 self.errorReportingLabel,
11444 [a.name for a in self.additionalArgs],
11445 ).define()
11446 prefix = ""
11447 if self.attr.getExtendedAttribute("CrossOriginWritable"):
11448 if type.isGeckoInterface() and not type.unroll().inner.isExternal():
11449 # a setter taking a Gecko interface would require us to deal with remote
11450 # proxies for the value here.
11451 raise TypeError(
11452 "We don't support the setter of %s marked as "
11453 "CrossOriginWritable because it takes a Gecko interface "
11454 "as the value",
11455 self.attr.identifier.name,
11457 prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
11458 prefix = fill(
11460 if (IsRemoteObjectProxy(obj, ${prototypeID})) {
11461 auto* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
11462 $*{call}
11464 """,
11465 prototypeID=prototypeID,
11466 nativeType=self.descriptor.nativeType,
11467 call=call,
11470 return prefix + fill(
11472 auto* self = static_cast<${nativeType}*>(void_self);
11473 $*{call}
11474 """,
11475 nativeType=self.descriptor.nativeType,
11476 call=call,
11479 def auto_profiler_label(self, profilerLabel=None):
11480 interface_name = self.descriptor.interface.identifier.name
11481 if profilerLabel is None:
11482 profilerLabel = '"' + self.attr.identifier.name + '"'
11483 return fill(
11485 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11486 "${interface_name}", ${attr_name}, DOM, cx,
11487 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER) |
11488 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11489 """,
11490 interface_name=interface_name,
11491 attr_name=profilerLabel,
11494 @staticmethod
11495 def error_reporting_label_helper(descriptor, attr):
11496 # Setters need a BindingCallContext if the type of the attribute needs
11497 # one.
11498 if not idlTypeNeedsCallContext(
11499 attr.type, descriptor, allowTreatNonCallableAsNull=True
11501 return None
11502 return '"%s"' % (
11503 GetLabelForErrorReporting(descriptor, attr, isConstructor=False) + " setter"
11506 def error_reporting_label(self):
11507 errorReportingLabel = CGSpecializedSetterCommon.error_reporting_label_helper(
11508 self.descriptor, self.attr
11510 if errorReportingLabel is None:
11511 return None
11512 if self.errorReportingLabel:
11513 return self.errorReportingLabel
11514 return errorReportingLabel
11516 @staticmethod
11517 def makeNativeName(descriptor, attr):
11518 name = attr.identifier.name
11519 return "Set" + MakeNativeName(descriptor.binaryNameFor(name, attr.isStatic()))
11522 class CGSpecializedSetter(CGSpecializedSetterCommon):
11524 A class for generating the code for a specialized attribute setter
11525 that the JIT can call with lower overhead.
11528 def __init__(self, descriptor, attr):
11529 self.attr = attr
11530 name = "set_" + IDLToCIdentifier(attr.identifier.name)
11531 args = [
11532 Argument("JSContext*", "cx"),
11533 Argument("JS::Handle<JSObject*>", "obj"),
11534 Argument("void*", "void_self"),
11535 Argument("JSJitSetterCallArgs", "args"),
11537 CGSpecializedSetterCommon.__init__(
11538 self,
11539 descriptor,
11540 name,
11541 CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
11542 attr,
11543 args,
11547 class CGTemplateForSpecializedSetter(CGSpecializedSetterCommon):
11549 A class for generating the code for a specialized attribute setter
11550 that can be used as the common setter that templated attribute
11551 setters can forward to.
11554 def __init__(self, descriptor, template):
11555 self.attr = template.attr
11556 self.attrNameString = template.attrNameString
11557 args = [
11558 Argument("JSContext*", "cx"),
11559 Argument("JS::Handle<JSObject*>", "obj"),
11560 Argument("void*", "void_self"),
11561 Argument("JSJitSetterCallArgs", "args"),
11563 errorDescription = (
11564 'ErrorDescriptionFor<ErrorFor::setter>{ "%s", attrName }'
11565 % descriptor.interface.identifier.name
11567 CGSpecializedSetterCommon.__init__(
11568 self,
11569 descriptor,
11570 template.setter,
11571 template.setter,
11572 self.attr,
11573 args,
11574 errorReportingLabel=errorDescription,
11575 additionalArg=Argument(template.argument.type, template.argument.name),
11578 def auto_profiler_label(self):
11579 return (
11580 fill(
11582 const char* attrName = ${attrNameString};
11583 """,
11584 attrNameString=self.attrNameString,
11586 + CGSpecializedSetterCommon.auto_profiler_label(self, "attrName")
11590 class CGSpecializedTemplatedSetter(CGAbstractStaticMethod):
11592 A class for generating the code for a specialized templated attribute
11593 setter that forwards to a common template setter.
11596 def __init__(self, descriptor, attr, template, additionalArg):
11597 self.attr = attr
11598 self.template = template
11599 self.additionalArg = additionalArg
11600 name = "set_" + IDLToCIdentifier(attr.identifier.name)
11601 args = [
11602 Argument("JSContext*", "cx"),
11603 Argument("JS::Handle<JSObject*>", "obj"),
11604 Argument("void*", "void_self"),
11605 Argument("JSJitSetterCallArgs", "args"),
11607 CGAbstractStaticMethod.__init__(
11608 self, descriptor, name, "bool", args, canRunScript=True
11611 def definition_body(self):
11612 additionalArgs = []
11613 if self.additionalArg is None:
11614 additionalArgs.append(self.attr.identifier.name)
11615 else:
11616 additionalArgs.append(self.additionalArg)
11618 return fill(
11620 return ${namespace}::${setter}(cx, obj, void_self, args, ${additionalArgs});
11621 """,
11622 namespace=toBindingNamespace(
11623 self.template.descriptor.interface.identifier.name
11625 setter=self.template.setter,
11626 additionalArgs=", ".join(additionalArgs),
11630 class CGStaticSetter(CGAbstractStaticBindingMethod):
11632 A class for generating the C++ code for an IDL static attribute setter.
11635 def __init__(self, descriptor, attr):
11636 self.attr = attr
11637 name = "set_" + IDLToCIdentifier(attr.identifier.name)
11638 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
11640 def generate_code(self):
11641 nativeName = CGSpecializedSetterCommon.makeNativeName(
11642 self.descriptor, self.attr
11644 checkForArg = CGGeneric(
11645 fill(
11647 if (!args.requireAtLeast(cx, "${name} setter", 1)) {
11648 return false;
11650 """,
11651 name=self.attr.identifier.name,
11654 call = CGSetterCall(self.attr.type, nativeName, self.descriptor, self.attr)
11655 return CGList([checkForArg, call])
11657 def auto_profiler_label(self):
11658 interface_name = self.descriptor.interface.identifier.name
11659 attr_name = self.attr.identifier.name
11660 return fill(
11662 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11663 "${interface_name}", "${attr_name}", DOM, cx,
11664 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER) |
11665 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11666 """,
11667 interface_name=interface_name,
11668 attr_name=attr_name,
11671 def error_reporting_label(self):
11672 return CGSpecializedSetterCommon.error_reporting_label_helper(
11673 self.descriptor, self.attr
11677 class CGSpecializedForwardingSetter(CGSpecializedSetter):
11679 A class for generating the code for a specialized attribute setter with
11680 PutForwards that the JIT can call with lower overhead.
11683 def __init__(self, descriptor, attr):
11684 CGSpecializedSetter.__init__(self, descriptor, attr)
11686 def definition_body(self):
11687 attrName = self.attr.identifier.name
11688 forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0]
11689 # JS_GetProperty and JS_SetProperty can only deal with ASCII
11690 assert all(ord(c) < 128 for c in attrName)
11691 assert all(ord(c) < 128 for c in forwardToAttrName)
11692 return fill(
11694 JS::Rooted<JS::Value> v(cx);
11695 if (!JS_GetProperty(cx, obj, "${attr}", &v)) {
11696 return false;
11699 if (!v.isObject()) {
11700 return cx.ThrowErrorMessage<MSG_NOT_OBJECT>("${interface}.${attr}");
11703 JS::Rooted<JSObject*> targetObj(cx, &v.toObject());
11704 return JS_SetProperty(cx, targetObj, "${forwardToAttrName}", args[0]);
11705 """,
11706 attr=attrName,
11707 interface=self.descriptor.interface.identifier.name,
11708 forwardToAttrName=forwardToAttrName,
11711 def error_reporting_label(self):
11712 # We always need to be able to throw.
11713 return '"%s"' % (
11714 GetLabelForErrorReporting(self.descriptor, self.attr, isConstructor=False)
11715 + " setter"
11719 class CGSpecializedReplaceableSetter(CGSpecializedSetter):
11721 A class for generating the code for a specialized attribute setter with
11722 Replaceable that the JIT can call with lower overhead.
11725 def __init__(self, descriptor, attr):
11726 CGSpecializedSetter.__init__(self, descriptor, attr)
11728 def definition_body(self):
11729 attrName = self.attr.identifier.name
11730 # JS_DefineProperty can only deal with ASCII
11731 assert all(ord(c) < 128 for c in attrName)
11732 return (
11733 'return JS_DefineProperty(cx, obj, "%s", args[0], JSPROP_ENUMERATE);\n'
11734 % attrName
11737 def error_reporting_label(self):
11738 # We never throw directly.
11739 return None
11742 class CGSpecializedLenientSetter(CGSpecializedSetter):
11744 A class for generating the code for a specialized attribute setter with
11745 LenientSetter that the JIT can call with lower overhead.
11748 def __init__(self, descriptor, attr):
11749 CGSpecializedSetter.__init__(self, descriptor, attr)
11751 def definition_body(self):
11752 attrName = self.attr.identifier.name
11753 # JS_DefineProperty can only deal with ASCII
11754 assert all(ord(c) < 128 for c in attrName)
11755 return dedent(
11757 DeprecationWarning(cx, obj, DeprecatedOperations::eLenientSetter);
11758 return true;
11762 def error_reporting_label(self):
11763 # We never throw; that's the whole point.
11764 return None
11767 def memberReturnsNewObject(member):
11768 return member.getExtendedAttribute("NewObject") is not None
11771 class CGMemberJITInfo(CGThing):
11773 A class for generating the JITInfo for a property that points to
11774 our specialized getter and setter.
11777 def __init__(self, descriptor, member):
11778 self.member = member
11779 self.descriptor = descriptor
11781 def declare(self):
11782 return ""
11784 def defineJitInfo(
11785 self,
11786 infoName,
11787 opName,
11788 opType,
11789 infallible,
11790 movable,
11791 eliminatable,
11792 aliasSet,
11793 alwaysInSlot,
11794 lazilyInSlot,
11795 slotIndex,
11796 returnTypes,
11797 args,
11800 aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit.
11802 args is None if we don't want to output argTypes for some
11803 reason (e.g. we have overloads or we're not a method) and
11804 otherwise an iterable of the arguments for this method.
11806 assert (
11807 not movable or aliasSet != "AliasEverything"
11808 ) # Can't move write-aliasing things
11809 assert (
11810 not alwaysInSlot or movable
11811 ) # Things always in slots had better be movable
11812 assert (
11813 not eliminatable or aliasSet != "AliasEverything"
11814 ) # Can't eliminate write-aliasing things
11815 assert (
11816 not alwaysInSlot or eliminatable
11817 ) # Things always in slots had better be eliminatable
11819 def jitInfoInitializer(isTypedMethod):
11820 initializer = fill(
11823 { ${opName} },
11824 { prototypes::id::${name} },
11825 { PrototypeTraits<prototypes::id::${name}>::Depth },
11826 JSJitInfo::${opType},
11827 JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */
11828 ${returnType}, /* returnType. Not relevant for setters. */
11829 ${isInfallible}, /* isInfallible. False in setters. */
11830 ${isMovable}, /* isMovable. Not relevant for setters. */
11831 ${isEliminatable}, /* isEliminatable. Not relevant for setters. */
11832 ${isAlwaysInSlot}, /* isAlwaysInSlot. Only relevant for getters. */
11833 ${isLazilyCachedInSlot}, /* isLazilyCachedInSlot. Only relevant for getters. */
11834 ${isTypedMethod}, /* isTypedMethod. Only relevant for methods. */
11835 ${slotIndex} /* Reserved slot index, if we're stored in a slot, else 0. */
11837 """,
11838 opName=opName,
11839 name=self.descriptor.name,
11840 opType=opType,
11841 aliasSet=aliasSet,
11842 returnType=functools.reduce(
11843 CGMemberJITInfo.getSingleReturnType, returnTypes, ""
11845 isInfallible=toStringBool(infallible),
11846 isMovable=toStringBool(movable),
11847 isEliminatable=toStringBool(eliminatable),
11848 isAlwaysInSlot=toStringBool(alwaysInSlot),
11849 isLazilyCachedInSlot=toStringBool(lazilyInSlot),
11850 isTypedMethod=toStringBool(isTypedMethod),
11851 slotIndex=slotIndex,
11853 return initializer.rstrip()
11855 if alwaysInSlot or lazilyInSlot:
11856 slotAssert = fill(
11858 static_assert(${slotIndex} <= JSJitInfo::maxSlotIndex, "We won't fit");
11859 static_assert(${slotIndex} < ${classReservedSlots}, "There is no slot for us");
11860 """,
11861 slotIndex=slotIndex,
11862 classReservedSlots=INSTANCE_RESERVED_SLOTS
11863 + self.descriptor.interface.totalMembersInSlots,
11865 else:
11866 slotAssert = ""
11867 if args is not None:
11868 argTypes = "%s_argTypes" % infoName
11869 args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args]
11870 args.append("JSJitInfo::ArgTypeListEnd")
11871 argTypesDecl = "static const JSJitInfo::ArgType %s[] = { %s };\n" % (
11872 argTypes,
11873 ", ".join(args),
11875 return fill(
11877 $*{argTypesDecl}
11878 static const JSTypedMethodJitInfo ${infoName} = {
11879 ${jitInfo},
11880 ${argTypes}
11882 $*{slotAssert}
11883 """,
11884 argTypesDecl=argTypesDecl,
11885 infoName=infoName,
11886 jitInfo=indent(jitInfoInitializer(True)),
11887 argTypes=argTypes,
11888 slotAssert=slotAssert,
11891 # Unexposed things are meant to be used from C++ directly, so we make
11892 # their jitinfo non-static. That way C++ can get at it.
11893 if self.member.getExtendedAttribute("Unexposed"):
11894 storageClass = "extern"
11895 else:
11896 storageClass = "static"
11898 return fill(
11900 ${storageClass} const JSJitInfo ${infoName} = ${jitInfo};
11901 $*{slotAssert}
11902 """,
11903 storageClass=storageClass,
11904 infoName=infoName,
11905 jitInfo=jitInfoInitializer(False),
11906 slotAssert=slotAssert,
11909 def define(self):
11910 if self.member.isAttr():
11911 getterinfo = "%s_getterinfo" % IDLToCIdentifier(self.member.identifier.name)
11912 name = IDLToCIdentifier(self.member.identifier.name)
11913 if self.member.type.isPromise():
11914 name = CGGetterPromiseWrapper.makeName(name)
11915 getter = "get_%s" % name
11916 extendedAttrs = self.descriptor.getExtendedAttributes(
11917 self.member, getter=True
11919 getterinfal = "needsErrorResult" not in extendedAttrs
11921 # At this point getterinfal is true if our getter either can't throw
11922 # at all, or can only throw OOM. In both cases, it's safe to move,
11923 # or dead-code-eliminate, the getter, because throwing OOM is not
11924 # semantically meaningful, so code can't rely on it happening. Note
11925 # that this makes the behavior consistent for OOM thrown from the
11926 # getter itself and OOM thrown from the to-JS conversion of the
11927 # return value (see the "canOOM" and "infallibleForMember" checks
11928 # below).
11929 movable = self.mayBeMovable() and getterinfal
11930 eliminatable = self.mayBeEliminatable() and getterinfal
11931 aliasSet = self.aliasSet()
11933 # Now we have to set getterinfal to whether we can _really_ ever
11934 # throw, from the point of view of the JS engine.
11935 getterinfal = (
11936 getterinfal
11937 and "canOOM" not in extendedAttrs
11938 and infallibleForMember(self.member, self.member.type, self.descriptor)
11940 isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot")
11942 if self.member.slotIndices is not None:
11943 assert (
11944 isAlwaysInSlot
11945 or self.member.getExtendedAttribute("Cached")
11946 or self.member.type.isObservableArray()
11948 isLazilyCachedInSlot = not isAlwaysInSlot
11949 slotIndex = memberReservedSlot(self.member, self.descriptor)
11950 # We'll statically assert that this is not too big in
11951 # CGUpdateMemberSlotsMethod, in the case when
11952 # isAlwaysInSlot is true.
11953 else:
11954 isLazilyCachedInSlot = False
11955 slotIndex = "0"
11957 result = self.defineJitInfo(
11958 getterinfo,
11959 getter,
11960 "Getter",
11961 getterinfal,
11962 movable,
11963 eliminatable,
11964 aliasSet,
11965 isAlwaysInSlot,
11966 isLazilyCachedInSlot,
11967 slotIndex,
11968 [self.member.type],
11969 None,
11971 if (
11972 not self.member.readonly
11973 or self.member.getExtendedAttribute("PutForwards") is not None
11974 or self.member.getExtendedAttribute("Replaceable") is not None
11975 or self.member.getExtendedAttribute("LegacyLenientSetter") is not None
11977 setterinfo = "%s_setterinfo" % IDLToCIdentifier(
11978 self.member.identifier.name
11980 # Actually a JSJitSetterOp, but JSJitGetterOp is first in the
11981 # union.
11982 setter = "(JSJitGetterOp)set_%s" % IDLToCIdentifier(
11983 self.member.identifier.name
11985 # Setters are always fallible, since they have to do a typed unwrap.
11986 result += self.defineJitInfo(
11987 setterinfo,
11988 setter,
11989 "Setter",
11990 False,
11991 False,
11992 False,
11993 "AliasEverything",
11994 False,
11995 False,
11996 "0",
11997 [BuiltinTypes[IDLBuiltinType.Types.undefined]],
11998 None,
12000 return result
12001 if self.member.isMethod():
12002 methodinfo = "%s_methodinfo" % IDLToCIdentifier(self.member.identifier.name)
12003 name = CppKeywords.checkMethodName(
12004 IDLToCIdentifier(self.member.identifier.name)
12006 if self.member.returnsPromise():
12007 name = CGMethodPromiseWrapper.makeName(name)
12008 # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union.
12009 method = "(JSJitGetterOp)%s" % name
12011 # Methods are infallible if they are infallible, have no arguments
12012 # to unwrap, and have a return type that's infallible to wrap up for
12013 # return.
12014 sigs = self.member.signatures()
12015 if len(sigs) != 1:
12016 # Don't handle overloading. If there's more than one signature,
12017 # one of them must take arguments.
12018 methodInfal = False
12019 args = None
12020 movable = False
12021 eliminatable = False
12022 else:
12023 sig = sigs[0]
12024 # For methods that affect nothing, it's OK to set movable to our
12025 # notion of infallible on the C++ side, without considering
12026 # argument conversions, since argument conversions that can
12027 # reliably throw would be effectful anyway and the jit doesn't
12028 # move effectful things.
12029 extendedAttrs = self.descriptor.getExtendedAttributes(self.member)
12030 hasInfallibleImpl = "needsErrorResult" not in extendedAttrs
12031 # At this point hasInfallibleImpl is true if our method either
12032 # can't throw at all, or can only throw OOM. In both cases, it
12033 # may be safe to move, or dead-code-eliminate, the method,
12034 # because throwing OOM is not semantically meaningful, so code
12035 # can't rely on it happening. Note that this makes the behavior
12036 # consistent for OOM thrown from the method itself and OOM
12037 # thrown from the to-JS conversion of the return value (see the
12038 # "canOOM" and "infallibleForMember" checks below).
12039 movable = self.mayBeMovable() and hasInfallibleImpl
12040 eliminatable = self.mayBeEliminatable() and hasInfallibleImpl
12041 # XXXbz can we move the smarts about fallibility due to arg
12042 # conversions into the JIT, using our new args stuff?
12043 if len(sig[1]) != 0 or not infallibleForMember(
12044 self.member, sig[0], self.descriptor
12046 # We have arguments or our return-value boxing can fail
12047 methodInfal = False
12048 else:
12049 methodInfal = hasInfallibleImpl and "canOOM" not in extendedAttrs
12050 # For now, only bother to output args if we're side-effect-free.
12051 if self.member.affects == "Nothing":
12052 args = sig[1]
12053 else:
12054 args = None
12056 aliasSet = self.aliasSet()
12057 result = self.defineJitInfo(
12058 methodinfo,
12059 method,
12060 "Method",
12061 methodInfal,
12062 movable,
12063 eliminatable,
12064 aliasSet,
12065 False,
12066 False,
12067 "0",
12068 [s[0] for s in sigs],
12069 args,
12071 return result
12072 raise TypeError("Illegal member type to CGPropertyJITInfo")
12074 def mayBeMovable(self):
12076 Returns whether this attribute or method may be movable, just
12077 based on Affects/DependsOn annotations.
12079 affects = self.member.affects
12080 dependsOn = self.member.dependsOn
12081 assert affects in IDLInterfaceMember.AffectsValues
12082 assert dependsOn in IDLInterfaceMember.DependsOnValues
12083 # Things that are DependsOn=DeviceState are not movable, because we
12084 # don't want them coalesced with each other or loop-hoisted, since
12085 # their return value can change even if nothing is going on from our
12086 # point of view.
12087 return affects == "Nothing" and (
12088 dependsOn != "Everything" and dependsOn != "DeviceState"
12091 def mayBeEliminatable(self):
12093 Returns whether this attribute or method may be eliminatable, just
12094 based on Affects/DependsOn annotations.
12096 # dependsOn shouldn't affect this decision at all, except in jitinfo we
12097 # have no way to express "Depends on everything, affects nothing",
12098 # because we only have three alias set values: AliasNone ("depends on
12099 # nothing, affects nothing"), AliasDOMSets ("depends on DOM sets,
12100 # affects nothing"), AliasEverything ("depends on everything, affects
12101 # everything"). So the [Affects=Nothing, DependsOn=Everything] case
12102 # gets encoded as AliasEverything and defineJitInfo asserts that if our
12103 # alias state is AliasEverything then we're not eliminatable (because it
12104 # thinks we might have side-effects at that point). Bug 1155796 is
12105 # tracking possible solutions for this.
12106 affects = self.member.affects
12107 dependsOn = self.member.dependsOn
12108 assert affects in IDLInterfaceMember.AffectsValues
12109 assert dependsOn in IDLInterfaceMember.DependsOnValues
12110 return affects == "Nothing" and dependsOn != "Everything"
12112 def aliasSet(self):
12114 Returns the alias set to store in the jitinfo. This may not be the
12115 effective alias set the JIT uses, depending on whether we have enough
12116 information about our args to allow the JIT to prove that effectful
12117 argument conversions won't happen.
12119 dependsOn = self.member.dependsOn
12120 assert dependsOn in IDLInterfaceMember.DependsOnValues
12122 if dependsOn == "Nothing" or dependsOn == "DeviceState":
12123 assert self.member.affects == "Nothing"
12124 return "AliasNone"
12126 if dependsOn == "DOMState":
12127 assert self.member.affects == "Nothing"
12128 return "AliasDOMSets"
12130 return "AliasEverything"
12132 @staticmethod
12133 def getJSReturnTypeTag(t):
12134 if t.nullable():
12135 # Sometimes it might return null, sometimes not
12136 return "JSVAL_TYPE_UNKNOWN"
12137 if t.isUndefined():
12138 # No return, every time
12139 return "JSVAL_TYPE_UNDEFINED"
12140 if t.isSequence():
12141 return "JSVAL_TYPE_OBJECT"
12142 if t.isRecord():
12143 return "JSVAL_TYPE_OBJECT"
12144 if t.isPromise():
12145 return "JSVAL_TYPE_OBJECT"
12146 if t.isGeckoInterface():
12147 return "JSVAL_TYPE_OBJECT"
12148 if t.isString():
12149 return "JSVAL_TYPE_STRING"
12150 if t.isEnum():
12151 return "JSVAL_TYPE_STRING"
12152 if t.isCallback():
12153 return "JSVAL_TYPE_OBJECT"
12154 if t.isAny():
12155 # The whole point is to return various stuff
12156 return "JSVAL_TYPE_UNKNOWN"
12157 if t.isObject():
12158 return "JSVAL_TYPE_OBJECT"
12159 if t.isSpiderMonkeyInterface():
12160 return "JSVAL_TYPE_OBJECT"
12161 if t.isUnion():
12162 u = t.unroll()
12163 if u.hasNullableType:
12164 # Might be null or not
12165 return "JSVAL_TYPE_UNKNOWN"
12166 return functools.reduce(
12167 CGMemberJITInfo.getSingleReturnType, u.flatMemberTypes, ""
12169 if t.isDictionary():
12170 return "JSVAL_TYPE_OBJECT"
12171 if t.isObservableArray():
12172 return "JSVAL_TYPE_OBJECT"
12173 if not t.isPrimitive():
12174 raise TypeError("No idea what type " + str(t) + " is.")
12175 tag = t.tag()
12176 if tag == IDLType.Tags.bool:
12177 return "JSVAL_TYPE_BOOLEAN"
12178 if tag in [
12179 IDLType.Tags.int8,
12180 IDLType.Tags.uint8,
12181 IDLType.Tags.int16,
12182 IDLType.Tags.uint16,
12183 IDLType.Tags.int32,
12185 return "JSVAL_TYPE_INT32"
12186 if tag in [
12187 IDLType.Tags.int64,
12188 IDLType.Tags.uint64,
12189 IDLType.Tags.unrestricted_float,
12190 IDLType.Tags.float,
12191 IDLType.Tags.unrestricted_double,
12192 IDLType.Tags.double,
12194 # These all use JS_NumberValue, which can return int or double.
12195 # But TI treats "double" as meaning "int or double", so we're
12196 # good to return JSVAL_TYPE_DOUBLE here.
12197 return "JSVAL_TYPE_DOUBLE"
12198 if tag != IDLType.Tags.uint32:
12199 raise TypeError("No idea what type " + str(t) + " is.")
12200 # uint32 is sometimes int and sometimes double.
12201 return "JSVAL_TYPE_DOUBLE"
12203 @staticmethod
12204 def getSingleReturnType(existingType, t):
12205 type = CGMemberJITInfo.getJSReturnTypeTag(t)
12206 if existingType == "":
12207 # First element of the list; just return its type
12208 return type
12210 if type == existingType:
12211 return existingType
12212 if (type == "JSVAL_TYPE_DOUBLE" and existingType == "JSVAL_TYPE_INT32") or (
12213 existingType == "JSVAL_TYPE_DOUBLE" and type == "JSVAL_TYPE_INT32"
12215 # Promote INT32 to DOUBLE as needed
12216 return "JSVAL_TYPE_DOUBLE"
12217 # Different types
12218 return "JSVAL_TYPE_UNKNOWN"
12220 @staticmethod
12221 def getJSArgType(t):
12222 assert not t.isUndefined()
12223 if t.nullable():
12224 # Sometimes it might return null, sometimes not
12225 return (
12226 "JSJitInfo::ArgType(JSJitInfo::Null | %s)"
12227 % CGMemberJITInfo.getJSArgType(t.inner)
12229 if t.isSequence():
12230 return "JSJitInfo::Object"
12231 if t.isPromise():
12232 return "JSJitInfo::Object"
12233 if t.isGeckoInterface():
12234 return "JSJitInfo::Object"
12235 if t.isString():
12236 return "JSJitInfo::String"
12237 if t.isEnum():
12238 return "JSJitInfo::String"
12239 if t.isCallback():
12240 return "JSJitInfo::Object"
12241 if t.isAny():
12242 # The whole point is to return various stuff
12243 return "JSJitInfo::Any"
12244 if t.isObject():
12245 return "JSJitInfo::Object"
12246 if t.isSpiderMonkeyInterface():
12247 return "JSJitInfo::Object"
12248 if t.isUnion():
12249 u = t.unroll()
12250 type = "JSJitInfo::Null" if u.hasNullableType else ""
12251 return "JSJitInfo::ArgType(%s)" % functools.reduce(
12252 CGMemberJITInfo.getSingleArgType, u.flatMemberTypes, type
12254 if t.isDictionary():
12255 return "JSJitInfo::Object"
12256 if not t.isPrimitive():
12257 raise TypeError("No idea what type " + str(t) + " is.")
12258 tag = t.tag()
12259 if tag == IDLType.Tags.bool:
12260 return "JSJitInfo::Boolean"
12261 if tag in [
12262 IDLType.Tags.int8,
12263 IDLType.Tags.uint8,
12264 IDLType.Tags.int16,
12265 IDLType.Tags.uint16,
12266 IDLType.Tags.int32,
12268 return "JSJitInfo::Integer"
12269 if tag in [
12270 IDLType.Tags.int64,
12271 IDLType.Tags.uint64,
12272 IDLType.Tags.unrestricted_float,
12273 IDLType.Tags.float,
12274 IDLType.Tags.unrestricted_double,
12275 IDLType.Tags.double,
12277 # These all use JS_NumberValue, which can return int or double.
12278 # But TI treats "double" as meaning "int or double", so we're
12279 # good to return JSVAL_TYPE_DOUBLE here.
12280 return "JSJitInfo::Double"
12281 if tag != IDLType.Tags.uint32:
12282 raise TypeError("No idea what type " + str(t) + " is.")
12283 # uint32 is sometimes int and sometimes double.
12284 return "JSJitInfo::Double"
12286 @staticmethod
12287 def getSingleArgType(existingType, t):
12288 type = CGMemberJITInfo.getJSArgType(t)
12289 if existingType == "":
12290 # First element of the list; just return its type
12291 return type
12293 if type == existingType:
12294 return existingType
12295 return "%s | %s" % (existingType, type)
12298 class CGStaticMethodJitinfo(CGGeneric):
12300 A class for generating the JITInfo for a promise-returning static method.
12303 def __init__(self, method):
12304 CGGeneric.__init__(
12305 self,
12306 "\n"
12307 "static const JSJitInfo %s_methodinfo = {\n"
12308 " { (JSJitGetterOp)%s },\n"
12309 " { prototypes::id::_ID_Count }, { 0 }, JSJitInfo::StaticMethod,\n"
12310 " JSJitInfo::AliasEverything, JSVAL_TYPE_OBJECT, false, false,\n"
12311 " false, false, 0\n"
12312 "};\n"
12314 IDLToCIdentifier(method.identifier.name),
12315 CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name)),
12320 def getEnumValueName(value):
12321 # Some enum values can be empty strings. Others might have weird
12322 # characters in them. Deal with the former by returning "_empty",
12323 # deal with possible name collisions from that by throwing if the
12324 # enum value is actually "_empty", and throw on any value
12325 # containing non-ASCII chars for now. Replace all chars other than
12326 # [0-9A-Za-z_] with '_'.
12327 if re.match("[^\x20-\x7E]", value):
12328 raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters')
12329 if re.match("^[0-9]", value):
12330 value = "_" + value
12331 value = re.sub(r"[^0-9A-Za-z_]", "_", value)
12332 if re.match("^_[A-Z]|__", value):
12333 raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec')
12334 if value == "_empty":
12335 raise SyntaxError('"_empty" is not an IDL enum value we support yet')
12336 if value == "":
12337 return "_empty"
12338 return MakeNativeName(value)
12341 class CGEnumToJSValue(CGAbstractMethod):
12342 def __init__(self, enum):
12343 enumType = enum.identifier.name
12344 self.stringsArray = "binding_detail::EnumStrings<" + enumType + ">::Values"
12345 CGAbstractMethod.__init__(
12346 self,
12347 None,
12348 "ToJSValue",
12349 "bool",
12351 Argument("JSContext*", "aCx"),
12352 Argument(enumType, "aArgument"),
12353 Argument("JS::MutableHandle<JS::Value>", "aValue"),
12357 def definition_body(self):
12358 return fill(
12360 MOZ_ASSERT(uint32_t(aArgument) < ArrayLength(${strings}));
12361 JSString* resultStr =
12362 JS_NewStringCopyN(aCx, ${strings}[uint32_t(aArgument)].BeginReading(),
12363 ${strings}[uint32_t(aArgument)].Length());
12364 if (!resultStr) {
12365 return false;
12367 aValue.setString(resultStr);
12368 return true;
12369 """,
12370 strings=self.stringsArray,
12374 class CGEnum(CGThing):
12375 def __init__(self, enum):
12376 CGThing.__init__(self)
12377 self.enum = enum
12378 strings = CGNamespace(
12379 "binding_detail",
12380 CGGeneric(
12381 declare=fill(
12383 template <> struct EnumStrings<${name}> {
12384 static const nsLiteralCString Values[${count}];
12386 """,
12387 name=self.enum.identifier.name,
12388 count=self.nEnumStrings(),
12390 define=fill(
12392 const nsLiteralCString EnumStrings<${name}>::Values[${count}] = {
12393 $*{entries}
12395 """,
12396 name=self.enum.identifier.name,
12397 count=self.nEnumStrings(),
12398 entries="".join('"%s"_ns,\n' % val for val in self.enum.values()),
12402 toJSValue = CGEnumToJSValue(enum)
12403 self.cgThings = CGList([strings, toJSValue], "\n")
12405 def nEnumStrings(self):
12406 return len(self.enum.values())
12408 @staticmethod
12409 def underlyingType(enum):
12410 count = len(enum.values())
12411 if count <= 256:
12412 return "uint8_t"
12413 if count <= 65536:
12414 return "uint16_t"
12415 raise ValueError("Enum " + enum.identifier.name + " has more than 65536 values")
12417 def declare(self):
12418 decl = fill(
12420 enum class ${name} : ${ty} {
12421 $*{enums}
12423 """,
12424 name=self.enum.identifier.name,
12425 ty=CGEnum.underlyingType(self.enum),
12426 enums=",\n".join(map(getEnumValueName, self.enum.values())) + ",\n",
12429 return decl + "\n" + self.cgThings.declare()
12431 def define(self):
12432 return self.cgThings.define()
12434 def deps(self):
12435 return self.enum.getDeps()
12438 class CGMaxContiguousEnumValue(CGThing):
12439 def __init__(self, enum):
12440 CGThing.__init__(self)
12441 self.enum = enum
12443 def declare(self):
12444 enumValues = self.enum.values()
12445 return fill(
12447 template <>
12448 struct MaxContiguousEnumValue<dom::${name}>
12450 static constexpr dom::${name} value = dom::${name}::${maxValue};
12452 static_assert(static_cast<${ty}>(dom::${name}::${minValue}) == 0,
12453 "We rely on this in ContiguousEnumValues");
12454 static_assert(mozilla::ArrayLength(dom::binding_detail::EnumStrings<dom::${name}>::Values) - 1 == UnderlyingValue(value),
12455 "Mismatch between enum strings and enum count");
12457 """,
12458 name=self.enum.identifier.name,
12459 ty=CGEnum.underlyingType(self.enum),
12460 maxValue=getEnumValueName(enumValues[-1]),
12461 minValue=getEnumValueName(enumValues[0]),
12464 def define(self):
12465 return ""
12467 def deps(self):
12468 return self.enum.getDeps()
12471 def getUnionAccessorSignatureType(type, descriptorProvider):
12473 Returns the types that are used in the getter and setter signatures for
12474 union types
12476 # Flat member types have already unwrapped nullables.
12477 assert not type.nullable()
12479 # Promise types can never appear in unions, because Promise is not
12480 # distinguishable from anything.
12481 assert not type.isPromise()
12483 if type.isSequence() or type.isRecord():
12484 if type.isSequence():
12485 wrapperType = "Sequence"
12486 else:
12487 wrapperType = "Record"
12488 # We don't use the returned template here, so it's OK to just pass no
12489 # sourceDescription.
12490 elementInfo = getJSToNativeConversionInfo(
12491 type.inner, descriptorProvider, isMember=wrapperType
12493 if wrapperType == "Sequence":
12494 innerType = elementInfo.declType
12495 else:
12496 innerType = [recordKeyDeclType(type), elementInfo.declType]
12498 return CGTemplatedType(wrapperType, innerType, isConst=True, isReference=True)
12500 # Nested unions are unwrapped automatically into our flatMemberTypes.
12501 assert not type.isUnion()
12503 if type.isGeckoInterface():
12504 descriptor = descriptorProvider.getDescriptor(
12505 type.unroll().inner.identifier.name
12507 typeName = CGGeneric(descriptor.nativeType)
12508 if not type.unroll().inner.isExternal():
12509 typeName = CGWrapper(typeName, post="&")
12510 elif descriptor.interface.identifier.name == "WindowProxy":
12511 typeName = CGGeneric("WindowProxyHolder const&")
12512 else:
12513 # Allow null pointers for old-binding classes.
12514 typeName = CGWrapper(typeName, post="*")
12515 return typeName
12517 if type.isSpiderMonkeyInterface():
12518 typeName = CGGeneric(type.name)
12519 return CGWrapper(typeName, post=" const &")
12521 if type.isJSString():
12522 raise TypeError("JSString not supported in unions")
12524 if type.isDOMString() or type.isUSVString():
12525 return CGGeneric("const nsAString&")
12527 if type.isUTF8String():
12528 return CGGeneric("const nsACString&")
12530 if type.isByteString():
12531 return CGGeneric("const nsCString&")
12533 if type.isEnum():
12534 return CGGeneric(type.inner.identifier.name)
12536 if type.isCallback():
12537 return CGGeneric("%s&" % type.unroll().callback.identifier.name)
12539 if type.isAny():
12540 return CGGeneric("JS::Value")
12542 if type.isObject():
12543 return CGGeneric("JSObject*")
12545 if type.isDictionary():
12546 return CGGeneric("const %s&" % type.inner.identifier.name)
12548 if not type.isPrimitive():
12549 raise TypeError("Need native type for argument type '%s'" % str(type))
12551 return CGGeneric(builtinNames[type.tag()])
12554 def getUnionTypeTemplateVars(unionType, type, descriptorProvider, isMember=False):
12555 assert not type.isUndefined()
12556 assert not isMember or isMember in ("Union", "OwningUnion")
12558 ownsMembers = isMember == "OwningUnion"
12559 name = getUnionMemberName(type)
12560 holderName = "m" + name + "Holder"
12562 # By the time tryNextCode is invoked, we're guaranteed the union has been
12563 # constructed as some type, since we've been trying to convert into the
12564 # corresponding member.
12565 tryNextCode = fill(
12567 Destroy${name}();
12568 tryNext = true;
12569 return true;
12570 """,
12571 name=name,
12574 sourceDescription = "%s branch of %s" % (type.prettyName(), unionType.prettyName())
12576 conversionInfo = getJSToNativeConversionInfo(
12577 type,
12578 descriptorProvider,
12579 failureCode=tryNextCode,
12580 isDefinitelyObject=not type.isDictionary(),
12581 isMember=isMember,
12582 sourceDescription=sourceDescription,
12585 ctorNeedsCx = conversionInfo.declArgs == "cx"
12586 ctorArgs = "cx" if ctorNeedsCx else ""
12588 structType = conversionInfo.declType.define()
12589 externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
12591 if type.isObject():
12592 if ownsMembers:
12593 setValue = "mValue.mObject.SetValue(obj);"
12594 else:
12595 setValue = "mValue.mObject.SetValue(cx, obj);"
12597 body = fill(
12599 MOZ_ASSERT(mType == eUninitialized);
12600 ${setValue}
12601 mType = eObject;
12602 """,
12603 setValue=setValue,
12606 # It's a bit sketchy to do the security check after setting the value,
12607 # but it keeps the code cleaner and lets us avoid rooting |obj| over the
12608 # call to CallerSubsumes().
12609 body = body + fill(
12611 if (passedToJSImpl && !CallerSubsumes(obj)) {
12612 cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}");
12613 return false;
12615 return true;
12616 """,
12617 sourceDescription=sourceDescription,
12620 setters = [
12621 ClassMethod(
12622 "SetToObject",
12623 "bool",
12625 Argument("BindingCallContext&", "cx"),
12626 Argument("JSObject*", "obj"),
12627 Argument("bool", "passedToJSImpl", default="false"),
12629 inline=True,
12630 bodyInHeader=True,
12631 body=body,
12634 elif type.isDictionary() and not type.inner.needsConversionFromJS:
12635 # In this case we are never initialized from JS to start with
12636 setters = None
12637 else:
12638 # Important: we need to not have our declName involve
12639 # maybe-GCing operations.
12640 jsConversion = fill(
12641 conversionInfo.template,
12642 val="value",
12643 maybeMutableVal="value",
12644 declName="memberSlot",
12645 holderName=(holderName if ownsMembers else "%s.ref()" % holderName),
12646 passedToJSImpl="passedToJSImpl",
12649 jsConversion = fill(
12651 tryNext = false;
12652 { // scope for memberSlot
12653 ${structType}& memberSlot = RawSetAs${name}(${ctorArgs});
12654 $*{jsConversion}
12656 return true;
12657 """,
12658 structType=structType,
12659 name=name,
12660 ctorArgs=ctorArgs,
12661 jsConversion=jsConversion,
12664 needCallContext = idlTypeNeedsCallContext(type)
12665 if needCallContext:
12666 cxType = "BindingCallContext&"
12667 else:
12668 cxType = "JSContext*"
12669 setters = [
12670 ClassMethod(
12671 "TrySetTo" + name,
12672 "bool",
12674 Argument(cxType, "cx"),
12675 Argument("JS::Handle<JS::Value>", "value"),
12676 Argument("bool&", "tryNext"),
12677 Argument("bool", "passedToJSImpl", default="false"),
12679 visibility="private",
12680 body=jsConversion,
12683 if needCallContext:
12684 # Add a method for non-binding uses of unions to allow them to set
12685 # things in the union without providing a call context (though if
12686 # they want good error reporting they'll provide one anyway).
12687 shimBody = fill(
12689 BindingCallContext cx(cx_, nullptr);
12690 return TrySetTo${name}(cx, value, tryNext, passedToJSImpl);
12691 """,
12692 name=name,
12694 setters.append(
12695 ClassMethod(
12696 "TrySetTo" + name,
12697 "bool",
12699 Argument("JSContext*", "cx_"),
12700 Argument("JS::Handle<JS::Value>", "value"),
12701 Argument("bool&", "tryNext"),
12702 Argument("bool", "passedToJSImpl", default="false"),
12704 visibility="private",
12705 body=shimBody,
12709 return {
12710 "name": name,
12711 "structType": structType,
12712 "externalType": externalType,
12713 "setters": setters,
12714 "ctorArgs": ctorArgs,
12715 "ctorArgList": [Argument("JSContext*", "cx")] if ctorNeedsCx else [],
12719 def getUnionConversionTemplate(type):
12720 assert type.isUnion()
12721 assert not type.nullable()
12723 memberTypes = type.flatMemberTypes
12724 prettyNames = []
12726 interfaceMemberTypes = [t for t in memberTypes if t.isNonCallbackInterface()]
12727 if len(interfaceMemberTypes) > 0:
12728 interfaceObject = []
12729 for memberType in interfaceMemberTypes:
12730 name = getUnionMemberName(memberType)
12731 interfaceObject.append(
12732 CGGeneric(
12733 "(failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext"
12734 % name
12737 prettyNames.append(memberType.prettyName())
12738 interfaceObject = CGWrapper(
12739 CGList(interfaceObject, " ||\n"),
12740 pre="done = ",
12741 post=";\n",
12742 reindent=True,
12744 else:
12745 interfaceObject = None
12747 sequenceObjectMemberTypes = [t for t in memberTypes if t.isSequence()]
12748 if len(sequenceObjectMemberTypes) > 0:
12749 assert len(sequenceObjectMemberTypes) == 1
12750 memberType = sequenceObjectMemberTypes[0]
12751 name = getUnionMemberName(memberType)
12752 sequenceObject = CGGeneric(
12753 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
12754 % name
12756 prettyNames.append(memberType.prettyName())
12757 else:
12758 sequenceObject = None
12760 callbackMemberTypes = [
12761 t for t in memberTypes if t.isCallback() or t.isCallbackInterface()
12763 if len(callbackMemberTypes) > 0:
12764 assert len(callbackMemberTypes) == 1
12765 memberType = callbackMemberTypes[0]
12766 name = getUnionMemberName(memberType)
12767 callbackObject = CGGeneric(
12768 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
12769 % name
12771 prettyNames.append(memberType.prettyName())
12772 else:
12773 callbackObject = None
12775 dictionaryMemberTypes = [t for t in memberTypes if t.isDictionary()]
12776 if len(dictionaryMemberTypes) > 0:
12777 assert len(dictionaryMemberTypes) == 1
12778 memberType = dictionaryMemberTypes[0]
12779 name = getUnionMemberName(memberType)
12780 setDictionary = CGGeneric(
12781 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
12782 % name
12784 prettyNames.append(memberType.prettyName())
12785 else:
12786 setDictionary = None
12788 recordMemberTypes = [t for t in memberTypes if t.isRecord()]
12789 if len(recordMemberTypes) > 0:
12790 assert len(recordMemberTypes) == 1
12791 memberType = recordMemberTypes[0]
12792 name = getUnionMemberName(memberType)
12793 recordObject = CGGeneric(
12794 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
12795 % name
12797 prettyNames.append(memberType.prettyName())
12798 else:
12799 recordObject = None
12801 objectMemberTypes = [t for t in memberTypes if t.isObject()]
12802 if len(objectMemberTypes) > 0:
12803 assert len(objectMemberTypes) == 1
12804 # Very important to NOT construct a temporary Rooted here, since the
12805 # SetToObject call can call a Rooted constructor and we need to keep
12806 # stack discipline for Rooted.
12807 object = CGGeneric(
12808 "if (!SetToObject(cx, &${val}.toObject(), ${passedToJSImpl})) {\n"
12809 " return false;\n"
12810 "}\n"
12811 "done = true;\n"
12813 prettyNames.append(objectMemberTypes[0].prettyName())
12814 else:
12815 object = None
12817 hasObjectTypes = (
12818 interfaceObject or sequenceObject or callbackObject or object or recordObject
12820 if hasObjectTypes:
12821 # "object" is not distinguishable from other types
12822 assert not object or not (
12823 interfaceObject or sequenceObject or callbackObject or recordObject
12825 if sequenceObject or callbackObject:
12826 # An object can be both an sequence object and a callback or
12827 # dictionary, but we shouldn't have both in the union's members
12828 # because they are not distinguishable.
12829 assert not (sequenceObject and callbackObject)
12830 templateBody = CGElseChain([sequenceObject, callbackObject])
12831 else:
12832 templateBody = None
12833 if interfaceObject:
12834 assert not object
12835 if templateBody:
12836 templateBody = CGIfWrapper(templateBody, "!done")
12837 templateBody = CGList([interfaceObject, templateBody])
12838 else:
12839 templateBody = CGList([templateBody, object])
12841 if recordObject:
12842 templateBody = CGList([templateBody, CGIfWrapper(recordObject, "!done")])
12844 templateBody = CGIfWrapper(templateBody, "${val}.isObject()")
12845 else:
12846 templateBody = CGGeneric()
12848 if setDictionary:
12849 assert not object
12850 templateBody = CGList([templateBody, CGIfWrapper(setDictionary, "!done")])
12852 stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
12853 numericTypes = [t for t in memberTypes if t.isNumeric()]
12854 booleanTypes = [t for t in memberTypes if t.isBoolean()]
12855 if stringTypes or numericTypes or booleanTypes:
12856 assert len(stringTypes) <= 1
12857 assert len(numericTypes) <= 1
12858 assert len(booleanTypes) <= 1
12860 # We will wrap all this stuff in a do { } while (0); so we
12861 # can use "break" for flow control.
12862 def getStringOrPrimitiveConversion(memberType):
12863 name = getUnionMemberName(memberType)
12864 return CGGeneric(
12865 "done = (failed = !TrySetTo%s(cx, ${val}, tryNext)) || !tryNext;\n"
12866 "break;\n" % name
12869 other = CGList([])
12870 stringConversion = [getStringOrPrimitiveConversion(t) for t in stringTypes]
12871 numericConversion = [getStringOrPrimitiveConversion(t) for t in numericTypes]
12872 booleanConversion = [getStringOrPrimitiveConversion(t) for t in booleanTypes]
12873 if stringConversion:
12874 if booleanConversion:
12875 other.append(CGIfWrapper(booleanConversion[0], "${val}.isBoolean()"))
12876 if numericConversion:
12877 other.append(CGIfWrapper(numericConversion[0], "${val}.isNumber()"))
12878 other.append(stringConversion[0])
12879 elif numericConversion:
12880 if booleanConversion:
12881 other.append(CGIfWrapper(booleanConversion[0], "${val}.isBoolean()"))
12882 other.append(numericConversion[0])
12883 else:
12884 assert booleanConversion
12885 other.append(booleanConversion[0])
12887 other = CGWrapper(CGIndenter(other), pre="do {\n", post="} while (false);\n")
12888 if hasObjectTypes or setDictionary:
12889 other = CGWrapper(CGIndenter(other), "{\n", post="}\n")
12890 if object:
12891 templateBody = CGElseChain([templateBody, other])
12892 else:
12893 other = CGWrapper(other, pre="if (!done) ")
12894 templateBody = CGList([templateBody, other])
12895 else:
12896 assert templateBody.define() == ""
12897 templateBody = other
12898 else:
12899 other = None
12901 templateBody = CGWrapper(
12902 templateBody, pre="bool done = false, failed = false, tryNext;\n"
12904 throw = CGGeneric(
12905 fill(
12907 if (failed) {
12908 return false;
12910 if (!done) {
12911 cx.ThrowErrorMessage<MSG_NOT_IN_UNION>(sourceDescription, "${names}");
12912 return false;
12914 """,
12915 names=", ".join(prettyNames),
12919 templateBody = CGList([templateBody, throw])
12921 hasUndefinedType = any(t.isUndefined() for t in memberTypes)
12922 elseChain = []
12924 # The spec does this before anything else, but we do it after checking
12925 # for null in the case of a nullable union. In practice this shouldn't
12926 # make a difference, but it makes things easier because we first need to
12927 # call Construct on our Maybe<...>, before we can set the union type to
12928 # undefined, and we do that below after checking for null (see the
12929 # 'if nullable:' block below).
12930 if hasUndefinedType:
12931 elseChain.append(
12932 CGIfWrapper(
12933 CGGeneric("SetUndefined();\n"),
12934 "${val}.isUndefined()",
12938 if type.hasNullableType:
12939 nullTest = (
12940 "${val}.isNull()" if hasUndefinedType else "${val}.isNullOrUndefined()"
12942 elseChain.append(
12943 CGIfWrapper(
12944 CGGeneric("SetNull();\n"),
12945 nullTest,
12949 if len(elseChain) > 0:
12950 elseChain.append(CGWrapper(CGIndenter(templateBody), pre="{\n", post="}\n"))
12951 templateBody = CGElseChain(elseChain)
12953 return templateBody
12956 def getUnionInitMethods(type, isOwningUnion=False):
12957 assert type.isUnion()
12959 template = getUnionConversionTemplate(type).define()
12961 replacements = {
12962 "val": "value",
12963 "passedToJSImpl": "passedToJSImpl",
12966 initBody = fill(
12968 MOZ_ASSERT(mType == eUninitialized);
12970 $*{conversion}
12971 return true;
12972 """,
12973 conversion=string.Template(template).substitute(replacements),
12976 return [
12977 ClassMethod(
12978 "Init",
12979 "bool",
12981 Argument("BindingCallContext&", "cx"),
12982 Argument("JS::Handle<JS::Value>", "value"),
12983 Argument("const char*", "sourceDescription", default='"Value"'),
12984 Argument("bool", "passedToJSImpl", default="false"),
12986 visibility="public",
12987 body=initBody,
12989 ClassMethod(
12990 "Init",
12991 "bool",
12993 Argument("JSContext*", "cx_"),
12994 Argument("JS::Handle<JS::Value>", "value"),
12995 Argument("const char*", "sourceDescription", default='"Value"'),
12996 Argument("bool", "passedToJSImpl", default="false"),
12998 visibility="public",
12999 body=dedent(
13001 BindingCallContext cx(cx_, nullptr);
13002 return Init(cx, value, sourceDescription, passedToJSImpl);
13009 class CGUnionStruct(CGThing):
13010 def __init__(self, type, descriptorProvider, ownsMembers=False):
13011 CGThing.__init__(self)
13012 self.type = type.unroll()
13013 self.descriptorProvider = descriptorProvider
13014 self.ownsMembers = ownsMembers
13015 self.struct = self.getStruct()
13017 def declare(self):
13018 return self.struct.declare()
13020 def define(self):
13021 return self.struct.define()
13023 def deps(self):
13024 return self.type.getDeps()
13026 def getStruct(self):
13027 members = [
13028 ClassMember("mType", "TypeOrUninit", body="eUninitialized"),
13029 ClassMember("mValue", "Value"),
13031 ctor = ClassConstructor(
13032 [], bodyInHeader=True, visibility="public", explicit=True
13035 methods = []
13036 enumValues = ["eUninitialized"]
13037 toJSValCases = [
13038 CGCase(
13039 "eUninitialized", CGGeneric("return false;\n"), CGCase.DONT_ADD_BREAK
13042 destructorCases = [CGCase("eUninitialized", None)]
13043 assignmentCase = CGCase(
13044 "eUninitialized",
13045 CGGeneric(
13046 "MOZ_ASSERT(mType == eUninitialized,\n"
13047 ' "We need to destroy ourselves?");\n'
13050 assignmentCases = [assignmentCase]
13051 moveCases = [assignmentCase]
13052 traceCases = []
13053 unionValues = []
13055 def addSpecialType(typename):
13056 enumValue = "e" + typename
13057 enumValues.append(enumValue)
13058 methods.append(
13059 ClassMethod(
13060 "Is" + typename,
13061 "bool",
13063 const=True,
13064 inline=True,
13065 body="return mType == %s;\n" % enumValue,
13066 bodyInHeader=True,
13069 methods.append(
13070 ClassMethod(
13071 "Set" + typename,
13072 "void",
13074 inline=True,
13075 body=fill(
13077 Uninit();
13078 mType = ${enumValue};
13079 """,
13080 enumValue=enumValue,
13082 bodyInHeader=True,
13085 destructorCases.append(CGCase(enumValue, None))
13086 assignmentCase = CGCase(
13087 enumValue,
13088 CGGeneric(
13089 fill(
13091 MOZ_ASSERT(mType == eUninitialized);
13092 mType = ${enumValue};
13093 """,
13094 enumValue=enumValue,
13098 assignmentCases.append(assignmentCase)
13099 moveCases.append(assignmentCase)
13100 toJSValCases.append(
13101 CGCase(
13102 enumValue,
13103 CGGeneric(
13104 fill(
13106 rval.set${typename}();
13107 return true;
13108 """,
13109 typename=typename,
13112 CGCase.DONT_ADD_BREAK,
13116 if self.type.hasNullableType:
13117 addSpecialType("Null")
13119 hasObjectType = any(t.isObject() for t in self.type.flatMemberTypes)
13120 skipToJSVal = False
13121 for t in self.type.flatMemberTypes:
13122 if t.isUndefined():
13123 addSpecialType("Undefined")
13124 continue
13126 vars = getUnionTypeTemplateVars(
13127 self.type,
13129 self.descriptorProvider,
13130 isMember="OwningUnion" if self.ownsMembers else "Union",
13132 if vars["setters"]:
13133 methods.extend(vars["setters"])
13134 uninit = "Uninit();"
13135 if hasObjectType and not self.ownsMembers:
13136 uninit = (
13137 'MOZ_ASSERT(mType != eObject, "This will not play well with Rooted");\n'
13138 + uninit
13140 if not t.isObject() or self.ownsMembers:
13141 body = fill(
13143 if (mType == e${name}) {
13144 return mValue.m${name}.Value();
13147 mType = e${name};
13148 return mValue.m${name}.SetValue(${ctorArgs});
13149 """,
13150 **vars,
13153 # bodyInHeader must be false for return values because they own
13154 # their union members and we don't want include headers in
13155 # UnionTypes.h just to call Addref/Release
13156 methods.append(
13157 ClassMethod(
13158 "RawSetAs" + vars["name"],
13159 vars["structType"] + "&",
13160 vars["ctorArgList"],
13161 bodyInHeader=not self.ownsMembers,
13162 body=body % "MOZ_ASSERT(mType == eUninitialized);",
13163 noDiscard=True,
13166 methods.append(
13167 ClassMethod(
13168 "SetAs" + vars["name"],
13169 vars["structType"] + "&",
13170 vars["ctorArgList"],
13171 bodyInHeader=not self.ownsMembers,
13172 body=body % uninit,
13173 noDiscard=True,
13177 # Provide a SetStringLiteral() method to support string defaults.
13178 if t.isByteString() or t.isUTF8String():
13179 charType = "const nsCString::char_type"
13180 elif t.isString():
13181 charType = "const nsString::char_type"
13182 else:
13183 charType = None
13185 if charType:
13186 methods.append(
13187 ClassMethod(
13188 "SetStringLiteral",
13189 "void",
13190 # Hack, but it works...
13191 [Argument(charType, "(&aData)[N]")],
13192 inline=True,
13193 bodyInHeader=True,
13194 templateArgs=["int N"],
13195 body="RawSetAs%s().AssignLiteral(aData);\n" % t.name,
13199 body = fill("return mType == e${name};\n", **vars)
13200 methods.append(
13201 ClassMethod(
13202 "Is" + vars["name"],
13203 "bool",
13205 const=True,
13206 bodyInHeader=True,
13207 body=body,
13211 body = fill(
13213 MOZ_RELEASE_ASSERT(Is${name}(), "Wrong type!");
13214 mValue.m${name}.Destroy();
13215 mType = eUninitialized;
13216 """,
13217 **vars,
13219 methods.append(
13220 ClassMethod(
13221 "Destroy" + vars["name"],
13222 "void",
13224 visibility="private",
13225 bodyInHeader=not self.ownsMembers,
13226 body=body,
13230 body = fill(
13232 MOZ_RELEASE_ASSERT(Is${name}(), "Wrong type!");
13233 return mValue.m${name}.Value();
13234 """,
13235 **vars,
13237 # The non-const version of GetAs* returns our internal type
13238 getterReturnType = "%s&" % vars["structType"]
13239 methods.append(
13240 ClassMethod(
13241 "GetAs" + vars["name"],
13242 getterReturnType,
13244 bodyInHeader=True,
13245 body=body,
13248 # The const version of GetAs* returns our internal type
13249 # for owning unions, but our external type for non-owning
13250 # ones.
13251 if self.ownsMembers:
13252 getterReturnType = "%s const &" % vars["structType"]
13253 else:
13254 getterReturnType = vars["externalType"]
13255 methods.append(
13256 ClassMethod(
13257 "GetAs" + vars["name"],
13258 getterReturnType,
13260 const=True,
13261 bodyInHeader=True,
13262 body=body,
13266 unionValues.append(fill("UnionMember<${structType} > m${name}", **vars))
13267 destructorCases.append(
13268 CGCase("e" + vars["name"], CGGeneric("Destroy%s();\n" % vars["name"]))
13271 enumValues.append("e" + vars["name"])
13273 conversionToJS = self.getConversionToJS(vars, t)
13274 if conversionToJS:
13275 toJSValCases.append(
13276 CGCase("e" + vars["name"], conversionToJS, CGCase.DONT_ADD_BREAK)
13278 else:
13279 skipToJSVal = True
13281 assignmentCases.append(
13282 CGCase(
13283 "e" + vars["name"],
13284 CGGeneric(
13285 "SetAs%s() = aOther.GetAs%s();\n" % (vars["name"], vars["name"])
13289 moveCases.append(
13290 CGCase(
13291 "e" + vars["name"],
13292 CGGeneric(
13293 "mType = e%s;\n" % vars["name"]
13294 + "mValue.m%s.SetValue(std::move(aOther.mValue.m%s.Value()));\n"
13295 % (vars["name"], vars["name"])
13299 if self.ownsMembers and typeNeedsRooting(t):
13300 if t.isObject():
13301 traceCases.append(
13302 CGCase(
13303 "e" + vars["name"],
13304 CGGeneric(
13305 'JS::TraceRoot(trc, %s, "%s");\n'
13307 "&mValue.m" + vars["name"] + ".Value()",
13308 "mValue.m" + vars["name"],
13313 elif t.isDictionary():
13314 traceCases.append(
13315 CGCase(
13316 "e" + vars["name"],
13317 CGGeneric(
13318 "mValue.m%s.Value().TraceDictionary(trc);\n"
13319 % vars["name"]
13323 elif t.isSequence():
13324 traceCases.append(
13325 CGCase(
13326 "e" + vars["name"],
13327 CGGeneric(
13328 "DoTraceSequence(trc, mValue.m%s.Value());\n"
13329 % vars["name"]
13333 elif t.isRecord():
13334 traceCases.append(
13335 CGCase(
13336 "e" + vars["name"],
13337 CGGeneric(
13338 "TraceRecord(trc, mValue.m%s.Value());\n" % vars["name"]
13342 else:
13343 assert t.isSpiderMonkeyInterface()
13344 traceCases.append(
13345 CGCase(
13346 "e" + vars["name"],
13347 CGGeneric(
13348 "mValue.m%s.Value().TraceSelf(trc);\n" % vars["name"]
13353 dtor = CGSwitch("mType", destructorCases).define()
13355 methods.extend(getUnionInitMethods(self.type, isOwningUnion=self.ownsMembers))
13356 methods.append(
13357 ClassMethod(
13358 "Uninit",
13359 "void",
13361 visibility="public",
13362 body=dtor,
13363 bodyInHeader=not self.ownsMembers,
13364 inline=not self.ownsMembers,
13368 if not skipToJSVal:
13369 methods.append(
13370 ClassMethod(
13371 "ToJSVal",
13372 "bool",
13374 Argument("JSContext*", "cx"),
13375 Argument("JS::Handle<JSObject*>", "scopeObj"),
13376 Argument("JS::MutableHandle<JS::Value>", "rval"),
13378 body=CGSwitch(
13379 "mType", toJSValCases, default=CGGeneric("return false;\n")
13380 ).define(),
13381 const=True,
13385 constructors = [ctor]
13386 selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers)
13387 if self.ownsMembers:
13388 if traceCases:
13389 traceBody = CGSwitch(
13390 "mType", traceCases, default=CGGeneric("")
13391 ).define()
13392 methods.append(
13393 ClassMethod(
13394 "TraceUnion",
13395 "void",
13396 [Argument("JSTracer*", "trc")],
13397 body=traceBody,
13401 op_body = CGList([])
13402 op_body.append(CGSwitch("aOther.mType", moveCases))
13403 constructors.append(
13404 ClassConstructor(
13405 [Argument("%s&&" % selfName, "aOther")],
13406 visibility="public",
13407 body=op_body.define(),
13411 methods.append(
13412 ClassMethod(
13413 "operator=",
13414 "%s&" % selfName,
13415 [Argument("%s&&" % selfName, "aOther")],
13416 body="this->~%s();\nnew (this) %s (std::move(aOther));\nreturn *this;\n"
13417 % (selfName, selfName),
13421 body = dedent(
13423 MOZ_RELEASE_ASSERT(mType != eUninitialized);
13424 return static_cast<Type>(mType);
13427 methods.append(
13428 ClassMethod(
13429 "GetType",
13430 "Type",
13432 bodyInHeader=True,
13433 body=body,
13434 const=True,
13438 if CGUnionStruct.isUnionCopyConstructible(self.type):
13439 constructors.append(
13440 ClassConstructor(
13441 [Argument("const %s&" % selfName, "aOther")],
13442 bodyInHeader=True,
13443 visibility="public",
13444 explicit=True,
13445 body="*this = aOther;\n",
13448 op_body = CGList([])
13449 op_body.append(CGSwitch("aOther.mType", assignmentCases))
13450 op_body.append(CGGeneric("return *this;\n"))
13451 methods.append(
13452 ClassMethod(
13453 "operator=",
13454 "%s&" % selfName,
13455 [Argument("const %s&" % selfName, "aOther")],
13456 body=op_body.define(),
13459 disallowCopyConstruction = False
13460 else:
13461 disallowCopyConstruction = True
13462 else:
13463 disallowCopyConstruction = True
13465 if self.ownsMembers and idlTypeNeedsCycleCollection(self.type):
13466 friend = (
13467 " friend void ImplCycleCollectionUnlink(%s& aUnion);\n"
13468 % CGUnionStruct.unionTypeName(self.type, True)
13470 else:
13471 friend = ""
13473 enumValuesNoUninit = [x for x in enumValues if x != "eUninitialized"]
13475 enums = [
13476 ClassGroup(
13478 ClassEnum("TypeOrUninit", enumValues, visibility="private"),
13479 ClassEnum(
13480 "Type",
13481 enumValuesNoUninit,
13482 visibility="public",
13483 enumClass=True,
13484 values=["TypeOrUninit::" + x for x in enumValuesNoUninit],
13490 bases = [
13491 ClassBase("AllOwningUnionBase" if self.ownsMembers else "AllUnionBase")
13494 typeAliases = []
13495 bufferSourceTypes = [
13496 t.name for t in self.type.flatMemberTypes if t.isBufferSource()
13498 if len(bufferSourceTypes) > 0:
13499 bases.append(ClassBase("UnionWithTypedArraysBase"))
13500 memberTypesCount = len(self.type.flatMemberTypes)
13501 if self.type.hasNullableType:
13502 memberTypesCount += 1
13504 typeAliases = [
13505 ClassUsingDeclaration(
13506 "ApplyToTypedArrays",
13507 "binding_detail::ApplyToTypedArraysHelper<%s, %s, %s>"
13509 selfName,
13510 toStringBool(memberTypesCount > len(bufferSourceTypes)),
13511 ", ".join(bufferSourceTypes),
13516 return CGClass(
13517 selfName,
13518 bases=bases,
13519 typeAliases=typeAliases,
13520 members=members,
13521 constructors=constructors,
13522 methods=methods,
13523 disallowCopyConstruction=disallowCopyConstruction,
13524 extradeclarations=friend,
13525 destructor=ClassDestructor(
13526 visibility="public", body="Uninit();\n", bodyInHeader=True
13528 enums=enums,
13529 unions=[ClassUnion("Value", unionValues, visibility="private")],
13532 def getConversionToJS(self, templateVars, type):
13533 if type.isDictionary() and not type.inner.needsConversionToJS:
13534 # We won't be able to convert this dictionary to a JS value, nor
13535 # will we need to, since we don't need a ToJSVal method at all.
13536 return None
13538 assert not type.nullable() # flatMemberTypes never has nullable types
13539 val = "mValue.m%(name)s.Value()" % templateVars
13540 wrapCode = wrapForType(
13541 type,
13542 self.descriptorProvider,
13544 "jsvalRef": "rval",
13545 "jsvalHandle": "rval",
13546 "obj": "scopeObj",
13547 "result": val,
13548 "spiderMonkeyInterfacesAreStructs": True,
13551 return CGGeneric(wrapCode)
13553 @staticmethod
13554 def isUnionCopyConstructible(type):
13555 return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes)
13557 @staticmethod
13558 def unionTypeName(type, ownsMembers):
13560 Returns a string name for this known union type.
13562 assert type.isUnion() and not type.nullable()
13563 return ("Owning" if ownsMembers else "") + type.name
13565 @staticmethod
13566 def unionTypeDecl(type, ownsMembers):
13568 Returns a string for declaring this possibly-nullable union type.
13570 assert type.isUnion()
13571 nullable = type.nullable()
13572 if nullable:
13573 type = type.inner
13574 decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers))
13575 if nullable:
13576 decl = CGTemplatedType("Nullable", decl)
13577 return decl.define()
13580 class ClassItem:
13581 """Use with CGClass"""
13583 def __init__(self, name, visibility):
13584 self.name = name
13585 self.visibility = visibility
13587 def declare(self, cgClass):
13588 assert False
13590 def define(self, cgClass):
13591 assert False
13594 class ClassBase(ClassItem):
13595 def __init__(self, name, visibility="public"):
13596 ClassItem.__init__(self, name, visibility)
13598 def declare(self, cgClass):
13599 return "%s %s" % (self.visibility, self.name)
13601 def define(self, cgClass):
13602 # Only in the header
13603 return ""
13606 class ClassMethod(ClassItem):
13607 def __init__(
13608 self,
13609 name,
13610 returnType,
13611 args,
13612 inline=False,
13613 static=False,
13614 virtual=False,
13615 const=False,
13616 bodyInHeader=False,
13617 templateArgs=None,
13618 visibility="public",
13619 body=None,
13620 breakAfterReturnDecl="\n",
13621 breakAfterSelf="\n",
13622 override=False,
13623 canRunScript=False,
13624 noDiscard=False,
13627 override indicates whether to flag the method as override
13629 assert not override or virtual
13630 assert not (override and static)
13631 self.returnType = returnType
13632 self.args = args
13633 self.inline = inline or bodyInHeader
13634 self.static = static
13635 self.virtual = virtual
13636 self.const = const
13637 self.bodyInHeader = bodyInHeader
13638 self.templateArgs = templateArgs
13639 self.body = body
13640 self.breakAfterReturnDecl = breakAfterReturnDecl
13641 self.breakAfterSelf = breakAfterSelf
13642 self.override = override
13643 self.canRunScript = canRunScript
13644 self.noDiscard = noDiscard
13645 ClassItem.__init__(self, name, visibility)
13647 def getDecorators(self, declaring):
13648 decorators = []
13649 if self.noDiscard:
13650 decorators.append("[[nodiscard]]")
13651 if self.canRunScript:
13652 decorators.append("MOZ_CAN_RUN_SCRIPT")
13653 if self.inline:
13654 decorators.append("inline")
13655 if declaring:
13656 if self.static:
13657 decorators.append("static")
13658 if self.virtual and not self.override:
13659 decorators.append("virtual")
13660 if decorators:
13661 return " ".join(decorators) + " "
13662 return ""
13664 def getBody(self):
13665 # Override me or pass a string to constructor
13666 assert self.body is not None
13667 return self.body
13669 def declare(self, cgClass):
13670 templateClause = (
13671 "template <%s>\n" % ", ".join(self.templateArgs)
13672 if self.bodyInHeader and self.templateArgs
13673 else ""
13675 args = ", ".join([a.declare() for a in self.args])
13676 if self.bodyInHeader:
13677 body = indent(self.getBody())
13678 body = "\n{\n" + body + "}\n"
13679 else:
13680 body = ";\n"
13682 return fill(
13683 "${templateClause}${decorators}${returnType}${breakAfterReturnDecl}"
13684 "${name}(${args})${const}${override}${body}"
13685 "${breakAfterSelf}",
13686 templateClause=templateClause,
13687 decorators=self.getDecorators(True),
13688 returnType=self.returnType,
13689 breakAfterReturnDecl=self.breakAfterReturnDecl,
13690 name=self.name,
13691 args=args,
13692 const=" const" if self.const else "",
13693 override=" override" if self.override else "",
13694 body=body,
13695 breakAfterSelf=self.breakAfterSelf,
13698 def define(self, cgClass):
13699 if self.bodyInHeader:
13700 return ""
13702 templateArgs = cgClass.templateArgs
13703 if templateArgs:
13704 if cgClass.templateSpecialization:
13705 templateArgs = templateArgs[len(cgClass.templateSpecialization) :]
13707 if templateArgs:
13708 templateClause = "template <%s>\n" % ", ".join(
13709 [str(a) for a in templateArgs]
13711 else:
13712 templateClause = ""
13714 return fill(
13716 ${templateClause}${decorators}${returnType}
13717 ${className}::${name}(${args})${const}
13719 $*{body}
13721 """,
13722 templateClause=templateClause,
13723 decorators=self.getDecorators(False),
13724 returnType=self.returnType,
13725 className=cgClass.getNameString(),
13726 name=self.name,
13727 args=", ".join([a.define() for a in self.args]),
13728 const=" const" if self.const else "",
13729 body=self.getBody(),
13733 class ClassUsingDeclaration(ClassItem):
13735 Used for declaring an alias for a type in a CGClass
13737 name is the name of the alias
13739 type is the type to declare an alias of
13741 visibility determines the visibility of the alias (public,
13742 protected, private), defaults to public.
13745 def __init__(self, name, type, visibility="public"):
13746 self.type = type
13747 ClassItem.__init__(self, name, visibility)
13749 def declare(self, cgClass):
13750 return "using %s = %s;\n\n" % (self.name, self.type)
13752 def define(self, cgClass):
13753 return ""
13756 class ClassUsingFromBaseDeclaration(ClassItem):
13758 Used for importing a name from a base class into a CGClass
13760 baseClass is the name of the base class to import the name from
13762 name is the name to import
13764 visibility determines the visibility of the name (public,
13765 protected, private), defaults to public.
13768 def __init__(self, baseClass, name, visibility="public"):
13769 self.baseClass = baseClass
13770 ClassItem.__init__(self, name, visibility)
13772 def declare(self, cgClass):
13773 return "using %s::%s;\n\n" % (self.baseClass, self.name)
13775 def define(self, cgClass):
13776 return ""
13779 class ClassConstructor(ClassItem):
13781 Used for adding a constructor to a CGClass.
13783 args is a list of Argument objects that are the arguments taken by the
13784 constructor.
13786 inline should be True if the constructor should be marked inline.
13788 bodyInHeader should be True if the body should be placed in the class
13789 declaration in the header.
13791 default should be True if the definition of the constructor should be
13792 `= default;`.
13794 visibility determines the visibility of the constructor (public,
13795 protected, private), defaults to private.
13797 explicit should be True if the constructor should be marked explicit.
13799 baseConstructors is a list of strings containing calls to base constructors,
13800 defaults to None.
13802 body contains a string with the code for the constructor, defaults to empty.
13805 def __init__(
13806 self,
13807 args,
13808 inline=False,
13809 bodyInHeader=False,
13810 default=False,
13811 visibility="private",
13812 explicit=False,
13813 constexpr=False,
13814 baseConstructors=None,
13815 body="",
13817 assert not (inline and constexpr)
13818 assert not (bodyInHeader and constexpr)
13819 assert not (default and body)
13820 self.args = args
13821 self.inline = inline or bodyInHeader
13822 self.bodyInHeader = bodyInHeader or constexpr or default
13823 self.default = default
13824 self.explicit = explicit
13825 self.constexpr = constexpr
13826 self.baseConstructors = baseConstructors or []
13827 self.body = body
13828 ClassItem.__init__(self, None, visibility)
13830 def getDecorators(self, declaring):
13831 decorators = []
13832 if declaring:
13833 if self.explicit:
13834 decorators.append("explicit")
13835 if self.inline:
13836 decorators.append("inline")
13837 if self.constexpr:
13838 decorators.append("constexpr")
13839 if decorators:
13840 return " ".join(decorators) + " "
13841 return ""
13843 def getInitializationList(self, cgClass):
13844 items = [str(c) for c in self.baseConstructors]
13845 for m in cgClass.members:
13846 if not m.static:
13847 initialize = m.body
13848 if initialize:
13849 items.append(m.name + "(" + initialize + ")")
13851 if len(items) > 0:
13852 return "\n : " + ",\n ".join(items)
13853 return ""
13855 def getBody(self):
13856 return self.body
13858 def declare(self, cgClass):
13859 args = ", ".join([a.declare() for a in self.args])
13860 if self.bodyInHeader:
13861 if self.default:
13862 body = " = default;\n"
13863 else:
13864 body = (
13865 self.getInitializationList(cgClass)
13866 + "\n{\n"
13867 + indent(self.getBody())
13868 + "}\n"
13870 else:
13871 body = ";\n"
13873 return fill(
13874 "${decorators}${className}(${args})${body}\n",
13875 decorators=self.getDecorators(True),
13876 className=cgClass.getNameString(),
13877 args=args,
13878 body=body,
13881 def define(self, cgClass):
13882 if self.bodyInHeader:
13883 return ""
13885 return fill(
13887 ${decorators}
13888 ${className}::${className}(${args})${initializationList}
13890 $*{body}
13892 """,
13893 decorators=self.getDecorators(False),
13894 className=cgClass.getNameString(),
13895 args=", ".join([a.define() for a in self.args]),
13896 initializationList=self.getInitializationList(cgClass),
13897 body=self.getBody(),
13901 class ClassDestructor(ClassItem):
13903 Used for adding a destructor to a CGClass.
13905 inline should be True if the destructor should be marked inline.
13907 bodyInHeader should be True if the body should be placed in the class
13908 declaration in the header.
13910 visibility determines the visibility of the destructor (public,
13911 protected, private), defaults to private.
13913 body contains a string with the code for the destructor, defaults to empty.
13915 virtual determines whether the destructor is virtual, defaults to False.
13918 def __init__(
13919 self,
13920 inline=False,
13921 bodyInHeader=False,
13922 visibility="private",
13923 body="",
13924 virtual=False,
13926 self.inline = inline or bodyInHeader
13927 self.bodyInHeader = bodyInHeader
13928 self.body = body
13929 self.virtual = virtual
13930 ClassItem.__init__(self, None, visibility)
13932 def getDecorators(self, declaring):
13933 decorators = []
13934 if self.virtual and declaring:
13935 decorators.append("virtual")
13936 if self.inline and declaring:
13937 decorators.append("inline")
13938 if decorators:
13939 return " ".join(decorators) + " "
13940 return ""
13942 def getBody(self):
13943 return self.body
13945 def declare(self, cgClass):
13946 if self.bodyInHeader:
13947 body = "\n{\n" + indent(self.getBody()) + "}\n"
13948 else:
13949 body = ";\n"
13951 return fill(
13952 "${decorators}~${className}()${body}\n",
13953 decorators=self.getDecorators(True),
13954 className=cgClass.getNameString(),
13955 body=body,
13958 def define(self, cgClass):
13959 if self.bodyInHeader:
13960 return ""
13961 return fill(
13963 ${decorators}
13964 ${className}::~${className}()
13966 $*{body}
13968 """,
13969 decorators=self.getDecorators(False),
13970 className=cgClass.getNameString(),
13971 body=self.getBody(),
13975 class ClassMember(ClassItem):
13976 def __init__(
13977 self,
13978 name,
13979 type,
13980 visibility="private",
13981 static=False,
13982 body=None,
13983 hasIgnoreInitCheckFlag=False,
13985 self.type = type
13986 self.static = static
13987 self.body = body
13988 self.hasIgnoreInitCheckFlag = hasIgnoreInitCheckFlag
13989 ClassItem.__init__(self, name, visibility)
13991 def declare(self, cgClass):
13992 return "%s%s%s %s;\n" % (
13993 "static " if self.static else "",
13994 "MOZ_INIT_OUTSIDE_CTOR " if self.hasIgnoreInitCheckFlag else "",
13995 self.type,
13996 self.name,
13999 def define(self, cgClass):
14000 if not self.static:
14001 return ""
14002 if self.body:
14003 body = " = " + self.body
14004 else:
14005 body = ""
14006 return "%s %s::%s%s;\n" % (self.type, cgClass.getNameString(), self.name, body)
14009 class ClassEnum(ClassItem):
14010 def __init__(
14011 self, name, entries, values=None, visibility="public", enumClass=False
14013 self.entries = entries
14014 self.values = values
14015 self.enumClass = enumClass
14016 ClassItem.__init__(self, name, visibility)
14018 def declare(self, cgClass):
14019 entries = []
14020 for i in range(0, len(self.entries)):
14021 if not self.values or i >= len(self.values):
14022 entry = "%s" % self.entries[i]
14023 else:
14024 entry = "%s = %s" % (self.entries[i], self.values[i])
14025 entries.append(entry)
14027 decl = ["enum"]
14028 self.enumClass and decl.append("class")
14029 self.name and decl.append(self.name)
14030 decl = " ".join(decl)
14032 return "%s\n{\n%s\n};\n" % (decl, indent(",\n".join(entries)))
14034 def define(self, cgClass):
14035 # Only goes in the header
14036 return ""
14039 class ClassUnion(ClassItem):
14040 def __init__(self, name, entries, visibility="public"):
14041 self.entries = [entry + ";\n" for entry in entries]
14042 ClassItem.__init__(self, name, visibility)
14044 def declare(self, cgClass):
14045 return "union %s\n{\n%s\n};\n" % (self.name, indent("".join(self.entries)))
14047 def define(self, cgClass):
14048 # Only goes in the header
14049 return ""
14052 class ClassGroup(ClassItem):
14053 def __init__(self, items):
14054 self.items = items
14055 ClassItem.__init__(self, "", items[0].visibility)
14057 def declare(self, cgClass):
14058 assert False
14060 def define(self, cgClass):
14061 assert False
14064 class CGClass(CGThing):
14065 def __init__(
14066 self,
14067 name,
14068 bases=[],
14069 typeAliases=[],
14070 members=[],
14071 constructors=[],
14072 destructor=None,
14073 methods=[],
14074 enums=[],
14075 unions=[],
14076 templateArgs=[],
14077 templateSpecialization=[],
14078 isStruct=False,
14079 disallowCopyConstruction=False,
14080 indent="",
14081 decorators="",
14082 extradeclarations="",
14083 extradefinitions="",
14085 CGThing.__init__(self)
14086 self.name = name
14087 self.bases = bases
14088 self.typeAliases = typeAliases
14089 self.members = members
14090 self.constructors = constructors
14091 # We store our single destructor in a list, since all of our
14092 # code wants lists of members.
14093 self.destructors = [destructor] if destructor else []
14094 self.methods = methods
14095 self.enums = enums
14096 self.unions = unions
14097 self.templateArgs = templateArgs
14098 self.templateSpecialization = templateSpecialization
14099 self.isStruct = isStruct
14100 self.disallowCopyConstruction = disallowCopyConstruction
14101 self.indent = indent
14102 self.defaultVisibility = "public" if isStruct else "private"
14103 self.decorators = decorators
14104 self.extradeclarations = extradeclarations
14105 self.extradefinitions = extradefinitions
14107 def getNameString(self):
14108 className = self.name
14109 if self.templateSpecialization:
14110 className += "<%s>" % ", ".join(
14111 [str(a) for a in self.templateSpecialization]
14113 return className
14115 @staticmethod
14116 def flattenClassItemLists(l):
14117 for item in l:
14118 if isinstance(item, ClassGroup):
14119 for inner in CGClass.flattenClassItemLists(item.items):
14120 yield inner
14121 else:
14122 yield item
14124 def declare(self):
14125 result = ""
14126 if self.templateArgs:
14127 templateArgs = [a.declare() for a in self.templateArgs]
14128 templateArgs = templateArgs[len(self.templateSpecialization) :]
14129 result += "template <%s>\n" % ",".join([str(a) for a in templateArgs])
14131 type = "struct" if self.isStruct else "class"
14133 if self.templateSpecialization:
14134 specialization = "<%s>" % ", ".join(
14135 [str(a) for a in self.templateSpecialization]
14137 else:
14138 specialization = ""
14140 myself = "%s %s%s" % (type, self.name, specialization)
14141 if self.decorators != "":
14142 myself += " " + self.decorators
14143 result += myself
14145 if self.bases:
14146 inherit = " : "
14147 result += inherit
14148 # Grab our first base
14149 baseItems = [CGGeneric(b.declare(self)) for b in self.bases]
14150 bases = baseItems[:1]
14151 # Indent the rest
14152 bases.extend(
14153 CGIndenter(b, len(myself) + len(inherit)) for b in baseItems[1:]
14155 result += ",\n".join(b.define() for b in bases)
14157 result += "\n{\n"
14159 result += self.extradeclarations
14161 def declareMembers(cgClass, memberList, defaultVisibility):
14162 members = {"private": [], "protected": [], "public": []}
14164 for member in memberList:
14165 members[member.visibility].append(member)
14167 if defaultVisibility == "public":
14168 order = ["public", "protected", "private"]
14169 else:
14170 order = ["private", "protected", "public"]
14172 result = ""
14174 lastVisibility = defaultVisibility
14175 for visibility in order:
14176 list = members[visibility]
14177 if list:
14178 for member in self.flattenClassItemLists(list):
14179 if member.visibility != lastVisibility:
14180 result += member.visibility + ":\n"
14181 result += indent(member.declare(cgClass))
14182 lastVisibility = member.visibility
14183 return (result, lastVisibility)
14185 if self.disallowCopyConstruction:
14187 class DisallowedCopyConstructor(object):
14188 def __init__(self):
14189 self.visibility = "private"
14191 def declare(self, cgClass):
14192 name = cgClass.getNameString()
14193 return (
14194 "%s(const %s&) = delete;\n"
14195 "%s& operator=(const %s&) = delete;\n"
14196 % (name, name, name, name)
14199 disallowedCopyConstructors = [DisallowedCopyConstructor()]
14200 else:
14201 disallowedCopyConstructors = []
14203 order = [
14204 self.typeAliases,
14205 self.enums,
14206 self.unions,
14207 self.members,
14208 self.constructors + disallowedCopyConstructors,
14209 self.destructors,
14210 self.methods,
14213 lastVisibility = self.defaultVisibility
14214 pieces = []
14215 for memberList in order:
14216 code, lastVisibility = declareMembers(self, memberList, lastVisibility)
14218 if code:
14219 code = code.rstrip() + "\n" # remove extra blank lines at the end
14220 pieces.append(code)
14222 result += "\n".join(pieces)
14223 result += "};\n"
14224 result = indent(result, len(self.indent))
14225 return result
14227 def define(self):
14228 def defineMembers(cgClass, memberList, itemCount, separator=""):
14229 result = ""
14230 for member in self.flattenClassItemLists(memberList):
14231 if itemCount != 0:
14232 result = result + separator
14233 definition = member.define(cgClass)
14234 if definition:
14235 # Member variables would only produce empty lines here.
14236 result += definition
14237 itemCount += 1
14238 return (result, itemCount)
14240 order = [
14241 (self.members, ""),
14242 (self.constructors, "\n"),
14243 (self.destructors, "\n"),
14244 (self.methods, "\n"),
14247 result = self.extradefinitions
14248 itemCount = 0
14249 for memberList, separator in order:
14250 memberString, itemCount = defineMembers(
14251 self, memberList, itemCount, separator
14253 result = result + memberString
14254 return result
14257 class CGResolveOwnPropertyViaResolve(CGAbstractBindingMethod):
14259 An implementation of Xray ResolveOwnProperty stuff for things that have a
14260 resolve hook.
14263 def __init__(self, descriptor):
14264 args = [
14265 Argument("JSContext*", "cx"),
14266 Argument("JS::Handle<JSObject*>", "wrapper"),
14267 Argument("JS::Handle<JSObject*>", "obj"),
14268 Argument("JS::Handle<jsid>", "id"),
14269 Argument("JS::MutableHandle<Maybe<JS::PropertyDescriptor>>", "desc"),
14271 CGAbstractBindingMethod.__init__(
14272 self,
14273 descriptor,
14274 "ResolveOwnPropertyViaResolve",
14275 args,
14276 getThisObj="",
14277 callArgs="",
14280 def generate_code(self):
14281 return CGGeneric(
14282 dedent(
14285 // Since we're dealing with an Xray, do the resolve on the
14286 // underlying object first. That gives it a chance to
14287 // define properties on the actual object as needed, and
14288 // then use the fact that it created the objects as a flag
14289 // to avoid re-resolving the properties if someone deletes
14290 // them.
14291 JSAutoRealm ar(cx, obj);
14292 JS_MarkCrossZoneId(cx, id);
14293 JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> objDesc(cx);
14294 if (!self->DoResolve(cx, obj, id, &objDesc)) {
14295 return false;
14297 // If desc->value() is undefined, then the DoResolve call
14298 // has already defined the property on the object. Don't
14299 // try to also define it.
14300 if (objDesc.isSome() &&
14301 !objDesc->value().isUndefined()) {
14302 JS::Rooted<JS::PropertyDescriptor> defineDesc(cx, *objDesc);
14303 if (!JS_DefinePropertyById(cx, obj, id, defineDesc)) {
14304 return false;
14308 return self->DoResolve(cx, wrapper, id, desc);
14314 class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod):
14316 An implementation of Xray EnumerateOwnProperties stuff for things
14317 that have a resolve hook.
14320 def __init__(self, descriptor):
14321 args = [
14322 Argument("JSContext*", "cx"),
14323 Argument("JS::Handle<JSObject*>", "wrapper"),
14324 Argument("JS::Handle<JSObject*>", "obj"),
14325 Argument("JS::MutableHandleVector<jsid>", "props"),
14327 CGAbstractBindingMethod.__init__(
14328 self,
14329 descriptor,
14330 "EnumerateOwnPropertiesViaGetOwnPropertyNames",
14331 args,
14332 getThisObj="",
14333 callArgs="",
14336 def generate_code(self):
14337 return CGGeneric(
14338 dedent(
14340 FastErrorResult rv;
14341 // This wants all own props, not just enumerable ones.
14342 self->GetOwnPropertyNames(cx, props, false, rv);
14343 if (rv.MaybeSetPendingException(cx)) {
14344 return false;
14346 return true;
14352 class CGPrototypeTraitsClass(CGClass):
14353 def __init__(self, descriptor, indent=""):
14354 templateArgs = [Argument("prototypes::ID", "PrototypeID")]
14355 templateSpecialization = ["prototypes::id::" + descriptor.name]
14356 enums = [ClassEnum("", ["Depth"], [descriptor.interface.inheritanceDepth()])]
14357 CGClass.__init__(
14358 self,
14359 "PrototypeTraits",
14360 indent=indent,
14361 templateArgs=templateArgs,
14362 templateSpecialization=templateSpecialization,
14363 enums=enums,
14364 isStruct=True,
14367 def deps(self):
14368 return set()
14371 class CGClassForwardDeclare(CGThing):
14372 def __init__(self, name, isStruct=False):
14373 CGThing.__init__(self)
14374 self.name = name
14375 self.isStruct = isStruct
14377 def declare(self):
14378 type = "struct" if self.isStruct else "class"
14379 return "%s %s;\n" % (type, self.name)
14381 def define(self):
14382 # Header only
14383 return ""
14385 def deps(self):
14386 return set()
14389 class CGProxySpecialOperation(CGPerSignatureCall):
14391 Base class for classes for calling an indexed or named special operation
14392 (don't use this directly, use the derived classes below).
14394 If checkFound is False, will just assert that the prop is found instead of
14395 checking that it is before wrapping the value.
14397 resultVar: See the docstring for CGCallGenerator.
14399 foundVar: For getters and deleters, the generated code can also set a bool
14400 variable, declared by the caller, if the given indexed or named property
14401 already existed. If the caller wants this, it should pass the name of the
14402 bool variable as the foundVar keyword argument to the constructor. The
14403 caller is responsible for declaring the variable and initializing it to
14404 false.
14407 def __init__(
14408 self,
14409 descriptor,
14410 operation,
14411 checkFound=True,
14412 argumentHandleValue=None,
14413 resultVar=None,
14414 foundVar=None,
14416 self.checkFound = checkFound
14417 self.foundVar = foundVar or "found"
14419 nativeName = MakeNativeName(descriptor.binaryNameFor(operation, False))
14420 operation = descriptor.operations[operation]
14421 assert len(operation.signatures()) == 1
14422 signature = operation.signatures()[0]
14424 returnType, arguments = signature
14426 # We pass len(arguments) as the final argument so that the
14427 # CGPerSignatureCall won't do any argument conversion of its own.
14428 CGPerSignatureCall.__init__(
14429 self,
14430 returnType,
14431 arguments,
14432 nativeName,
14433 False,
14434 descriptor,
14435 operation,
14436 len(arguments),
14437 resultVar=resultVar,
14438 objectName="proxy",
14441 if operation.isSetter():
14442 # arguments[0] is the index or name of the item that we're setting.
14443 argument = arguments[1]
14444 info = getJSToNativeConversionInfo(
14445 argument.type,
14446 descriptor,
14447 sourceDescription=(
14448 "value being assigned to %s setter"
14449 % descriptor.interface.identifier.name
14452 if argumentHandleValue is None:
14453 argumentHandleValue = "desc.value()"
14454 rootedValue = fill(
14456 JS::Rooted<JS::Value> rootedValue(cx, ${argumentHandleValue});
14457 """,
14458 argumentHandleValue=argumentHandleValue,
14460 templateValues = {
14461 "declName": argument.identifier.name,
14462 "holderName": argument.identifier.name + "_holder",
14463 "val": argumentHandleValue,
14464 "maybeMutableVal": "&rootedValue",
14465 "obj": "obj",
14466 "passedToJSImpl": "false",
14468 self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues))
14469 # rootedValue needs to come before the conversion, so we
14470 # need to prepend it last.
14471 self.cgRoot.prepend(CGGeneric(rootedValue))
14472 elif operation.isGetter() or operation.isDeleter():
14473 if foundVar is None:
14474 self.cgRoot.prepend(CGGeneric("bool found = false;\n"))
14476 def getArguments(self):
14477 args = [(a, a.identifier.name) for a in self.arguments]
14478 if self.idlNode.isGetter() or self.idlNode.isDeleter():
14479 args.append(
14481 FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean]),
14482 self.foundVar,
14485 return args
14487 def wrap_return_value(self):
14488 if not self.idlNode.isGetter() or self.templateValues is None:
14489 return ""
14491 wrap = CGGeneric(
14492 wrapForType(self.returnType, self.descriptor, self.templateValues)
14494 if self.checkFound:
14495 wrap = CGIfWrapper(wrap, self.foundVar)
14496 else:
14497 wrap = CGList([CGGeneric("MOZ_ASSERT(" + self.foundVar + ");\n"), wrap])
14498 return "\n" + wrap.define()
14501 class CGProxyIndexedOperation(CGProxySpecialOperation):
14503 Class to generate a call to an indexed operation.
14505 If doUnwrap is False, the caller is responsible for making sure a variable
14506 named 'self' holds the C++ object somewhere where the code we generate
14507 will see it.
14509 If checkFound is False, will just assert that the prop is found instead of
14510 checking that it is before wrapping the value.
14512 resultVar: See the docstring for CGCallGenerator.
14514 foundVar: See the docstring for CGProxySpecialOperation.
14517 def __init__(
14518 self,
14519 descriptor,
14520 name,
14521 doUnwrap=True,
14522 checkFound=True,
14523 argumentHandleValue=None,
14524 resultVar=None,
14525 foundVar=None,
14527 self.doUnwrap = doUnwrap
14528 CGProxySpecialOperation.__init__(
14529 self,
14530 descriptor,
14531 name,
14532 checkFound,
14533 argumentHandleValue=argumentHandleValue,
14534 resultVar=resultVar,
14535 foundVar=foundVar,
14538 def define(self):
14539 # Our first argument is the id we're getting.
14540 argName = self.arguments[0].identifier.name
14541 if argName == "index":
14542 # We already have our index in a variable with that name
14543 setIndex = ""
14544 else:
14545 setIndex = "uint32_t %s = index;\n" % argName
14546 if self.doUnwrap:
14547 unwrap = "%s* self = UnwrapProxy(proxy);\n" % self.descriptor.nativeType
14548 else:
14549 unwrap = ""
14550 return setIndex + unwrap + CGProxySpecialOperation.define(self)
14553 class CGProxyIndexedGetter(CGProxyIndexedOperation):
14555 Class to generate a call to an indexed getter. If templateValues is not None
14556 the returned value will be wrapped with wrapForType using templateValues.
14558 If doUnwrap is False, the caller is responsible for making sure a variable
14559 named 'self' holds the C++ object somewhere where the code we generate
14560 will see it.
14562 If checkFound is False, will just assert that the prop is found instead of
14563 checking that it is before wrapping the value.
14565 foundVar: See the docstring for CGProxySpecialOperation.
14568 def __init__(
14569 self,
14570 descriptor,
14571 templateValues=None,
14572 doUnwrap=True,
14573 checkFound=True,
14574 foundVar=None,
14576 self.templateValues = templateValues
14577 CGProxyIndexedOperation.__init__(
14578 self, descriptor, "IndexedGetter", doUnwrap, checkFound, foundVar=foundVar
14582 class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter):
14584 Class to generate a call that checks whether an indexed property exists.
14586 For now, we just delegate to CGProxyIndexedGetter
14588 foundVar: See the docstring for CGProxySpecialOperation.
14591 def __init__(self, descriptor, foundVar):
14592 CGProxyIndexedGetter.__init__(self, descriptor, foundVar=foundVar)
14593 self.cgRoot.append(CGGeneric("(void)result;\n"))
14596 class CGProxyIndexedSetter(CGProxyIndexedOperation):
14598 Class to generate a call to an indexed setter.
14601 def __init__(self, descriptor, argumentHandleValue=None):
14602 CGProxyIndexedOperation.__init__(
14603 self, descriptor, "IndexedSetter", argumentHandleValue=argumentHandleValue
14607 class CGProxyNamedOperation(CGProxySpecialOperation):
14609 Class to generate a call to a named operation.
14611 'value' is the jsval to use for the name; None indicates that it should be
14612 gotten from the property id.
14614 resultVar: See the docstring for CGCallGenerator.
14616 foundVar: See the docstring for CGProxySpecialOperation.
14618 tailCode: if we end up with a non-symbol string id, run this code after
14619 we do all our other work.
14622 def __init__(
14623 self,
14624 descriptor,
14625 name,
14626 value=None,
14627 argumentHandleValue=None,
14628 resultVar=None,
14629 foundVar=None,
14630 tailCode="",
14632 CGProxySpecialOperation.__init__(
14633 self,
14634 descriptor,
14635 name,
14636 argumentHandleValue=argumentHandleValue,
14637 resultVar=resultVar,
14638 foundVar=foundVar,
14640 self.value = value
14641 self.tailCode = tailCode
14643 def define(self):
14644 # Our first argument is the id we're getting.
14645 argName = self.arguments[0].identifier.name
14646 if argName == "id":
14647 # deal with the name collision
14648 decls = "JS::Rooted<jsid> id_(cx, id);\n"
14649 idName = "id_"
14650 else:
14651 decls = ""
14652 idName = "id"
14654 decls += "FakeString<char16_t> %s;\n" % argName
14656 main = fill(
14658 ${nativeType}* self = UnwrapProxy(proxy);
14659 $*{op}
14660 $*{tailCode}
14661 """,
14662 nativeType=self.descriptor.nativeType,
14663 op=CGProxySpecialOperation.define(self),
14664 tailCode=self.tailCode,
14667 if self.value is None:
14668 return fill(
14670 $*{decls}
14671 bool isSymbol;
14672 if (!ConvertIdToString(cx, ${idName}, ${argName}, isSymbol)) {
14673 return false;
14675 if (!isSymbol) {
14676 $*{main}
14678 """,
14679 decls=decls,
14680 idName=idName,
14681 argName=argName,
14682 main=main,
14685 # Sadly, we have to set up nameVal even if we have an atom id,
14686 # because we don't know for sure, and we can end up needing it
14687 # so it needs to be higher up the stack. Using a Maybe here
14688 # seems like probable overkill.
14689 return fill(
14691 $*{decls}
14692 JS::Rooted<JS::Value> nameVal(cx, ${value});
14693 if (!nameVal.isSymbol()) {
14694 if (!ConvertJSValueToString(cx, nameVal, eStringify, eStringify,
14695 ${argName})) {
14696 return false;
14698 $*{main}
14700 """,
14701 decls=decls,
14702 value=self.value,
14703 argName=argName,
14704 main=main,
14708 class CGProxyNamedGetter(CGProxyNamedOperation):
14710 Class to generate a call to an named getter. If templateValues is not None
14711 the returned value will be wrapped with wrapForType using templateValues.
14712 'value' is the jsval to use for the name; None indicates that it should be
14713 gotten from the property id.
14715 foundVar: See the docstring for CGProxySpecialOperation.
14718 def __init__(self, descriptor, templateValues=None, value=None, foundVar=None):
14719 self.templateValues = templateValues
14720 CGProxyNamedOperation.__init__(
14721 self, descriptor, "NamedGetter", value, foundVar=foundVar
14725 class CGProxyNamedPresenceChecker(CGProxyNamedGetter):
14727 Class to generate a call that checks whether a named property exists.
14729 For now, we just delegate to CGProxyNamedGetter
14731 foundVar: See the docstring for CGProxySpecialOperation.
14734 def __init__(self, descriptor, foundVar=None):
14735 CGProxyNamedGetter.__init__(self, descriptor, foundVar=foundVar)
14736 self.cgRoot.append(CGGeneric("(void)result;\n"))
14739 class CGProxyNamedSetter(CGProxyNamedOperation):
14741 Class to generate a call to a named setter.
14744 def __init__(self, descriptor, tailCode, argumentHandleValue=None):
14745 CGProxyNamedOperation.__init__(
14746 self,
14747 descriptor,
14748 "NamedSetter",
14749 argumentHandleValue=argumentHandleValue,
14750 tailCode=tailCode,
14754 class CGProxyNamedDeleter(CGProxyNamedOperation):
14756 Class to generate a call to a named deleter.
14758 resultVar: See the docstring for CGCallGenerator.
14760 foundVar: See the docstring for CGProxySpecialOperation.
14763 def __init__(self, descriptor, resultVar=None, foundVar=None):
14764 CGProxyNamedOperation.__init__(
14765 self, descriptor, "NamedDeleter", resultVar=resultVar, foundVar=foundVar
14769 class CGProxyIsProxy(CGAbstractMethod):
14770 def __init__(self, descriptor):
14771 args = [Argument("JSObject*", "obj")]
14772 CGAbstractMethod.__init__(
14773 self, descriptor, "IsProxy", "bool", args, alwaysInline=True
14776 def declare(self):
14777 return ""
14779 def definition_body(self):
14780 return "return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();\n"
14783 class CGProxyUnwrap(CGAbstractMethod):
14784 def __init__(self, descriptor):
14785 args = [Argument("JSObject*", "obj")]
14786 CGAbstractMethod.__init__(
14787 self,
14788 descriptor,
14789 "UnwrapProxy",
14790 descriptor.nativeType + "*",
14791 args,
14792 alwaysInline=True,
14795 def declare(self):
14796 return ""
14798 def definition_body(self):
14799 return fill(
14801 MOZ_ASSERT(js::IsProxy(obj));
14802 if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) {
14803 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj));
14804 obj = js::UncheckedUnwrap(obj);
14806 MOZ_ASSERT(IsProxy(obj));
14807 return static_cast<${type}*>(js::GetProxyReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate());
14808 """,
14809 type=self.descriptor.nativeType,
14813 MISSING_PROP_PREF = "dom.missing_prop_counters.enabled"
14816 def missingPropUseCountersForDescriptor(desc):
14817 if not desc.needsMissingPropUseCounters:
14818 return ""
14820 return fill(
14822 if (StaticPrefs::${pref}() && id.isAtom()) {
14823 CountMaybeMissingProperty(proxy, id);
14826 """,
14827 pref=prefIdentifier(MISSING_PROP_PREF),
14831 def findAncestorWithInstrumentedProps(desc):
14833 Find an ancestor of desc.interface (not including desc.interface
14834 itself) that has instrumented properties on it. May return None
14835 if there is no such ancestor.
14837 ancestor = desc.interface.parent
14838 while ancestor:
14839 if ancestor.getExtendedAttribute("InstrumentedProps"):
14840 return ancestor
14841 ancestor = ancestor.parent
14842 return None
14845 class CGCountMaybeMissingProperty(CGAbstractMethod):
14846 def __init__(self, descriptor):
14848 Returns whether we counted the property involved.
14850 CGAbstractMethod.__init__(
14851 self,
14852 descriptor,
14853 "CountMaybeMissingProperty",
14854 "bool",
14856 Argument("JS::Handle<JSObject*>", "proxy"),
14857 Argument("JS::Handle<jsid>", "id"),
14861 def gen_switch(self, switchDecriptor):
14863 Generate a switch from the switch descriptor. The descriptor
14864 dictionary must have the following properties:
14866 1) A "precondition" property that contains code to run before the
14867 switch statement. Its value ie a string.
14868 2) A "condition" property for the condition. Its value is a string.
14869 3) A "cases" property. Its value is an object that has property names
14870 corresponding to the case labels. The values of those properties
14871 are either new switch descriptor dictionaries (which will then
14872 generate nested switches) or strings to use for case bodies.
14874 cases = []
14875 for label, body in sorted(switchDecriptor["cases"].items()):
14876 if isinstance(body, dict):
14877 body = self.gen_switch(body)
14878 cases.append(
14879 fill(
14881 case ${label}: {
14882 $*{body}
14883 break;
14885 """,
14886 label=label,
14887 body=body,
14890 return fill(
14892 $*{precondition}
14893 switch (${condition}) {
14894 $*{cases}
14896 """,
14897 precondition=switchDecriptor["precondition"],
14898 condition=switchDecriptor["condition"],
14899 cases="".join(cases),
14902 def charSwitch(self, props, charIndex):
14904 Create a switch for the given props, based on the first char where
14905 they start to differ at index charIndex or more. Each prop is a tuple
14906 containing interface name and prop name.
14908 Incoming props should be a sorted list.
14910 if len(props) == 1:
14911 # We're down to one string: just check whether we match it.
14912 return fill(
14914 if (JS_LinearStringEqualsLiteral(str, "${name}")) {
14915 counter.emplace(eUseCounter_${iface}_${name});
14917 """,
14918 iface=self.descriptor.name,
14919 name=props[0],
14922 switch = dict()
14923 if charIndex == 0:
14924 switch["precondition"] = "StringIdChars chars(nogc, str);\n"
14925 else:
14926 switch["precondition"] = ""
14928 # Find the first place where we might actually have a difference.
14929 while all(prop[charIndex] == props[0][charIndex] for prop in props):
14930 charIndex += 1
14932 switch["condition"] = "chars[%d]" % charIndex
14933 switch["cases"] = dict()
14934 current_props = None
14935 curChar = None
14936 idx = 0
14937 while idx < len(props):
14938 nextChar = "'%s'" % props[idx][charIndex]
14939 if nextChar != curChar:
14940 if curChar:
14941 switch["cases"][curChar] = self.charSwitch(
14942 current_props, charIndex + 1
14944 current_props = []
14945 curChar = nextChar
14946 current_props.append(props[idx])
14947 idx += 1
14948 switch["cases"][curChar] = self.charSwitch(current_props, charIndex + 1)
14949 return switch
14951 def definition_body(self):
14952 ancestor = findAncestorWithInstrumentedProps(self.descriptor)
14954 if ancestor:
14955 body = fill(
14957 if (${ancestor}_Binding::CountMaybeMissingProperty(proxy, id)) {
14958 return true;
14961 """,
14962 ancestor=ancestor.identifier.name,
14964 else:
14965 body = ""
14967 instrumentedProps = self.descriptor.instrumentedProps
14968 if not instrumentedProps:
14969 return body + dedent(
14971 return false;
14975 lengths = set(len(prop) for prop in instrumentedProps)
14976 switchDesc = {"condition": "JS::GetLinearStringLength(str)", "precondition": ""}
14977 switchDesc["cases"] = dict()
14978 for length in sorted(lengths):
14979 switchDesc["cases"][str(length)] = self.charSwitch(
14980 list(sorted(prop for prop in instrumentedProps if len(prop) == length)),
14984 return body + fill(
14986 MOZ_ASSERT(StaticPrefs::${pref}() && id.isAtom());
14987 Maybe<UseCounter> counter;
14989 // Scope for our no-GC section, so we don't need to rely on SetUseCounter not GCing.
14990 JS::AutoCheckCannotGC nogc;
14991 JSLinearString* str = JS::AtomToLinearString(id.toAtom());
14992 // Don't waste time fetching the chars until we've done the length switch.
14993 $*{switch}
14995 if (counter) {
14996 SetUseCounter(proxy, *counter);
14997 return true;
15000 return false;
15001 """,
15002 pref=prefIdentifier(MISSING_PROP_PREF),
15003 switch=self.gen_switch(switchDesc),
15007 class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod):
15008 def __init__(self, descriptor):
15009 args = [
15010 Argument("JSContext*", "cx"),
15011 Argument("JS::Handle<JSObject*>", "proxy"),
15012 Argument("JS::Handle<jsid>", "id"),
15013 Argument("bool", "ignoreNamedProps"),
15014 Argument("JS::MutableHandle<Maybe<JS::PropertyDescriptor>>", "desc"),
15016 ClassMethod.__init__(
15017 self,
15018 "getOwnPropDescriptor",
15019 "bool",
15020 args,
15021 virtual=True,
15022 override=True,
15023 const=True,
15025 self.descriptor = descriptor
15027 def getBody(self):
15028 indexedSetter = self.descriptor.operations["IndexedSetter"]
15030 if self.descriptor.isMaybeCrossOriginObject():
15031 xrayDecl = dedent(
15033 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
15034 MOZ_ASSERT(IsPlatformObjectSameOrigin(cx, proxy),
15035 "getOwnPropertyDescriptor() and set() should have dealt");
15036 MOZ_ASSERT(js::IsObjectInContextCompartment(proxy, cx),
15037 "getOwnPropertyDescriptor() and set() should have dealt");
15041 xrayCheck = ""
15042 else:
15043 xrayDecl = "bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);\n"
15044 xrayCheck = "!isXray &&"
15046 if self.descriptor.supportsIndexedProperties():
15047 attributes = [
15048 "JS::PropertyAttribute::Configurable",
15049 "JS::PropertyAttribute::Enumerable",
15051 if indexedSetter is not None:
15052 attributes.append("JS::PropertyAttribute::Writable")
15053 setDescriptor = (
15054 "desc.set(mozilla::Some(JS::PropertyDescriptor::Data(value, { %s })));\nreturn true;\n"
15055 % ", ".join(attributes)
15057 templateValues = {
15058 "jsvalRef": "value",
15059 "jsvalHandle": "&value",
15060 "obj": "proxy",
15061 "successCode": setDescriptor,
15063 getIndexed = fill(
15065 uint32_t index = GetArrayIndexFromId(id);
15066 if (IsArrayIndex(index)) {
15067 JS::Rooted<JS::Value> value(cx);
15068 $*{callGetter}
15071 """,
15072 callGetter=CGProxyIndexedGetter(
15073 self.descriptor, templateValues
15074 ).define(),
15076 else:
15077 getIndexed = ""
15079 missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor)
15081 if self.descriptor.supportsNamedProperties():
15082 operations = self.descriptor.operations
15083 attributes = ["JS::PropertyAttribute::Configurable"]
15084 if self.descriptor.namedPropertiesEnumerable:
15085 attributes.append("JS::PropertyAttribute::Enumerable")
15086 if operations["NamedSetter"] is not None:
15087 attributes.append("JS::PropertyAttribute::Writable")
15088 setDescriptor = (
15089 "desc.set(mozilla::Some(JS::PropertyDescriptor::Data(value, { %s })));\nreturn true;\n"
15090 % ", ".join(attributes)
15092 templateValues = {
15093 "jsvalRef": "value",
15094 "jsvalHandle": "&value",
15095 "obj": "proxy",
15096 "successCode": setDescriptor,
15099 computeCondition = dedent(
15101 bool hasOnProto;
15102 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
15103 return false;
15105 callNamedGetter = !hasOnProto;
15108 if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
15109 computeCondition = fill(
15111 if (!isXray) {
15112 callNamedGetter = true;
15113 } else {
15114 $*{hasOnProto}
15116 """,
15117 hasOnProto=computeCondition,
15120 outerCondition = "!ignoreNamedProps"
15121 if self.descriptor.supportsIndexedProperties():
15122 outerCondition = "!IsArrayIndex(index) && " + outerCondition
15124 namedGetCode = CGProxyNamedGetter(self.descriptor, templateValues).define()
15125 namedGet = fill(
15127 bool callNamedGetter = false;
15128 if (${outerCondition}) {
15129 $*{computeCondition}
15131 if (callNamedGetter) {
15132 JS::Rooted<JS::Value> value(cx);
15133 $*{namedGetCode}
15135 """,
15136 outerCondition=outerCondition,
15137 computeCondition=computeCondition,
15138 namedGetCode=namedGetCode,
15140 namedGet += "\n"
15141 else:
15142 namedGet = ""
15144 return fill(
15146 $*{xrayDecl}
15147 $*{getIndexed}
15148 $*{missingPropUseCounters}
15149 JS::Rooted<JSObject*> expando(cx);
15150 if (${xrayCheck}(expando = GetExpandoObject(proxy))) {
15151 if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc)) {
15152 return false;
15154 if (desc.isSome()) {
15155 return true;
15159 $*{namedGet}
15160 desc.reset();
15161 return true;
15162 """,
15163 xrayDecl=xrayDecl,
15164 xrayCheck=xrayCheck,
15165 getIndexed=getIndexed,
15166 missingPropUseCounters=missingPropUseCounters,
15167 namedGet=namedGet,
15171 class CGDOMJSProxyHandler_defineProperty(ClassMethod):
15172 def __init__(self, descriptor):
15173 # The usual convention is to name the ObjectOpResult out-parameter
15174 # `result`, but that name is a bit overloaded around here.
15175 args = [
15176 Argument("JSContext*", "cx_"),
15177 Argument("JS::Handle<JSObject*>", "proxy"),
15178 Argument("JS::Handle<jsid>", "id"),
15179 Argument("JS::Handle<JS::PropertyDescriptor>", "desc"),
15180 Argument("JS::ObjectOpResult&", "opresult"),
15181 Argument("bool*", "done"),
15183 ClassMethod.__init__(
15184 self,
15185 "defineProperty",
15186 "bool",
15187 args,
15188 virtual=True,
15189 override=True,
15190 const=True,
15192 self.descriptor = descriptor
15194 def getBody(self):
15195 set = ""
15197 indexedSetter = self.descriptor.operations["IndexedSetter"]
15198 if indexedSetter:
15199 error_label = CGSpecializedMethod.error_reporting_label_helper(
15200 self.descriptor, indexedSetter, isConstructor=False
15202 if error_label:
15203 cxDecl = fill(
15205 BindingCallContext cx(cx_, ${error_label});
15206 """,
15207 error_label=error_label,
15209 else:
15210 cxDecl = dedent(
15212 JSContext* cx = cx_;
15215 set += fill(
15217 uint32_t index = GetArrayIndexFromId(id);
15218 if (IsArrayIndex(index)) {
15219 $*{cxDecl}
15220 *done = true;
15221 // https://webidl.spec.whatwg.org/#legacy-platform-object-defineownproperty
15222 // Step 1.1. The no-indexed-setter case is handled by step 1.2.
15223 if (!desc.isDataDescriptor()) {
15224 return opresult.failNotDataDescriptor();
15227 $*{callSetter}
15228 return opresult.succeed();
15230 """,
15231 cxDecl=cxDecl,
15232 callSetter=CGProxyIndexedSetter(self.descriptor).define(),
15234 elif self.descriptor.supportsIndexedProperties():
15235 # We allow untrusted content to prevent Xrays from setting a
15236 # property if that property is an indexed property and we have no
15237 # indexed setter. That's how the object would normally behave if
15238 # you tried to set the property on it. That means we don't need to
15239 # do anything special for Xrays here.
15240 set += dedent(
15242 if (IsArrayIndex(GetArrayIndexFromId(id))) {
15243 *done = true;
15244 return opresult.failNoIndexedSetter();
15249 namedSetter = self.descriptor.operations["NamedSetter"]
15250 if namedSetter:
15251 error_label = CGSpecializedMethod.error_reporting_label_helper(
15252 self.descriptor, namedSetter, isConstructor=False
15254 if error_label:
15255 set += fill(
15257 BindingCallContext cx(cx_, ${error_label});
15258 """,
15259 error_label=error_label,
15261 else:
15262 set += dedent(
15264 JSContext* cx = cx_;
15267 if self.descriptor.hasLegacyUnforgeableMembers:
15268 raise TypeError(
15269 "Can't handle a named setter on an interface "
15270 "that has unforgeables. Figure out how that "
15271 "should work!"
15273 set += dedent(
15275 // https://webidl.spec.whatwg.org/#legacy-platform-object-defineownproperty
15276 // Step 2.2.2.1.
15277 if (!desc.isDataDescriptor()) {
15278 *done = true;
15279 return opresult.failNotDataDescriptor();
15283 tailCode = dedent(
15285 *done = true;
15286 return opresult.succeed();
15289 set += CGProxyNamedSetter(self.descriptor, tailCode).define()
15290 else:
15291 # We allow untrusted content to prevent Xrays from setting a
15292 # property if that property is already a named property on the
15293 # object and we have no named setter. That's how the object would
15294 # normally behave if you tried to set the property on it. That
15295 # means we don't need to do anything special for Xrays here.
15296 if self.descriptor.supportsNamedProperties():
15297 set += fill(
15299 JSContext* cx = cx_;
15300 bool found = false;
15301 $*{presenceChecker}
15303 if (found) {
15304 *done = true;
15305 return opresult.failNoNamedSetter();
15307 """,
15308 presenceChecker=CGProxyNamedPresenceChecker(
15309 self.descriptor, foundVar="found"
15310 ).define(),
15312 if self.descriptor.isMaybeCrossOriginObject():
15313 set += dedent(
15315 MOZ_ASSERT(IsPlatformObjectSameOrigin(cx_, proxy),
15316 "Why did the MaybeCrossOriginObject defineProperty override fail?");
15317 MOZ_ASSERT(js::IsObjectInContextCompartment(proxy, cx_),
15318 "Why did the MaybeCrossOriginObject defineProperty override fail?");
15322 # In all cases we want to tail-call to our base class; we can
15323 # always land here for symbols.
15324 set += (
15325 "return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n"
15326 % ", ".join(a.name for a in self.args)
15328 return set
15331 def getDeleterBody(descriptor, type, foundVar=None):
15333 type should be "Named" or "Indexed"
15335 The possible outcomes:
15336 - an error happened (the emitted code returns false)
15337 - own property not found (foundVar=false, deleteSucceeded=true)
15338 - own property found and deleted (foundVar=true, deleteSucceeded=true)
15339 - own property found but can't be deleted (foundVar=true, deleteSucceeded=false)
15341 assert type in ("Named", "Indexed")
15342 deleter = descriptor.operations[type + "Deleter"]
15343 if deleter:
15344 assert type == "Named"
15345 assert foundVar is not None
15346 if descriptor.hasLegacyUnforgeableMembers:
15347 raise TypeError(
15348 "Can't handle a deleter on an interface "
15349 "that has unforgeables. Figure out how "
15350 "that should work!"
15352 # See if the deleter method is fallible.
15353 t = deleter.signatures()[0][0]
15354 if t.isPrimitive() and not t.nullable() and t.tag() == IDLType.Tags.bool:
15355 # The deleter method has a boolean return value. When a
15356 # property is found, the return value indicates whether it
15357 # was successfully deleted.
15358 setDS = fill(
15360 if (!${foundVar}) {
15361 deleteSucceeded = true;
15363 """,
15364 foundVar=foundVar,
15366 else:
15367 # No boolean return value: if a property is found,
15368 # deleting it always succeeds.
15369 setDS = "deleteSucceeded = true;\n"
15371 body = (
15372 CGProxyNamedDeleter(
15373 descriptor, resultVar="deleteSucceeded", foundVar=foundVar
15374 ).define()
15375 + setDS
15377 elif getattr(descriptor, "supports%sProperties" % type)():
15378 presenceCheckerClass = globals()["CGProxy%sPresenceChecker" % type]
15379 foundDecl = ""
15380 if foundVar is None:
15381 foundVar = "found"
15382 foundDecl = "bool found = false;\n"
15383 body = fill(
15385 $*{foundDecl}
15386 $*{presenceChecker}
15387 deleteSucceeded = !${foundVar};
15388 """,
15389 foundDecl=foundDecl,
15390 presenceChecker=presenceCheckerClass(
15391 descriptor, foundVar=foundVar
15392 ).define(),
15393 foundVar=foundVar,
15395 else:
15396 body = None
15397 return body
15400 class CGDeleteNamedProperty(CGAbstractStaticMethod):
15401 def __init__(self, descriptor):
15402 args = [
15403 Argument("JSContext*", "cx"),
15404 Argument("JS::Handle<JSObject*>", "xray"),
15405 Argument("JS::Handle<JSObject*>", "proxy"),
15406 Argument("JS::Handle<jsid>", "id"),
15407 Argument("JS::ObjectOpResult&", "opresult"),
15409 CGAbstractStaticMethod.__init__(
15410 self, descriptor, "DeleteNamedProperty", "bool", args
15413 def definition_body(self):
15414 return fill(
15416 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(xray));
15417 MOZ_ASSERT(js::IsProxy(proxy));
15418 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
15419 JSAutoRealm ar(cx, proxy);
15420 bool deleteSucceeded = false;
15421 bool found = false;
15422 $*{namedBody}
15423 if (!found || deleteSucceeded) {
15424 return opresult.succeed();
15426 return opresult.failCantDelete();
15427 """,
15428 namedBody=getDeleterBody(self.descriptor, "Named", foundVar="found"),
15432 class CGDOMJSProxyHandler_delete(ClassMethod):
15433 def __init__(self, descriptor):
15434 args = [
15435 Argument("JSContext*", "cx"),
15436 Argument("JS::Handle<JSObject*>", "proxy"),
15437 Argument("JS::Handle<jsid>", "id"),
15438 Argument("JS::ObjectOpResult&", "opresult"),
15440 ClassMethod.__init__(
15441 self, "delete_", "bool", args, virtual=True, override=True, const=True
15443 self.descriptor = descriptor
15445 def getBody(self):
15446 delete = dedent(
15448 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
15449 "Should not have a XrayWrapper here");
15454 if self.descriptor.isMaybeCrossOriginObject():
15455 delete += dedent(
15457 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
15458 return ReportCrossOriginDenial(cx, id, "delete"_ns);
15461 // Safe to enter the Realm of proxy now.
15462 JSAutoRealm ar(cx, proxy);
15463 JS_MarkCrossZoneId(cx, id);
15467 indexedBody = getDeleterBody(self.descriptor, "Indexed")
15468 if indexedBody is not None:
15469 # Can't handle cross-origin objects here.
15470 assert not self.descriptor.isMaybeCrossOriginObject()
15471 delete += fill(
15473 uint32_t index = GetArrayIndexFromId(id);
15474 if (IsArrayIndex(index)) {
15475 bool deleteSucceeded;
15476 $*{indexedBody}
15477 return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
15479 """,
15480 indexedBody=indexedBody,
15483 namedBody = getDeleterBody(self.descriptor, "Named", foundVar="found")
15484 if namedBody is not None:
15485 delete += dedent(
15487 // Try named delete only if the named property visibility
15488 // algorithm says the property is visible.
15489 bool tryNamedDelete = true;
15490 { // Scope for expando
15491 JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
15492 if (expando) {
15493 bool hasProp;
15494 if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
15495 return false;
15497 tryNamedDelete = !hasProp;
15503 if not self.descriptor.interface.getExtendedAttribute(
15504 "LegacyOverrideBuiltIns"
15506 delete += dedent(
15508 if (tryNamedDelete) {
15509 bool hasOnProto;
15510 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
15511 return false;
15513 tryNamedDelete = !hasOnProto;
15518 # We always return above for an index id in the case when we support
15519 # indexed properties, so we can just treat the id as a name
15520 # unconditionally here.
15521 delete += fill(
15523 if (tryNamedDelete) {
15524 bool found = false;
15525 bool deleteSucceeded;
15526 $*{namedBody}
15527 if (found) {
15528 return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
15531 """,
15532 namedBody=namedBody,
15535 delete += dedent(
15538 return dom::DOMProxyHandler::delete_(cx, proxy, id, opresult);
15542 return delete
15545 class CGDOMJSProxyHandler_ownPropNames(ClassMethod):
15546 def __init__(
15547 self,
15548 descriptor,
15550 args = [
15551 Argument("JSContext*", "cx"),
15552 Argument("JS::Handle<JSObject*>", "proxy"),
15553 Argument("unsigned", "flags"),
15554 Argument("JS::MutableHandleVector<jsid>", "props"),
15556 ClassMethod.__init__(
15557 self, "ownPropNames", "bool", args, virtual=True, override=True, const=True
15559 self.descriptor = descriptor
15561 def getBody(self):
15562 if self.descriptor.isMaybeCrossOriginObject():
15563 xrayDecl = dedent(
15565 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
15566 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
15567 if (!(flags & JSITER_HIDDEN)) {
15568 // There are no enumerable cross-origin props, so we're done.
15569 return true;
15572 JS::Rooted<JSObject*> holder(cx);
15573 if (!EnsureHolder(cx, proxy, &holder)) {
15574 return false;
15577 if (!js::GetPropertyKeys(cx, holder, flags, props)) {
15578 return false;
15581 return xpc::AppendCrossOriginWhitelistedPropNames(cx, props);
15586 xrayCheck = ""
15587 else:
15588 xrayDecl = "bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);\n"
15589 xrayCheck = "!isXray &&"
15591 # Per spec, we do indices, then named props, then everything else.
15592 if self.descriptor.supportsIndexedProperties():
15593 if self.descriptor.lengthNeedsCallerType():
15594 callerType = callerTypeGetterForDescriptor(self.descriptor)
15595 else:
15596 callerType = ""
15597 addIndices = fill(
15600 uint32_t length = UnwrapProxy(proxy)->Length(${callerType});
15601 MOZ_ASSERT(int32_t(length) >= 0);
15602 for (int32_t i = 0; i < int32_t(length); ++i) {
15603 if (!props.append(JS::PropertyKey::Int(i))) {
15604 return false;
15607 """,
15608 callerType=callerType,
15610 else:
15611 addIndices = ""
15613 if self.descriptor.supportsNamedProperties():
15614 if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
15615 shadow = "!isXray"
15616 else:
15617 shadow = "false"
15619 if self.descriptor.supportedNamesNeedCallerType():
15620 callerType = ", " + callerTypeGetterForDescriptor(self.descriptor)
15621 else:
15622 callerType = ""
15624 addNames = fill(
15626 nsTArray<nsString> names;
15627 UnwrapProxy(proxy)->GetSupportedNames(names${callerType});
15628 if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) {
15629 return false;
15631 """,
15632 callerType=callerType,
15633 shadow=shadow,
15635 if not self.descriptor.namedPropertiesEnumerable:
15636 addNames = CGIfWrapper(
15637 CGGeneric(addNames), "flags & JSITER_HIDDEN"
15638 ).define()
15639 addNames = "\n" + addNames
15640 else:
15641 addNames = ""
15643 addExpandoProps = fill(
15645 JS::Rooted<JSObject*> expando(cx);
15646 if (${xrayCheck}(expando = DOMProxyHandler::GetExpandoObject(proxy)) &&
15647 !js::GetPropertyKeys(cx, expando, flags, props)) {
15648 return false;
15650 """,
15651 xrayCheck=xrayCheck,
15654 if self.descriptor.isMaybeCrossOriginObject():
15655 # We need to enter our compartment (which we might not be
15656 # in right now) to get the expando props.
15657 addExpandoProps = fill(
15659 { // Scope for accessing the expando.
15660 // Safe to enter our compartment, because IsPlatformObjectSameOrigin tested true.
15661 JSAutoRealm ar(cx, proxy);
15662 $*{addExpandoProps}
15664 for (auto& id : props) {
15665 JS_MarkCrossZoneId(cx, id);
15667 """,
15668 addExpandoProps=addExpandoProps,
15671 return fill(
15673 $*{xrayDecl}
15674 $*{addIndices}
15675 $*{addNames}
15677 $*{addExpandoProps}
15679 return true;
15680 """,
15681 xrayDecl=xrayDecl,
15682 addIndices=addIndices,
15683 addNames=addNames,
15684 addExpandoProps=addExpandoProps,
15688 class CGDOMJSProxyHandler_hasOwn(ClassMethod):
15689 def __init__(self, descriptor):
15690 args = [
15691 Argument("JSContext*", "cx"),
15692 Argument("JS::Handle<JSObject*>", "proxy"),
15693 Argument("JS::Handle<jsid>", "id"),
15694 Argument("bool*", "bp"),
15696 ClassMethod.__init__(
15697 self, "hasOwn", "bool", args, virtual=True, override=True, const=True
15699 self.descriptor = descriptor
15701 def getBody(self):
15702 if self.descriptor.isMaybeCrossOriginObject():
15703 maybeCrossOrigin = dedent(
15705 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
15706 // Just hand this off to BaseProxyHandler to do the slow-path thing.
15707 // The BaseProxyHandler code is OK with this happening without entering the
15708 // compartment of "proxy", which is important to get the right answers.
15709 return js::BaseProxyHandler::hasOwn(cx, proxy, id, bp);
15712 // Now safe to enter the Realm of proxy and do the rest of the work there.
15713 JSAutoRealm ar(cx, proxy);
15714 JS_MarkCrossZoneId(cx, id);
15717 else:
15718 maybeCrossOrigin = ""
15720 if self.descriptor.supportsIndexedProperties():
15721 indexed = fill(
15723 uint32_t index = GetArrayIndexFromId(id);
15724 if (IsArrayIndex(index)) {
15725 bool found = false;
15726 $*{presenceChecker}
15728 *bp = found;
15729 return true;
15732 """,
15733 presenceChecker=CGProxyIndexedPresenceChecker(
15734 self.descriptor, foundVar="found"
15735 ).define(),
15737 else:
15738 indexed = ""
15740 if self.descriptor.supportsNamedProperties():
15741 # If we support indexed properties we always return above for index
15742 # property names, so no need to check for those here.
15743 named = fill(
15745 bool found = false;
15746 $*{presenceChecker}
15748 *bp = found;
15749 """,
15750 presenceChecker=CGProxyNamedPresenceChecker(
15751 self.descriptor, foundVar="found"
15752 ).define(),
15754 if not self.descriptor.interface.getExtendedAttribute(
15755 "LegacyOverrideBuiltIns"
15757 named = fill(
15759 bool hasOnProto;
15760 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
15761 return false;
15763 if (!hasOnProto) {
15764 $*{protoLacksProperty}
15765 return true;
15767 """,
15768 protoLacksProperty=named,
15770 named += "*bp = false;\n"
15771 else:
15772 named += "\n"
15773 else:
15774 named = "*bp = false;\n"
15776 missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor)
15778 return fill(
15780 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
15781 "Should not have a XrayWrapper here");
15782 $*{maybeCrossOrigin}
15783 $*{indexed}
15785 $*{missingPropUseCounters}
15786 JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy));
15787 if (expando) {
15788 bool b = true;
15789 bool ok = JS_HasPropertyById(cx, expando, id, &b);
15790 *bp = !!b;
15791 if (!ok || *bp) {
15792 return ok;
15796 $*{named}
15797 return true;
15798 """,
15799 maybeCrossOrigin=maybeCrossOrigin,
15800 indexed=indexed,
15801 missingPropUseCounters=missingPropUseCounters,
15802 named=named,
15806 class CGDOMJSProxyHandler_get(ClassMethod):
15807 def __init__(self, descriptor):
15808 args = [
15809 Argument("JSContext*", "cx"),
15810 Argument("JS::Handle<JSObject*>", "proxy"),
15811 Argument("JS::Handle<JS::Value>", "receiver"),
15812 Argument("JS::Handle<jsid>", "id"),
15813 Argument("JS::MutableHandle<JS::Value>", "vp"),
15815 ClassMethod.__init__(
15816 self, "get", "bool", args, virtual=True, override=True, const=True
15818 self.descriptor = descriptor
15820 def getBody(self):
15821 missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor)
15823 getUnforgeableOrExpando = dedent(
15825 bool expandoHasProp = false;
15826 { // Scope for expando
15827 JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
15828 if (expando) {
15829 if (!JS_HasPropertyById(cx, expando, id, &expandoHasProp)) {
15830 return false;
15833 if (expandoHasProp) {
15834 // Forward the get to the expando object, but our receiver is whatever our
15835 // receiver is.
15836 if (!JS_ForwardGetPropertyTo(cx, expando, id, ${receiver}, vp)) {
15837 return false;
15845 getOnPrototype = dedent(
15847 bool foundOnPrototype;
15848 if (!GetPropertyOnPrototype(cx, proxy, ${receiver}, id, &foundOnPrototype, vp)) {
15849 return false;
15854 if self.descriptor.isMaybeCrossOriginObject():
15855 # We can't handle these for cross-origin objects
15856 assert not self.descriptor.supportsIndexedProperties()
15857 assert not self.descriptor.supportsNamedProperties()
15859 return fill(
15861 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
15862 "Should not have a XrayWrapper here");
15864 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
15865 return CrossOriginGet(cx, proxy, receiver, id, vp);
15868 $*{missingPropUseCounters}
15869 { // Scope for the JSAutoRealm accessing expando and prototype.
15870 JSAutoRealm ar(cx, proxy);
15871 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
15872 if (!MaybeWrapValue(cx, &wrappedReceiver)) {
15873 return false;
15875 JS_MarkCrossZoneId(cx, id);
15877 $*{getUnforgeableOrExpando}
15878 if (!expandoHasProp) {
15879 $*{getOnPrototype}
15880 if (!foundOnPrototype) {
15881 MOZ_ASSERT(vp.isUndefined());
15882 return true;
15887 return MaybeWrapValue(cx, vp);
15888 """,
15889 missingPropUseCounters=missingPropUseCountersForDescriptor(
15890 self.descriptor
15892 getUnforgeableOrExpando=fill(
15893 getUnforgeableOrExpando, receiver="wrappedReceiver"
15895 getOnPrototype=fill(getOnPrototype, receiver="wrappedReceiver"),
15898 templateValues = {"jsvalRef": "vp", "jsvalHandle": "vp", "obj": "proxy"}
15900 getUnforgeableOrExpando = fill(
15901 getUnforgeableOrExpando, receiver="receiver"
15902 ) + dedent(
15905 if (expandoHasProp) {
15906 return true;
15910 if self.descriptor.supportsIndexedProperties():
15911 getIndexedOrExpando = fill(
15913 uint32_t index = GetArrayIndexFromId(id);
15914 if (IsArrayIndex(index)) {
15915 $*{callGetter}
15916 // Even if we don't have this index, we don't forward the
15917 // get on to our expando object.
15918 } else {
15919 $*{getUnforgeableOrExpando}
15921 """,
15922 callGetter=CGProxyIndexedGetter(
15923 self.descriptor, templateValues
15924 ).define(),
15925 getUnforgeableOrExpando=getUnforgeableOrExpando,
15927 else:
15928 getIndexedOrExpando = getUnforgeableOrExpando
15930 if self.descriptor.supportsNamedProperties():
15931 getNamed = CGProxyNamedGetter(self.descriptor, templateValues)
15932 if self.descriptor.supportsIndexedProperties():
15933 getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)")
15934 getNamed = getNamed.define() + "\n"
15935 else:
15936 getNamed = ""
15938 getOnPrototype = fill(getOnPrototype, receiver="receiver") + dedent(
15941 if (foundOnPrototype) {
15942 return true;
15945 MOZ_ASSERT(vp.isUndefined());
15949 if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
15950 getNamedOrOnPrototype = getNamed + getOnPrototype
15951 else:
15952 getNamedOrOnPrototype = getOnPrototype + getNamed
15954 return fill(
15956 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
15957 "Should not have a XrayWrapper here");
15959 $*{missingPropUseCounters}
15960 $*{indexedOrExpando}
15962 $*{namedOrOnPropotype}
15963 return true;
15964 """,
15965 missingPropUseCounters=missingPropUseCounters,
15966 indexedOrExpando=getIndexedOrExpando,
15967 namedOrOnPropotype=getNamedOrOnPrototype,
15971 class CGDOMJSProxyHandler_setCustom(ClassMethod):
15972 def __init__(self, descriptor):
15973 args = [
15974 Argument("JSContext*", "cx_"),
15975 Argument("JS::Handle<JSObject*>", "proxy"),
15976 Argument("JS::Handle<jsid>", "id"),
15977 Argument("JS::Handle<JS::Value>", "v"),
15978 Argument("bool*", "done"),
15980 ClassMethod.__init__(
15981 self, "setCustom", "bool", args, virtual=True, override=True, const=True
15983 self.descriptor = descriptor
15985 def getBody(self):
15986 assertion = (
15987 "MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),\n"
15988 ' "Should not have a XrayWrapper here");\n'
15991 # Correctness first. If we have a NamedSetter and [LegacyOverrideBuiltIns],
15992 # always call the NamedSetter and never do anything else.
15993 namedSetter = self.descriptor.operations["NamedSetter"]
15994 if namedSetter is not None and self.descriptor.interface.getExtendedAttribute(
15995 "LegacyOverrideBuiltIns"
15997 # Check assumptions.
15998 if self.descriptor.supportsIndexedProperties():
15999 raise ValueError(
16000 "In interface "
16001 + self.descriptor.name
16002 + ": "
16003 + "Can't cope with [LegacyOverrideBuiltIns] and an indexed getter"
16005 if self.descriptor.hasLegacyUnforgeableMembers:
16006 raise ValueError(
16007 "In interface "
16008 + self.descriptor.name
16009 + ": "
16010 + "Can't cope with [LegacyOverrideBuiltIns] and unforgeable members"
16013 tailCode = dedent(
16015 *done = true;
16016 return true;
16019 callSetter = CGProxyNamedSetter(
16020 self.descriptor, tailCode, argumentHandleValue="v"
16022 error_label = CGSpecializedMethod.error_reporting_label_helper(
16023 self.descriptor, namedSetter, isConstructor=False
16025 if error_label:
16026 cxDecl = fill(
16028 BindingCallContext cx(cx_, ${error_label});
16029 """,
16030 error_label=error_label,
16032 else:
16033 cxDecl = dedent(
16035 JSContext* cx = cx_;
16038 return fill(
16040 $*{assertion}
16041 $*{cxDecl}
16042 $*{callSetter}
16043 *done = false;
16044 return true;
16045 """,
16046 assertion=assertion,
16047 cxDecl=cxDecl,
16048 callSetter=callSetter.define(),
16051 # As an optimization, if we are going to call an IndexedSetter, go
16052 # ahead and call it and have done.
16053 indexedSetter = self.descriptor.operations["IndexedSetter"]
16054 if indexedSetter is not None:
16055 error_label = CGSpecializedMethod.error_reporting_label_helper(
16056 self.descriptor, indexedSetter, isConstructor=False
16058 if error_label:
16059 cxDecl = fill(
16061 BindingCallContext cx(cx_, ${error_label});
16062 """,
16063 error_label=error_label,
16065 else:
16066 cxDecl = dedent(
16068 JSContext* cx = cx_;
16071 setIndexed = fill(
16073 uint32_t index = GetArrayIndexFromId(id);
16074 if (IsArrayIndex(index)) {
16075 $*{cxDecl}
16076 $*{callSetter}
16077 *done = true;
16078 return true;
16081 """,
16082 cxDecl=cxDecl,
16083 callSetter=CGProxyIndexedSetter(
16084 self.descriptor, argumentHandleValue="v"
16085 ).define(),
16087 else:
16088 setIndexed = ""
16090 return assertion + setIndexed + "*done = false;\n" "return true;\n"
16093 class CGDOMJSProxyHandler_className(ClassMethod):
16094 def __init__(self, descriptor):
16095 args = [
16096 Argument("JSContext*", "cx"),
16097 Argument("JS::Handle<JSObject*>", "proxy"),
16099 ClassMethod.__init__(
16100 self,
16101 "className",
16102 "const char*",
16103 args,
16104 virtual=True,
16105 override=True,
16106 const=True,
16108 self.descriptor = descriptor
16110 def getBody(self):
16111 if self.descriptor.isMaybeCrossOriginObject():
16112 crossOrigin = dedent(
16114 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
16115 return "Object";
16120 else:
16121 crossOrigin = ""
16122 return fill(
16124 $*{crossOrigin}
16125 return "${name}";
16126 """,
16127 crossOrigin=crossOrigin,
16128 name=self.descriptor.name,
16132 class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod):
16133 def __init__(self, descriptor):
16134 args = [Argument("const JS::Value&", "priv")]
16135 ClassMethod.__init__(
16136 self,
16137 "finalizeInBackground",
16138 "bool",
16139 args,
16140 virtual=True,
16141 override=True,
16142 const=True,
16144 self.descriptor = descriptor
16146 def getBody(self):
16147 return "return false;\n"
16150 class CGDOMJSProxyHandler_finalize(ClassMethod):
16151 def __init__(self, descriptor):
16152 args = [Argument("JS::GCContext*", "gcx"), Argument("JSObject*", "proxy")]
16153 ClassMethod.__init__(
16154 self, "finalize", "void", args, virtual=True, override=True, const=True
16156 self.descriptor = descriptor
16158 def getBody(self):
16159 return (
16160 "%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(proxy);\n"
16161 % (self.descriptor.nativeType, self.descriptor.nativeType)
16162 ) + finalizeHook(
16163 self.descriptor,
16164 FINALIZE_HOOK_NAME,
16165 self.args[0].name,
16166 self.args[1].name,
16167 ).define()
16170 class CGDOMJSProxyHandler_objectMoved(ClassMethod):
16171 def __init__(self, descriptor):
16172 args = [Argument("JSObject*", "obj"), Argument("JSObject*", "old")]
16173 ClassMethod.__init__(
16174 self, "objectMoved", "size_t", args, virtual=True, override=True, const=True
16176 self.descriptor = descriptor
16178 def getBody(self):
16179 return (
16180 "%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n"
16181 % (self.descriptor.nativeType, self.descriptor.nativeType)
16182 ) + objectMovedHook(
16183 self.descriptor,
16184 OBJECT_MOVED_HOOK_NAME,
16185 self.args[0].name,
16186 self.args[1].name,
16190 class CGDOMJSProxyHandler_getElements(ClassMethod):
16191 def __init__(self, descriptor):
16192 assert descriptor.supportsIndexedProperties()
16194 args = [
16195 Argument("JSContext*", "cx"),
16196 Argument("JS::Handle<JSObject*>", "proxy"),
16197 Argument("uint32_t", "begin"),
16198 Argument("uint32_t", "end"),
16199 Argument("js::ElementAdder*", "adder"),
16201 ClassMethod.__init__(
16202 self, "getElements", "bool", args, virtual=True, override=True, const=True
16204 self.descriptor = descriptor
16206 def getBody(self):
16207 # Just like ownPropertyKeys we'll assume that we have no holes, so
16208 # we have all properties from 0 to length. If that ever changes
16209 # (unlikely), we'll need to do something a bit more clever with how we
16210 # forward on to our ancestor.
16212 templateValues = {
16213 "jsvalRef": "temp",
16214 "jsvalHandle": "&temp",
16215 "obj": "proxy",
16216 "successCode": (
16217 "if (!adder->append(cx, temp)) return false;\n" "continue;\n"
16220 get = CGProxyIndexedGetter(
16221 self.descriptor, templateValues, False, False
16222 ).define()
16224 if self.descriptor.lengthNeedsCallerType():
16225 callerType = callerTypeGetterForDescriptor(self.descriptor)
16226 else:
16227 callerType = ""
16229 return fill(
16231 JS::Rooted<JS::Value> temp(cx);
16232 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
16233 "Should not have a XrayWrapper here");
16235 ${nativeType}* self = UnwrapProxy(proxy);
16236 uint32_t length = self->Length(${callerType});
16237 // Compute the end of the indices we'll get ourselves
16238 uint32_t ourEnd = std::max(begin, std::min(end, length));
16240 for (uint32_t index = begin; index < ourEnd; ++index) {
16241 $*{get}
16244 if (end > ourEnd) {
16245 JS::Rooted<JSObject*> proto(cx);
16246 if (!js::GetObjectProto(cx, proxy, &proto)) {
16247 return false;
16249 return js::GetElementsWithAdder(cx, proto, proxy, ourEnd, end, adder);
16252 return true;
16253 """,
16254 nativeType=self.descriptor.nativeType,
16255 callerType=callerType,
16256 get=get,
16260 class CGJSProxyHandler_getInstance(ClassMethod):
16261 def __init__(self, type):
16262 self.type = type
16263 ClassMethod.__init__(
16264 self, "getInstance", "const %s*" % self.type, [], static=True
16267 def getBody(self):
16268 return fill(
16270 static const ${type} instance;
16271 return &instance;
16272 """,
16273 type=self.type,
16277 class CGDOMJSProxyHandler_call(ClassMethod):
16278 def __init__(self):
16279 args = [
16280 Argument("JSContext*", "cx"),
16281 Argument("JS::Handle<JSObject*>", "proxy"),
16282 Argument("const JS::CallArgs&", "args"),
16285 ClassMethod.__init__(
16286 self, "call", "bool", args, virtual=True, override=True, const=True
16289 def getBody(self):
16290 return fill(
16292 return js::ForwardToNative(cx, ${legacyCaller}, args);
16293 """,
16294 legacyCaller=LEGACYCALLER_HOOK_NAME,
16298 class CGDOMJSProxyHandler_isCallable(ClassMethod):
16299 def __init__(self):
16300 ClassMethod.__init__(
16301 self,
16302 "isCallable",
16303 "bool",
16304 [Argument("JSObject*", "obj")],
16305 virtual=True,
16306 override=True,
16307 const=True,
16310 def getBody(self):
16311 return dedent(
16313 return true;
16318 class CGDOMJSProxyHandler_canNurseryAllocate(ClassMethod):
16320 Override the default canNurseryAllocate in BaseProxyHandler, for cases when
16321 we should be nursery-allocated.
16324 def __init__(self):
16325 ClassMethod.__init__(
16326 self,
16327 "canNurseryAllocate",
16328 "bool",
16330 virtual=True,
16331 override=True,
16332 const=True,
16335 def getBody(self):
16336 return dedent(
16338 return true;
16343 class CGDOMJSProxyHandler_getOwnPropertyDescriptor(ClassMethod):
16345 Implementation of getOwnPropertyDescriptor. We only use this for
16346 cross-origin objects.
16349 def __init__(self, descriptor):
16350 assert descriptor.isMaybeCrossOriginObject()
16352 args = [
16353 Argument("JSContext*", "cx"),
16354 Argument("JS::Handle<JSObject*>", "proxy"),
16355 Argument("JS::Handle<jsid>", "id"),
16356 Argument("JS::MutableHandle<Maybe<JS::PropertyDescriptor>>", "desc"),
16358 ClassMethod.__init__(
16359 self,
16360 "getOwnPropertyDescriptor",
16361 "bool",
16362 args,
16363 virtual=True,
16364 override=True,
16365 const=True,
16367 self.descriptor = descriptor
16369 def getBody(self):
16370 return dedent(
16372 // Implementation of <https://html.spec.whatwg.org/multipage/history.html#location-getownproperty>.
16373 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
16375 // Step 1.
16376 if (IsPlatformObjectSameOrigin(cx, proxy)) {
16377 { // Scope so we can wrap our PropertyDescriptor back into
16378 // the caller compartment.
16379 // Enter the Realm of "proxy" so we can work with it.
16380 JSAutoRealm ar(cx, proxy);
16382 JS_MarkCrossZoneId(cx, id);
16384 // The spec messes around with configurability of the returned
16385 // descriptor here, but it's not clear what should actually happen
16386 // here. See <https://github.com/whatwg/html/issues/4157>. For
16387 // now, keep our old behavior and don't do any magic.
16388 if (!dom::DOMProxyHandler::getOwnPropertyDescriptor(cx, proxy, id, desc)) {
16389 return false;
16392 return JS_WrapPropertyDescriptor(cx, desc);
16395 // Step 2.
16396 if (!CrossOriginGetOwnPropertyHelper(cx, proxy, id, desc)) {
16397 return false;
16400 // Step 3.
16401 if (desc.isSome()) {
16402 return true;
16405 // And step 4.
16406 return CrossOriginPropertyFallback(cx, proxy, id, desc);
16411 class CGDOMJSProxyHandler_getSameOriginPrototype(ClassMethod):
16413 Implementation of getSameOriginPrototype. We only use this for
16414 cross-origin objects.
16417 def __init__(self, descriptor):
16418 assert descriptor.isMaybeCrossOriginObject()
16420 args = [Argument("JSContext*", "cx")]
16421 ClassMethod.__init__(
16422 self,
16423 "getSameOriginPrototype",
16424 "JSObject*",
16425 args,
16426 virtual=True,
16427 override=True,
16428 const=True,
16430 self.descriptor = descriptor
16432 def getBody(self):
16433 return dedent(
16435 return GetProtoObjectHandle(cx);
16440 class CGDOMJSProxyHandler_definePropertySameOrigin(ClassMethod):
16442 Implementation of definePropertySameOrigin. We only use this for
16443 cross-origin objects.
16446 def __init__(self, descriptor):
16447 assert descriptor.isMaybeCrossOriginObject()
16449 args = [
16450 Argument("JSContext*", "cx"),
16451 Argument("JS::Handle<JSObject*>", "proxy"),
16452 Argument("JS::Handle<jsid>", "id"),
16453 Argument("JS::Handle<JS::PropertyDescriptor>", "desc"),
16454 Argument("JS::ObjectOpResult&", "result"),
16456 ClassMethod.__init__(
16457 self,
16458 "definePropertySameOrigin",
16459 "bool",
16460 args,
16461 virtual=True,
16462 override=True,
16463 const=True,
16465 self.descriptor = descriptor
16467 def getBody(self):
16468 return dedent(
16470 return dom::DOMProxyHandler::defineProperty(cx, proxy, id, desc, result);
16475 class CGDOMJSProxyHandler_set(ClassMethod):
16477 Implementation of set(). We only use this for cross-origin objects.
16480 def __init__(self, descriptor):
16481 assert descriptor.isMaybeCrossOriginObject()
16483 args = [
16484 Argument("JSContext*", "cx"),
16485 Argument("JS::Handle<JSObject*>", "proxy"),
16486 Argument("JS::Handle<jsid>", "id"),
16487 Argument("JS::Handle<JS::Value>", "v"),
16488 Argument("JS::Handle<JS::Value>", "receiver"),
16489 Argument("JS::ObjectOpResult&", "result"),
16491 ClassMethod.__init__(
16492 self, "set", "bool", args, virtual=True, override=True, const=True
16494 self.descriptor = descriptor
16496 def getBody(self):
16497 return dedent(
16499 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
16500 return CrossOriginSet(cx, proxy, id, v, receiver, result);
16503 // Safe to enter the Realm of proxy now, since it's same-origin with us.
16504 JSAutoRealm ar(cx, proxy);
16505 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
16506 if (!MaybeWrapValue(cx, &wrappedReceiver)) {
16507 return false;
16510 JS::Rooted<JS::Value> wrappedValue(cx, v);
16511 if (!MaybeWrapValue(cx, &wrappedValue)) {
16512 return false;
16515 JS_MarkCrossZoneId(cx, id);
16517 return dom::DOMProxyHandler::set(cx, proxy, id, wrappedValue, wrappedReceiver, result);
16522 class CGDOMJSProxyHandler_EnsureHolder(ClassMethod):
16524 Implementation of EnsureHolder(). We only use this for cross-origin objects.
16527 def __init__(self, descriptor):
16528 args = [
16529 Argument("JSContext*", "cx"),
16530 Argument("JS::Handle<JSObject*>", "proxy"),
16531 Argument("JS::MutableHandle<JSObject*>", "holder"),
16533 ClassMethod.__init__(
16534 self, "EnsureHolder", "bool", args, virtual=True, override=True, const=True
16536 self.descriptor = descriptor
16538 def getBody(self):
16539 return dedent(
16541 return EnsureHolder(cx, proxy,
16542 JSCLASS_RESERVED_SLOTS(JS::GetClass(proxy)) - 1,
16543 sCrossOriginProperties, holder);
16548 class CGDOMJSProxyHandler(CGClass):
16549 def __init__(self, descriptor):
16550 assert (
16551 descriptor.supportsIndexedProperties()
16552 or descriptor.supportsNamedProperties()
16553 or descriptor.isMaybeCrossOriginObject()
16555 methods = [
16556 CGDOMJSProxyHandler_getOwnPropDescriptor(descriptor),
16557 CGDOMJSProxyHandler_defineProperty(descriptor),
16558 ClassUsingFromBaseDeclaration(
16559 "mozilla::dom::DOMProxyHandler", "defineProperty"
16561 CGDOMJSProxyHandler_ownPropNames(descriptor),
16562 CGDOMJSProxyHandler_hasOwn(descriptor),
16563 CGDOMJSProxyHandler_get(descriptor),
16564 CGDOMJSProxyHandler_className(descriptor),
16565 CGDOMJSProxyHandler_finalizeInBackground(descriptor),
16566 CGDOMJSProxyHandler_finalize(descriptor),
16567 CGJSProxyHandler_getInstance("DOMProxyHandler"),
16568 CGDOMJSProxyHandler_delete(descriptor),
16570 constructors = [
16571 ClassConstructor([], constexpr=True, visibility="public", explicit=True)
16574 if descriptor.supportsIndexedProperties():
16575 methods.append(CGDOMJSProxyHandler_getElements(descriptor))
16576 if descriptor.operations["IndexedSetter"] is not None or (
16577 descriptor.operations["NamedSetter"] is not None
16578 and descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns")
16580 methods.append(CGDOMJSProxyHandler_setCustom(descriptor))
16581 if descriptor.operations["LegacyCaller"]:
16582 methods.append(CGDOMJSProxyHandler_call())
16583 methods.append(CGDOMJSProxyHandler_isCallable())
16584 if descriptor.interface.hasProbablyShortLivingWrapper():
16585 if not descriptor.wrapperCache:
16586 raise TypeError(
16587 "Need a wrapper cache to support nursery "
16588 "allocation of DOM objects"
16590 methods.append(CGDOMJSProxyHandler_canNurseryAllocate())
16591 if descriptor.wrapperCache:
16592 methods.append(CGDOMJSProxyHandler_objectMoved(descriptor))
16594 if descriptor.isMaybeCrossOriginObject():
16595 methods.extend(
16597 CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor),
16598 CGDOMJSProxyHandler_getSameOriginPrototype(descriptor),
16599 CGDOMJSProxyHandler_definePropertySameOrigin(descriptor),
16600 CGDOMJSProxyHandler_set(descriptor),
16601 CGDOMJSProxyHandler_EnsureHolder(descriptor),
16602 ClassUsingFromBaseDeclaration(
16603 "MaybeCrossOriginObjectMixins", "EnsureHolder"
16608 if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
16609 assert not descriptor.isMaybeCrossOriginObject()
16610 parentClass = "ShadowingDOMProxyHandler"
16611 elif descriptor.isMaybeCrossOriginObject():
16612 parentClass = "MaybeCrossOriginObject<mozilla::dom::DOMProxyHandler>"
16613 else:
16614 parentClass = "mozilla::dom::DOMProxyHandler"
16616 CGClass.__init__(
16617 self,
16618 "DOMProxyHandler",
16619 bases=[ClassBase(parentClass)],
16620 constructors=constructors,
16621 methods=methods,
16625 class CGDOMJSProxyHandlerDeclarer(CGThing):
16627 A class for declaring a DOMProxyHandler.
16630 def __init__(self, handlerThing):
16631 self.handlerThing = handlerThing
16633 def declare(self):
16634 # Our class declaration should happen when we're defining
16635 return ""
16637 def define(self):
16638 return self.handlerThing.declare()
16641 class CGDOMJSProxyHandlerDefiner(CGThing):
16643 A class for defining a DOMProxyHandler.
16646 def __init__(self, handlerThing):
16647 self.handlerThing = handlerThing
16649 def declare(self):
16650 return ""
16652 def define(self):
16653 return self.handlerThing.define()
16656 def stripTrailingWhitespace(text):
16657 tail = "\n" if text.endswith("\n") else ""
16658 lines = text.splitlines()
16659 return "\n".join(line.rstrip() for line in lines) + tail
16662 class MemberProperties:
16663 def __init__(self):
16664 self.isCrossOriginMethod = False
16665 self.isCrossOriginGetter = False
16666 self.isCrossOriginSetter = False
16669 def memberProperties(m, descriptor):
16670 props = MemberProperties()
16671 if m.isMethod():
16672 if not m.isIdentifierLess() or m == descriptor.operations["Stringifier"]:
16673 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
16674 if m.getExtendedAttribute("CrossOriginCallable"):
16675 props.isCrossOriginMethod = True
16676 elif m.isAttr():
16677 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
16678 if m.getExtendedAttribute("CrossOriginReadable"):
16679 props.isCrossOriginGetter = True
16680 if not m.readonly:
16681 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
16682 if m.getExtendedAttribute("CrossOriginWritable"):
16683 props.isCrossOriginSetter = True
16684 elif m.getExtendedAttribute("PutForwards"):
16685 if m.getExtendedAttribute("CrossOriginWritable"):
16686 props.isCrossOriginSetter = True
16687 elif m.getExtendedAttribute("Replaceable") or m.getExtendedAttribute(
16688 "LegacyLenientSetter"
16690 if m.getExtendedAttribute("CrossOriginWritable"):
16691 props.isCrossOriginSetter = True
16693 return props
16696 class CGDescriptor(CGThing):
16697 def __init__(self, descriptor, attributeTemplates):
16698 CGThing.__init__(self)
16700 assert (
16701 not descriptor.concrete
16702 or descriptor.interface.hasInterfacePrototypeObject()
16705 self._deps = descriptor.interface.getDeps()
16707 iteratorCGThings = None
16708 if (
16709 descriptor.interface.isIterable()
16710 and descriptor.interface.maplikeOrSetlikeOrIterable.isPairIterator()
16711 ) or descriptor.interface.isAsyncIterable():
16712 # 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.
16713 iteratorCGThings = []
16714 itr_iface = (
16715 descriptor.interface.maplikeOrSetlikeOrIterable.iteratorType.inner
16717 iteratorDescriptor = descriptor.getDescriptor(itr_iface.identifier.name)
16718 iteratorCGThings.append(
16719 CGWrapNonWrapperCacheMethod(
16720 iteratorDescriptor, static=True, signatureOnly=True
16723 iteratorCGThings = CGList(
16724 (CGIndenter(t, declareOnly=True) for t in iteratorCGThings), "\n"
16726 iteratorCGThings = CGWrapper(iteratorCGThings, pre="\n", post="\n")
16727 iteratorCGThings = CGWrapper(
16728 CGNamespace(
16729 toBindingNamespace(iteratorDescriptor.name), iteratorCGThings
16731 post="\n",
16734 cgThings = []
16736 isIteratorInterface = (
16737 descriptor.interface.isIteratorInterface()
16738 or descriptor.interface.isAsyncIteratorInterface()
16740 if not isIteratorInterface:
16741 cgThings.append(
16742 CGGeneric(declare="typedef %s NativeType;\n" % descriptor.nativeType)
16744 parent = descriptor.interface.parent
16745 if parent:
16746 cgThings.append(
16747 CGGeneric(
16748 "static_assert(IsRefcounted<NativeType>::value == IsRefcounted<%s::NativeType>::value,\n"
16749 ' "Can\'t inherit from an interface with a different ownership model.");\n'
16750 % toBindingNamespace(descriptor.parentPrototypeName)
16754 defaultToJSONMethod = None
16755 needCrossOriginPropertyArrays = False
16756 unscopableNames = list()
16758 for n in descriptor.interface.legacyFactoryFunctions:
16759 cgThings.append(
16760 CGClassConstructor(descriptor, n, LegacyFactoryFunctionName(n))
16763 if descriptor.attributeTemplates is not None:
16764 for template in descriptor.attributeTemplates:
16765 if template.getter is not None:
16766 cgThings.append(
16767 CGTemplateForSpecializedGetter(descriptor, template)
16769 if template.setter is not None:
16770 cgThings.append(
16771 CGTemplateForSpecializedSetter(descriptor, template)
16774 for m in descriptor.interface.members:
16775 if m.isMethod() and m.identifier.name == "QueryInterface":
16776 continue
16778 props = memberProperties(m, descriptor)
16780 if m.isMethod():
16781 if m.getExtendedAttribute("Unscopable"):
16782 assert not m.isStatic()
16783 unscopableNames.append(m.identifier.name)
16784 if m.isDefaultToJSON():
16785 defaultToJSONMethod = m
16786 elif (
16787 not m.isIdentifierLess()
16788 or m == descriptor.operations["Stringifier"]
16790 if m.isStatic():
16791 assert descriptor.interface.hasInterfaceObject()
16792 cgThings.append(CGStaticMethod(descriptor, m))
16793 if m.returnsPromise():
16794 cgThings.append(CGStaticMethodJitinfo(m))
16795 elif descriptor.interface.hasInterfacePrototypeObject():
16796 specializedMethod = CGSpecializedMethod(descriptor, m)
16797 cgThings.append(specializedMethod)
16798 if m.returnsPromise():
16799 cgThings.append(
16800 CGMethodPromiseWrapper(descriptor, specializedMethod)
16802 cgThings.append(CGMemberJITInfo(descriptor, m))
16803 if props.isCrossOriginMethod:
16804 needCrossOriginPropertyArrays = True
16805 # If we've hit the maplike/setlike member itself, go ahead and
16806 # generate its convenience functions.
16807 elif m.isMaplikeOrSetlike():
16808 cgThings.append(CGMaplikeOrSetlikeHelperGenerator(descriptor, m))
16809 elif m.isAttr():
16810 if m.type.isObservableArray():
16811 cgThings.append(
16812 CGObservableArrayProxyHandlerGenerator(descriptor, m)
16814 cgThings.append(CGObservableArrayHelperGenerator(descriptor, m))
16815 if m.getExtendedAttribute("Unscopable"):
16816 assert not m.isStatic()
16817 unscopableNames.append(m.identifier.name)
16818 if m.isStatic():
16819 assert descriptor.interface.hasInterfaceObject()
16820 cgThings.append(CGStaticGetter(descriptor, m))
16821 elif descriptor.interface.hasInterfacePrototypeObject():
16822 template = m.getExtendedAttribute("BindingTemplate")
16823 if template is not None:
16824 templateName = template[0][0]
16825 additionalArg = template[0][1]
16826 if not (m.type.isPrimitive() or m.type.isString()):
16827 raise TypeError(
16828 "We only support primitives or strings on templated attributes. "
16829 "Attribute '%s' on interface '%s' has type '%s' but tries to "
16830 "use template '%s'"
16832 m.identifier.name,
16833 descriptor.interface.identifier.name,
16834 m.type,
16835 templateName,
16838 template = attributeTemplates.get(templateName)
16839 specializedGetter = CGSpecializedTemplatedGetter(
16840 descriptor, m, template, additionalArg
16842 else:
16843 specializedGetter = CGSpecializedGetter(descriptor, m)
16844 cgThings.append(specializedGetter)
16845 if m.type.isPromise():
16846 cgThings.append(
16847 CGGetterPromiseWrapper(descriptor, specializedGetter)
16849 if props.isCrossOriginGetter:
16850 needCrossOriginPropertyArrays = True
16851 if not m.readonly:
16852 if m.isStatic():
16853 assert descriptor.interface.hasInterfaceObject()
16854 cgThings.append(CGStaticSetter(descriptor, m))
16855 elif descriptor.interface.hasInterfacePrototypeObject():
16856 template = m.getExtendedAttribute("BindingTemplate")
16857 if template is not None:
16858 if isinstance(template[0], list):
16859 templateName = template[0][0]
16860 additionalArg = template[0][1]
16861 else:
16862 templateName = template[0]
16863 additionalArg = None
16864 template = attributeTemplates.get(templateName)
16865 specializedSetter = CGSpecializedTemplatedSetter(
16866 descriptor, m, template, additionalArg
16868 else:
16869 specializedSetter = CGSpecializedSetter(descriptor, m)
16870 cgThings.append(specializedSetter)
16871 if props.isCrossOriginSetter:
16872 needCrossOriginPropertyArrays = True
16873 elif m.getExtendedAttribute("PutForwards"):
16874 cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
16875 if props.isCrossOriginSetter:
16876 needCrossOriginPropertyArrays = True
16877 elif m.getExtendedAttribute("Replaceable"):
16878 cgThings.append(CGSpecializedReplaceableSetter(descriptor, m))
16879 elif m.getExtendedAttribute("LegacyLenientSetter"):
16880 # XXX In this case, we need to add an include for mozilla/dom/Document.h to the generated cpp file.
16881 cgThings.append(CGSpecializedLenientSetter(descriptor, m))
16882 if (
16883 not m.isStatic()
16884 and descriptor.interface.hasInterfacePrototypeObject()
16886 cgThings.append(CGMemberJITInfo(descriptor, m))
16887 if m.isConst() and m.type.isPrimitive():
16888 cgThings.append(CGConstDefinition(m))
16890 if defaultToJSONMethod:
16891 cgThings.append(CGDefaultToJSONMethod(descriptor, defaultToJSONMethod))
16892 cgThings.append(CGMemberJITInfo(descriptor, defaultToJSONMethod))
16894 if descriptor.concrete and not descriptor.proxy:
16895 if wantsAddProperty(descriptor):
16896 cgThings.append(CGAddPropertyHook(descriptor))
16898 # Always have a finalize hook, regardless of whether the class
16899 # wants a custom hook.
16900 cgThings.append(CGClassFinalizeHook(descriptor))
16902 if wantsGetWrapperCache(descriptor):
16903 cgThings.append(CGGetWrapperCacheHook(descriptor))
16905 if descriptor.concrete and descriptor.wrapperCache and not descriptor.proxy:
16906 cgThings.append(CGClassObjectMovedHook(descriptor))
16908 properties = PropertyArrays(descriptor)
16909 cgThings.append(CGGeneric(define=str(properties)))
16910 cgThings.append(CGNativeProperties(descriptor, properties))
16912 if defaultToJSONMethod:
16913 # Now that we know about our property arrays, we can
16914 # output our "collect attribute values" method, which uses those.
16915 cgThings.append(
16916 CGCollectJSONAttributesMethod(descriptor, defaultToJSONMethod)
16919 # Declare our DOMProxyHandler.
16920 if descriptor.concrete and descriptor.proxy:
16921 cgThings.append(
16922 CGGeneric(
16923 fill(
16925 static_assert(std::is_base_of_v<nsISupports, ${nativeType}>,
16926 "We don't support non-nsISupports native classes for "
16927 "proxy-based bindings yet");
16929 """,
16930 nativeType=descriptor.nativeType,
16934 if not descriptor.wrapperCache:
16935 raise TypeError(
16936 "We need a wrappercache to support expandos for proxy-based "
16937 "bindings (" + descriptor.name + ")"
16939 handlerThing = CGDOMJSProxyHandler(descriptor)
16940 cgThings.append(CGDOMJSProxyHandlerDeclarer(handlerThing))
16941 cgThings.append(CGProxyIsProxy(descriptor))
16942 cgThings.append(CGProxyUnwrap(descriptor))
16944 # Set up our Xray callbacks as needed. This needs to come
16945 # after we have our DOMProxyHandler defined.
16946 if descriptor.wantsXrays:
16947 if descriptor.concrete and descriptor.proxy:
16948 if descriptor.needsXrayNamedDeleterHook():
16949 cgThings.append(CGDeleteNamedProperty(descriptor))
16950 elif descriptor.needsXrayResolveHooks():
16951 cgThings.append(CGResolveOwnPropertyViaResolve(descriptor))
16952 cgThings.append(
16953 CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor)
16955 if descriptor.wantsXrayExpandoClass:
16956 cgThings.append(CGXrayExpandoJSClass(descriptor))
16958 # Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff
16959 # done, set up our NativePropertyHooks.
16960 cgThings.append(CGNativePropertyHooks(descriptor, properties))
16962 if descriptor.interface.isNamespace():
16963 cgThings.append(CGNamespaceObjectJSClass(descriptor))
16964 elif descriptor.interface.hasInterfaceObject():
16965 cgThings.append(CGClassConstructor(descriptor, descriptor.interface.ctor()))
16966 cgThings.append(CGInterfaceObjectInfo(descriptor))
16967 cgThings.append(CGLegacyFactoryFunctions(descriptor))
16969 cgThings.append(CGLegacyCallHook(descriptor))
16970 if descriptor.interface.getExtendedAttribute("NeedResolve"):
16971 cgThings.append(CGResolveHook(descriptor))
16972 cgThings.append(CGMayResolveHook(descriptor))
16973 cgThings.append(CGEnumerateHook(descriptor))
16975 if descriptor.hasNamedPropertiesObject:
16976 cgThings.append(CGGetNamedPropertiesObjectMethod(descriptor))
16978 if descriptor.interface.hasInterfacePrototypeObject():
16979 cgThings.append(CGPrototypeJSClass(descriptor, properties))
16981 if (
16982 descriptor.interface.hasInterfaceObject()
16983 and not descriptor.interface.isExternal()
16984 and descriptor.isExposedConditionally()
16986 cgThings.append(CGConstructorEnabled(descriptor))
16988 if (
16989 descriptor.interface.hasMembersInSlots()
16990 and descriptor.interface.hasChildInterfaces()
16992 raise TypeError(
16993 "We don't support members in slots on "
16994 "non-leaf interfaces like %s" % descriptor.interface.identifier.name
16997 if descriptor.needsMissingPropUseCounters:
16998 cgThings.append(CGCountMaybeMissingProperty(descriptor))
17000 if descriptor.concrete:
17001 if descriptor.interface.isSerializable():
17002 cgThings.append(CGSerializer(descriptor))
17003 cgThings.append(CGDeserializer(descriptor))
17005 # CGDOMProxyJSClass/CGDOMJSClass need GetProtoObjectHandle, but we don't want to export it for the iterator interfaces, so declare it here.
17006 if isIteratorInterface:
17007 cgThings.append(
17008 CGGetProtoObjectHandleMethod(
17009 descriptor, static=True, signatureOnly=True
17013 if descriptor.proxy:
17014 cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing))
17015 cgThings.append(CGDOMProxyJSClass(descriptor))
17016 else:
17017 cgThings.append(CGDOMJSClass(descriptor))
17019 if descriptor.interface.hasMembersInSlots():
17020 cgThings.append(CGUpdateMemberSlotsMethod(descriptor))
17022 if descriptor.isGlobal():
17023 assert descriptor.wrapperCache
17024 cgThings.append(CGWrapGlobalMethod(descriptor, properties))
17025 elif descriptor.wrapperCache:
17026 cgThings.append(CGWrapWithCacheMethod(descriptor))
17027 cgThings.append(CGWrapMethod(descriptor))
17028 else:
17029 cgThings.append(
17030 CGWrapNonWrapperCacheMethod(descriptor, static=isIteratorInterface)
17033 # If we're not wrappercached, we don't know how to clear our
17034 # cached values, since we can't get at the JSObject.
17035 if descriptor.wrapperCache:
17036 cgThings.extend(
17037 CGClearCachedValueMethod(descriptor, m)
17038 for m in clearableCachedAttrs(descriptor)
17041 haveUnscopables = (
17042 len(unscopableNames) != 0
17043 and descriptor.interface.hasInterfacePrototypeObject()
17045 if haveUnscopables:
17046 cgThings.append(
17047 CGList(
17049 CGGeneric("static const char* const unscopableNames[] = {"),
17050 CGIndenter(
17051 CGList(
17052 [CGGeneric('"%s"' % name) for name in unscopableNames]
17053 + [CGGeneric("nullptr")],
17054 ",\n",
17057 CGGeneric("};\n"),
17059 "\n",
17063 legacyWindowAliases = descriptor.interface.legacyWindowAliases
17064 haveLegacyWindowAliases = len(legacyWindowAliases) != 0
17065 if haveLegacyWindowAliases:
17066 cgThings.append(
17067 CGList(
17069 CGGeneric("static const char* const legacyWindowAliases[] = {"),
17070 CGIndenter(
17071 CGList(
17073 CGGeneric('"%s"' % name)
17074 for name in legacyWindowAliases
17076 + [CGGeneric("nullptr")],
17077 ",\n",
17080 CGGeneric("};\n"),
17082 "\n",
17086 # CGCreateInterfaceObjectsMethod needs to come after our
17087 # CGDOMJSClass and unscopables, if any.
17088 cgThings.append(
17089 CGCreateInterfaceObjectsMethod(
17090 descriptor,
17091 properties,
17092 haveUnscopables,
17093 haveLegacyWindowAliases,
17094 static=isIteratorInterface,
17098 # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need
17099 # to come after CGCreateInterfaceObjectsMethod.
17100 if (
17101 descriptor.interface.hasInterfacePrototypeObject()
17102 and not descriptor.hasOrdinaryObjectPrototype
17104 cgThings.append(
17105 CGGetProtoObjectHandleMethod(descriptor, static=isIteratorInterface)
17107 if descriptor.interface.hasChildInterfaces():
17108 assert not isIteratorInterface
17109 cgThings.append(CGGetProtoObjectMethod(descriptor))
17110 if descriptor.interface.hasInterfaceObject():
17111 cgThings.append(CGGetConstructorObjectHandleMethod(descriptor))
17112 cgThings.append(CGGetConstructorObjectMethod(descriptor))
17114 # See whether we need to generate cross-origin property arrays.
17115 if needCrossOriginPropertyArrays:
17116 cgThings.append(CGCrossOriginProperties(descriptor))
17118 cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
17119 cgThings = CGWrapper(cgThings, pre="\n", post="\n")
17120 cgThings = CGWrapper(
17121 CGNamespace(toBindingNamespace(descriptor.name), cgThings), post="\n"
17123 self.cgRoot = CGList([iteratorCGThings, cgThings], "\n")
17125 def declare(self):
17126 return self.cgRoot.declare()
17128 def define(self):
17129 return self.cgRoot.define()
17131 def deps(self):
17132 return self._deps
17135 class CGNamespacedEnum(CGThing):
17136 def __init__(self, namespace, enumName, names, values, comment=""):
17137 if not values:
17138 values = []
17140 # Account for explicit enum values.
17141 entries = []
17142 for i in range(0, len(names)):
17143 if len(values) > i and values[i] is not None:
17144 entry = "%s = %s" % (names[i], values[i])
17145 else:
17146 entry = names[i]
17147 entries.append(entry)
17149 # Append a Count.
17150 entries.append("_" + enumName + "_Count")
17152 # Indent.
17153 entries = [" " + e for e in entries]
17155 # Build the enum body.
17156 enumstr = comment + "enum %s : uint16_t\n{\n%s\n};\n" % (
17157 enumName,
17158 ",\n".join(entries),
17160 curr = CGGeneric(declare=enumstr)
17162 # Add some whitespace padding.
17163 curr = CGWrapper(curr, pre="\n", post="\n")
17165 # Add the namespace.
17166 curr = CGNamespace(namespace, curr)
17168 # Add the typedef
17169 typedef = "\ntypedef %s::%s %s;\n\n" % (namespace, enumName, enumName)
17170 curr = CGList([curr, CGGeneric(declare=typedef)])
17172 # Save the result.
17173 self.node = curr
17175 def declare(self):
17176 return self.node.declare()
17178 def define(self):
17179 return ""
17182 def initIdsClassMethod(identifiers, atomCacheName):
17183 idinit = [
17184 '!atomsCache->%s.init(cx, "%s")' % (CGDictionary.makeIdName(id), id)
17185 for id in identifiers
17187 idinit.reverse()
17188 body = fill(
17190 MOZ_ASSERT(reinterpret_cast<jsid*>(atomsCache)->isVoid());
17192 // Initialize these in reverse order so that any failure leaves the first one
17193 // uninitialized.
17194 if (${idinit}) {
17195 return false;
17197 return true;
17198 """,
17199 idinit=" ||\n ".join(idinit),
17201 return ClassMethod(
17202 "InitIds",
17203 "bool",
17204 [Argument("JSContext*", "cx"), Argument("%s*" % atomCacheName, "atomsCache")],
17205 static=True,
17206 body=body,
17207 visibility="private",
17211 class CGDictionary(CGThing):
17212 def __init__(self, dictionary, descriptorProvider):
17213 self.dictionary = dictionary
17214 self.descriptorProvider = descriptorProvider
17215 self.needToInitIds = len(dictionary.members) > 0
17216 self.memberInfo = [
17218 member,
17219 getJSToNativeConversionInfo(
17220 member.type,
17221 descriptorProvider,
17222 isMember="Dictionary",
17223 isOptional=member.canHaveMissingValue(),
17224 isKnownMissing=not dictionary.needsConversionFromJS,
17225 defaultValue=member.defaultValue,
17226 sourceDescription=self.getMemberSourceDescription(member),
17229 for member in dictionary.members
17232 # If we have a union member which is going to be declared in a different
17233 # header but contains something that will be declared in the same header
17234 # as us, bail: the C++ includes won't work out.
17235 for member in dictionary.members:
17236 type = member.type.unroll()
17237 if type.isUnion() and CGHeaders.getUnionDeclarationFilename(
17238 descriptorProvider.getConfig(), type
17239 ) != CGHeaders.getDeclarationFilename(dictionary):
17240 for t in type.flatMemberTypes:
17241 if t.isDictionary() and CGHeaders.getDeclarationFilename(
17242 t.inner
17243 ) == CGHeaders.getDeclarationFilename(dictionary):
17244 raise TypeError(
17245 "Dictionary contains a union that will live in a different "
17246 "header that contains a dictionary from the same header as "
17247 "the original dictionary. This won't compile. Move the "
17248 "inner dictionary to a different Web IDL file to move it "
17249 "to a different header.\n%s\n%s"
17250 % (t.location, t.inner.location)
17252 self.structs = self.getStructs()
17254 def declare(self):
17255 return self.structs.declare()
17257 def define(self):
17258 return self.structs.define()
17260 def base(self):
17261 if self.dictionary.parent:
17262 return self.makeClassName(self.dictionary.parent)
17263 return "DictionaryBase"
17265 def initMethod(self):
17267 This function outputs the body of the Init() method for the dictionary.
17269 For the most part, this is some bookkeeping for our atoms so
17270 we can avoid atomizing strings all the time, then we just spit
17271 out the getMemberConversion() output for each member,
17272 separated by newlines.
17275 body = dedent(
17277 // Passing a null JSContext is OK only if we're initing from null,
17278 // Since in that case we will not have to do any property gets
17279 // Also evaluate isNullOrUndefined in order to avoid false-positive
17280 // checkers by static analysis tools
17281 MOZ_ASSERT_IF(!cx, val.isNull() && val.isNullOrUndefined());
17285 if self.needToInitIds:
17286 body += fill(
17288 ${dictName}Atoms* atomsCache = nullptr;
17289 if (cx) {
17290 atomsCache = GetAtomCache<${dictName}Atoms>(cx);
17291 if (reinterpret_cast<jsid*>(atomsCache)->isVoid() &&
17292 !InitIds(cx, atomsCache)) {
17293 return false;
17297 """,
17298 dictName=self.makeClassName(self.dictionary),
17301 if self.dictionary.parent:
17302 body += fill(
17304 // Per spec, we init the parent's members first
17305 if (!${dictName}::Init(cx, val)) {
17306 return false;
17309 """,
17310 dictName=self.makeClassName(self.dictionary.parent),
17312 else:
17313 body += dedent(
17315 if (!IsConvertibleToDictionary(val)) {
17316 return cx.ThrowErrorMessage<MSG_CONVERSION_ERROR>(sourceDescription, "dictionary");
17322 memberInits = [self.getMemberConversion(m).define() for m in self.memberInfo]
17323 if memberInits:
17324 body += fill(
17326 bool isNull = val.isNullOrUndefined();
17327 // We only need these if !isNull, in which case we have |cx|.
17328 Maybe<JS::Rooted<JSObject *> > object;
17329 Maybe<JS::Rooted<JS::Value> > temp;
17330 if (!isNull) {
17331 MOZ_ASSERT(cx);
17332 object.emplace(cx, &val.toObject());
17333 temp.emplace(cx);
17335 $*{memberInits}
17336 """,
17337 memberInits="\n".join(memberInits),
17340 body += "return true;\n"
17342 return ClassMethod(
17343 "Init",
17344 "bool",
17346 Argument("BindingCallContext&", "cx"),
17347 Argument("JS::Handle<JS::Value>", "val"),
17348 Argument("const char*", "sourceDescription", default='"Value"'),
17349 Argument("bool", "passedToJSImpl", default="false"),
17351 body=body,
17354 def initWithoutCallContextMethod(self):
17356 This function outputs the body of an Init() method for the dictionary
17357 that takes just a JSContext*. This is needed for non-binding consumers.
17359 body = dedent(
17361 // We don't want to use sourceDescription for our context here;
17362 // that's not really what it's formatted for.
17363 BindingCallContext cx(cx_, nullptr);
17364 return Init(cx, val, sourceDescription, passedToJSImpl);
17367 return ClassMethod(
17368 "Init",
17369 "bool",
17371 Argument("JSContext*", "cx_"),
17372 Argument("JS::Handle<JS::Value>", "val"),
17373 Argument("const char*", "sourceDescription", default='"Value"'),
17374 Argument("bool", "passedToJSImpl", default="false"),
17376 body=body,
17379 def simpleInitMethod(self):
17381 This function outputs the body of the Init() method for the dictionary,
17382 for cases when we are just default-initializing it.
17385 relevantMembers = [
17387 for m in self.memberInfo
17388 # We only need to init the things that can have
17389 # default values.
17390 if m[0].optional and m[0].defaultValue
17393 # We mostly avoid outputting code that uses cx in our native-to-JS
17394 # conversions, but there is one exception: we may have a
17395 # dictionary-typed member that _does_ generally support conversion from
17396 # JS. If we have such a thing, we can pass it a null JSContext and
17397 # JS::NullHandleValue to default-initialize it, but since the
17398 # native-to-JS templates hardcode `cx` as the JSContext value, we're
17399 # going to need to provide that.
17400 haveMemberThatNeedsCx = any(
17401 m[0].type.isDictionary() and m[0].type.unroll().inner.needsConversionFromJS
17402 for m in relevantMembers
17404 if haveMemberThatNeedsCx:
17405 body = dedent(
17407 JSContext* cx = nullptr;
17410 else:
17411 body = ""
17413 if self.dictionary.parent:
17414 if self.dictionary.parent.needsConversionFromJS:
17415 args = "nullptr, JS::NullHandleValue"
17416 else:
17417 args = ""
17418 body += fill(
17420 // We init the parent's members first
17421 if (!${dictName}::Init(${args})) {
17422 return false;
17425 """,
17426 dictName=self.makeClassName(self.dictionary.parent),
17427 args=args,
17430 memberInits = [
17431 self.getMemberConversion(m, isKnownMissing=True).define()
17432 for m in relevantMembers
17434 if memberInits:
17435 body += fill(
17437 $*{memberInits}
17438 """,
17439 memberInits="\n".join(memberInits),
17442 body += "return true;\n"
17444 return ClassMethod(
17445 "Init",
17446 "bool",
17448 Argument("const char*", "sourceDescription", default='"Value"'),
17449 Argument("bool", "passedToJSImpl", default="false"),
17451 body=body,
17454 def initFromJSONMethod(self):
17455 return ClassMethod(
17456 "Init",
17457 "bool",
17458 [Argument("const nsAString&", "aJSON")],
17459 body=dedent(
17461 AutoJSAPI jsapi;
17462 JSObject* cleanGlobal = SimpleGlobalObject::Create(SimpleGlobalObject::GlobalType::BindingDetail);
17463 if (!cleanGlobal) {
17464 return false;
17466 if (!jsapi.Init(cleanGlobal)) {
17467 return false;
17469 JSContext* cx = jsapi.cx();
17470 JS::Rooted<JS::Value> json(cx);
17471 bool ok = ParseJSON(cx, aJSON, &json);
17472 NS_ENSURE_TRUE(ok, false);
17473 return Init(cx, json);
17478 def toJSONMethod(self):
17479 return ClassMethod(
17480 "ToJSON",
17481 "bool",
17482 [Argument("nsAString&", "aJSON")],
17483 body=dedent(
17485 AutoJSAPI jsapi;
17486 jsapi.Init();
17487 JSContext *cx = jsapi.cx();
17488 // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here
17489 // because we'll only be creating objects, in ways that have no
17490 // side-effects, followed by a call to JS::ToJSONMaybeSafely,
17491 // which likewise guarantees no side-effects for the sorts of
17492 // things we will pass it.
17493 JSObject* scope = UnprivilegedJunkScopeOrWorkerGlobal(fallible);
17494 if (!scope) {
17495 JS_ReportOutOfMemory(cx);
17496 return false;
17498 JSAutoRealm ar(cx, scope);
17499 JS::Rooted<JS::Value> val(cx);
17500 if (!ToObjectInternal(cx, &val)) {
17501 return false;
17503 JS::Rooted<JSObject*> obj(cx, &val.toObject());
17504 return StringifyToJSON(cx, obj, aJSON);
17507 const=True,
17510 def toObjectInternalMethod(self):
17511 body = ""
17512 if self.needToInitIds:
17513 body += fill(
17515 ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx);
17516 if (reinterpret_cast<jsid*>(atomsCache)->isVoid() &&
17517 !InitIds(cx, atomsCache)) {
17518 return false;
17521 """,
17522 dictName=self.makeClassName(self.dictionary),
17525 if self.dictionary.parent:
17526 body += fill(
17528 // Per spec, we define the parent's members first
17529 if (!${dictName}::ToObjectInternal(cx, rval)) {
17530 return false;
17532 JS::Rooted<JSObject*> obj(cx, &rval.toObject());
17534 """,
17535 dictName=self.makeClassName(self.dictionary.parent),
17537 else:
17538 body += dedent(
17540 JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
17541 if (!obj) {
17542 return false;
17544 rval.set(JS::ObjectValue(*obj));
17549 if self.memberInfo:
17550 body += "\n".join(
17551 self.getMemberDefinition(m).define() for m in self.memberInfo
17553 body += "\nreturn true;\n"
17555 return ClassMethod(
17556 "ToObjectInternal",
17557 "bool",
17559 Argument("JSContext*", "cx"),
17560 Argument("JS::MutableHandle<JS::Value>", "rval"),
17562 const=True,
17563 body=body,
17566 def initIdsMethod(self):
17567 assert self.needToInitIds
17568 return initIdsClassMethod(
17569 [m.identifier.name for m in self.dictionary.members],
17570 "%sAtoms" % self.makeClassName(self.dictionary),
17573 def traceDictionaryMethod(self):
17574 body = ""
17575 if self.dictionary.parent:
17576 cls = self.makeClassName(self.dictionary.parent)
17577 body += "%s::TraceDictionary(trc);\n" % cls
17579 memberTraces = [
17580 self.getMemberTrace(m)
17581 for m in self.dictionary.members
17582 if typeNeedsRooting(m.type)
17585 if memberTraces:
17586 body += "\n".join(memberTraces)
17588 return ClassMethod(
17589 "TraceDictionary",
17590 "void",
17592 Argument("JSTracer*", "trc"),
17594 body=body,
17597 @staticmethod
17598 def dictionaryNeedsCycleCollection(dictionary):
17599 return any(idlTypeNeedsCycleCollection(m.type) for m in dictionary.members) or (
17600 dictionary.parent
17601 and CGDictionary.dictionaryNeedsCycleCollection(dictionary.parent)
17604 def traverseForCCMethod(self):
17605 body = ""
17606 if self.dictionary.parent and self.dictionaryNeedsCycleCollection(
17607 self.dictionary.parent
17609 cls = self.makeClassName(self.dictionary.parent)
17610 body += "%s::TraverseForCC(aCallback, aFlags);\n" % cls
17612 for m, _ in self.memberInfo:
17613 if idlTypeNeedsCycleCollection(m.type):
17614 memberName = self.makeMemberName(m.identifier.name)
17615 body += (
17616 'ImplCycleCollectionTraverse(aCallback, %s, "%s", aFlags);\n'
17617 % (memberName, memberName)
17620 return ClassMethod(
17621 "TraverseForCC",
17622 "void",
17624 Argument("nsCycleCollectionTraversalCallback&", "aCallback"),
17625 Argument("uint32_t", "aFlags"),
17627 body=body,
17628 # Inline so we don't pay a codesize hit unless someone actually uses
17629 # this traverse method.
17630 inline=True,
17631 bodyInHeader=True,
17634 def unlinkForCCMethod(self):
17635 body = ""
17636 if self.dictionary.parent and self.dictionaryNeedsCycleCollection(
17637 self.dictionary.parent
17639 cls = self.makeClassName(self.dictionary.parent)
17640 body += "%s::UnlinkForCC();\n" % cls
17642 for m, _ in self.memberInfo:
17643 if idlTypeNeedsCycleCollection(m.type):
17644 memberName = self.makeMemberName(m.identifier.name)
17645 body += "ImplCycleCollectionUnlink(%s);\n" % memberName
17647 return ClassMethod(
17648 "UnlinkForCC",
17649 "void",
17651 body=body,
17652 # Inline so we don't pay a codesize hit unless someone actually uses
17653 # this unlink method.
17654 inline=True,
17655 bodyInHeader=True,
17658 def assignmentOperator(self):
17659 body = CGList([])
17660 body.append(CGGeneric("%s::operator=(aOther);\n" % self.base()))
17662 for m, _ in self.memberInfo:
17663 memberName = self.makeMemberName(m.identifier.name)
17664 if m.canHaveMissingValue():
17665 memberAssign = CGGeneric(
17666 fill(
17668 ${name}.Reset();
17669 if (aOther.${name}.WasPassed()) {
17670 ${name}.Construct(aOther.${name}.Value());
17672 """,
17673 name=memberName,
17676 else:
17677 memberAssign = CGGeneric("%s = aOther.%s;\n" % (memberName, memberName))
17678 body.append(memberAssign)
17679 body.append(CGGeneric("return *this;\n"))
17680 return ClassMethod(
17681 "operator=",
17682 "%s&" % self.makeClassName(self.dictionary),
17683 [Argument("const %s&" % self.makeClassName(self.dictionary), "aOther")],
17684 body=body.define(),
17687 def canHaveEqualsOperator(self):
17688 return all(
17689 m.type.isString() or m.type.isPrimitive() for (m, _) in self.memberInfo
17692 def equalsOperator(self):
17693 body = CGList([])
17695 for m, _ in self.memberInfo:
17696 memberName = self.makeMemberName(m.identifier.name)
17697 memberTest = CGGeneric(
17698 fill(
17700 if (${memberName} != aOther.${memberName}) {
17701 return false;
17703 """,
17704 memberName=memberName,
17707 body.append(memberTest)
17708 body.append(CGGeneric("return true;\n"))
17709 return ClassMethod(
17710 "operator==",
17711 "bool",
17712 [Argument("const %s&" % self.makeClassName(self.dictionary), "aOther")],
17713 const=True,
17714 body=body.define(),
17717 def getStructs(self):
17718 d = self.dictionary
17719 selfName = self.makeClassName(d)
17720 members = [
17721 ClassMember(
17722 self.makeMemberName(m[0].identifier.name),
17723 self.getMemberType(m),
17724 visibility="public",
17725 body=self.getMemberInitializer(m),
17726 hasIgnoreInitCheckFlag=True,
17728 for m in self.memberInfo
17730 if d.parent:
17731 # We always want to init our parent with our non-initializing
17732 # constructor arg, because either we're about to init ourselves (and
17733 # hence our parent) or we don't want any init happening.
17734 baseConstructors = [
17735 "%s(%s)"
17736 % (self.makeClassName(d.parent), self.getNonInitializingCtorArg())
17738 else:
17739 baseConstructors = None
17741 if d.needsConversionFromJS:
17742 initArgs = "nullptr, JS::NullHandleValue"
17743 else:
17744 initArgs = ""
17745 ctors = [
17746 ClassConstructor(
17748 visibility="public",
17749 baseConstructors=baseConstructors,
17750 body=(
17751 "// Safe to pass a null context if we pass a null value\n"
17752 "Init(%s);\n" % initArgs
17755 ClassConstructor(
17756 [Argument("const FastDictionaryInitializer&", "")],
17757 visibility="public",
17758 baseConstructors=baseConstructors,
17759 explicit=True,
17760 bodyInHeader=True,
17761 body='// Do nothing here; this is used by our "Fast" subclass\n',
17764 methods = []
17766 if self.needToInitIds:
17767 methods.append(self.initIdsMethod())
17769 if d.needsConversionFromJS:
17770 methods.append(self.initMethod())
17771 methods.append(self.initWithoutCallContextMethod())
17772 else:
17773 methods.append(self.simpleInitMethod())
17775 canBeRepresentedAsJSON = self.dictionarySafeToJSONify(d)
17776 if canBeRepresentedAsJSON and d.getExtendedAttribute("GenerateInitFromJSON"):
17777 methods.append(self.initFromJSONMethod())
17779 if d.needsConversionToJS:
17780 methods.append(self.toObjectInternalMethod())
17782 if canBeRepresentedAsJSON and d.getExtendedAttribute("GenerateToJSON"):
17783 methods.append(self.toJSONMethod())
17785 methods.append(self.traceDictionaryMethod())
17787 try:
17788 if self.dictionaryNeedsCycleCollection(d):
17789 methods.append(self.traverseForCCMethod())
17790 methods.append(self.unlinkForCCMethod())
17791 except CycleCollectionUnsupported:
17792 # We have some member that we don't know how to CC. Don't output
17793 # our cycle collection overloads, so attempts to CC us will fail to
17794 # compile instead of misbehaving.
17795 pass
17797 ctors.append(
17798 ClassConstructor(
17799 [Argument("%s&&" % selfName, "aOther")],
17800 default=True,
17801 visibility="public",
17802 baseConstructors=baseConstructors,
17806 if CGDictionary.isDictionaryCopyConstructible(d):
17807 disallowCopyConstruction = False
17808 # Note: gcc's -Wextra has a warning against not initializng our
17809 # base explicitly. If we have one. Use our non-initializing base
17810 # constructor to get around that.
17811 ctors.append(
17812 ClassConstructor(
17813 [Argument("const %s&" % selfName, "aOther")],
17814 bodyInHeader=True,
17815 visibility="public",
17816 baseConstructors=baseConstructors,
17817 explicit=True,
17818 body="*this = aOther;\n",
17821 methods.append(self.assignmentOperator())
17822 else:
17823 disallowCopyConstruction = True
17825 if self.canHaveEqualsOperator():
17826 methods.append(self.equalsOperator())
17828 struct = CGClass(
17829 selfName,
17830 bases=[ClassBase(self.base())],
17831 members=members,
17832 constructors=ctors,
17833 methods=methods,
17834 isStruct=True,
17835 disallowCopyConstruction=disallowCopyConstruction,
17838 fastDictionaryCtor = ClassConstructor(
17840 visibility="public",
17841 bodyInHeader=True,
17842 baseConstructors=["%s(%s)" % (selfName, self.getNonInitializingCtorArg())],
17843 body="// Doesn't matter what int we pass to the parent constructor\n",
17846 fastStruct = CGClass(
17847 "Fast" + selfName,
17848 bases=[ClassBase(selfName)],
17849 constructors=[fastDictionaryCtor],
17850 isStruct=True,
17853 return CGList([struct, CGNamespace("binding_detail", fastStruct)], "\n")
17855 def deps(self):
17856 return self.dictionary.getDeps()
17858 @staticmethod
17859 def makeDictionaryName(dictionary):
17860 return dictionary.identifier.name
17862 def makeClassName(self, dictionary):
17863 return self.makeDictionaryName(dictionary)
17865 @staticmethod
17866 def makeMemberName(name):
17867 return "m" + name[0].upper() + IDLToCIdentifier(name[1:])
17869 def getMemberType(self, memberInfo):
17870 member, conversionInfo = memberInfo
17871 # We can't handle having a holderType here
17872 assert conversionInfo.holderType is None
17874 if member.getExtendedAttribute("BinaryType"):
17875 return member.getExtendedAttribute("BinaryType")[0]
17877 declType = conversionInfo.declType
17878 if conversionInfo.dealWithOptional:
17879 declType = CGTemplatedType("Optional", declType)
17880 return declType.define()
17882 def getMemberConversion(self, memberInfo, isKnownMissing=False):
17884 A function that outputs the initialization of a single dictionary
17885 member from the given dictionary value.
17887 We start with our conversionInfo, which tells us how to
17888 convert a JS::Value to whatever type this member is. We
17889 substiture the template from the conversionInfo with values
17890 that point to our "temp" JS::Value and our member (which is
17891 the C++ value we want to produce). The output is a string of
17892 code to do the conversion. We store this string in
17893 conversionReplacements["convert"].
17895 Now we have three different ways we might use (or skip) this
17896 string of code, depending on whether the value is required,
17897 optional with default value, or optional without default
17898 value. We set up a template in the 'conversion' variable for
17899 exactly how to do this, then substitute into it from the
17900 conversionReplacements dictionary.
17902 member, conversionInfo = memberInfo
17904 # We should only be initializing things with default values if
17905 # we're always-missing.
17906 assert not isKnownMissing or (member.optional and member.defaultValue)
17908 replacements = {
17909 "declName": self.makeMemberName(member.identifier.name),
17910 # We need a holder name for external interfaces, but
17911 # it's scoped down to the conversion so we can just use
17912 # anything we want.
17913 "holderName": "holder",
17914 "passedToJSImpl": "passedToJSImpl",
17917 if isKnownMissing:
17918 replacements["val"] = "(JS::NullHandleValue)"
17919 else:
17920 replacements["val"] = "temp.ref()"
17921 replacements["maybeMutableVal"] = "temp.ptr()"
17923 # We can't handle having a holderType here
17924 assert conversionInfo.holderType is None
17925 if conversionInfo.dealWithOptional:
17926 replacements["declName"] = "(" + replacements["declName"] + ".Value())"
17927 if member.defaultValue:
17928 if isKnownMissing:
17929 replacements["haveValue"] = "false"
17930 else:
17931 replacements["haveValue"] = "!isNull && !temp->isUndefined()"
17933 propId = self.makeIdName(member.identifier.name)
17934 propGet = "JS_GetPropertyById(cx, *object, atomsCache->%s, temp.ptr())" % propId
17936 conversionReplacements = {
17937 "prop": self.makeMemberName(member.identifier.name),
17938 "convert": string.Template(conversionInfo.template).substitute(
17939 replacements
17941 "propGet": propGet,
17943 # The conversion code will only run where a default value or a value passed
17944 # by the author needs to get converted, so we can remember if we have any
17945 # members present here.
17946 conversionReplacements["convert"] += "mIsAnyMemberPresent = true;\n"
17947 if isKnownMissing:
17948 conversion = ""
17949 else:
17950 setTempValue = CGGeneric(
17951 dedent(
17953 if (!${propGet}) {
17954 return false;
17959 conditions = getConditionList(member, "cx", "*object")
17960 if len(conditions) != 0:
17961 setTempValue = CGIfElseWrapper(
17962 conditions.define(),
17963 setTempValue,
17964 CGGeneric("temp->setUndefined();\n"),
17966 setTempValue = CGIfWrapper(setTempValue, "!isNull")
17967 conversion = setTempValue.define()
17969 if member.defaultValue:
17970 if member.type.isUnion() and (
17971 not member.type.nullable()
17972 or not isinstance(member.defaultValue, IDLNullValue)
17974 # Since this has a default value, it might have been initialized
17975 # already. Go ahead and uninit it before we try to init it
17976 # again.
17977 memberName = self.makeMemberName(member.identifier.name)
17978 if member.type.nullable():
17979 conversion += fill(
17981 if (!${memberName}.IsNull()) {
17982 ${memberName}.Value().Uninit();
17984 """,
17985 memberName=memberName,
17987 else:
17988 conversion += "%s.Uninit();\n" % memberName
17989 conversion += "${convert}"
17990 elif not conversionInfo.dealWithOptional:
17991 # We're required, but have no default value. Make sure
17992 # that we throw if we have no value provided.
17993 conversion += dedent(
17995 if (!isNull && !temp->isUndefined()) {
17996 ${convert}
17997 } else if (cx) {
17998 // Don't error out if we have no cx. In that
17999 // situation the caller is default-constructing us and we'll
18000 // just assume they know what they're doing.
18001 return cx.ThrowErrorMessage<MSG_MISSING_REQUIRED_DICTIONARY_MEMBER>("%s");
18004 % self.getMemberSourceDescription(member)
18006 conversionReplacements["convert"] = indent(
18007 conversionReplacements["convert"]
18008 ).rstrip()
18009 else:
18010 conversion += (
18011 "if (!isNull && !temp->isUndefined()) {\n"
18012 " ${prop}.Construct();\n"
18013 "${convert}"
18014 "}\n"
18016 conversionReplacements["convert"] = indent(
18017 conversionReplacements["convert"]
18020 return CGGeneric(string.Template(conversion).substitute(conversionReplacements))
18022 def getMemberDefinition(self, memberInfo):
18023 member = memberInfo[0]
18024 declType = memberInfo[1].declType
18025 memberLoc = self.makeMemberName(member.identifier.name)
18026 if not member.canHaveMissingValue():
18027 memberData = memberLoc
18028 else:
18029 # The data is inside the Optional<>
18030 memberData = "%s.InternalValue()" % memberLoc
18032 # If you have to change this list (which you shouldn't!), make sure it
18033 # continues to match the list in test_Object.prototype_props.html
18034 if member.identifier.name in [
18035 "constructor",
18036 "toString",
18037 "toLocaleString",
18038 "valueOf",
18039 "hasOwnProperty",
18040 "isPrototypeOf",
18041 "propertyIsEnumerable",
18042 "__defineGetter__",
18043 "__defineSetter__",
18044 "__lookupGetter__",
18045 "__lookupSetter__",
18046 "__proto__",
18048 raise TypeError(
18049 "'%s' member of %s dictionary shadows "
18050 "a property of Object.prototype, and Xrays to "
18051 "Object can't handle that.\n"
18052 "%s"
18054 member.identifier.name,
18055 self.dictionary.identifier.name,
18056 member.location,
18060 propDef = (
18061 "JS_DefinePropertyById(cx, obj, atomsCache->%s, temp, JSPROP_ENUMERATE)"
18062 % self.makeIdName(member.identifier.name)
18065 innerTemplate = wrapForType(
18066 member.type,
18067 self.descriptorProvider,
18069 "result": "currentValue",
18070 "successCode": (
18071 "if (!%s) {\n" " return false;\n" "}\n" "break;\n" % propDef
18073 "jsvalRef": "temp",
18074 "jsvalHandle": "&temp",
18075 "returnsNewObject": False,
18076 # 'obj' can just be allowed to be the string "obj", since that
18077 # will be our dictionary object, which is presumably itself in
18078 # the right scope.
18079 "spiderMonkeyInterfacesAreStructs": True,
18082 conversion = CGGeneric(innerTemplate)
18083 conversion = CGWrapper(
18084 conversion,
18085 pre=(
18086 "JS::Rooted<JS::Value> temp(cx);\n"
18087 "%s const & currentValue = %s;\n" % (declType.define(), memberData)
18091 # Now make sure that our successCode can actually break out of the
18092 # conversion. This incidentally gives us a scope for 'temp' and
18093 # 'currentValue'.
18094 conversion = CGWrapper(
18095 CGIndenter(conversion),
18096 pre=(
18097 "do {\n"
18098 " // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n"
18100 post="} while(false);\n",
18102 if member.canHaveMissingValue():
18103 # Only do the conversion if we have a value
18104 conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc)
18105 conditions = getConditionList(member, "cx", "obj")
18106 if len(conditions) != 0:
18107 conversion = CGIfWrapper(conversion, conditions.define())
18108 return conversion
18110 def getMemberTrace(self, member):
18111 type = member.type
18112 assert typeNeedsRooting(type)
18113 memberLoc = self.makeMemberName(member.identifier.name)
18114 if not member.canHaveMissingValue():
18115 memberData = memberLoc
18116 else:
18117 # The data is inside the Optional<>
18118 memberData = "%s.Value()" % memberLoc
18120 memberName = "%s.%s" % (self.makeClassName(self.dictionary), memberLoc)
18122 if type.isObject():
18123 trace = CGGeneric(
18124 'JS::TraceRoot(trc, %s, "%s");\n' % ("&" + memberData, memberName)
18126 if type.nullable():
18127 trace = CGIfWrapper(trace, memberData)
18128 elif type.isAny():
18129 trace = CGGeneric(
18130 'JS::TraceRoot(trc, %s, "%s");\n' % ("&" + memberData, memberName)
18132 elif (
18133 type.isSequence()
18134 or type.isDictionary()
18135 or type.isSpiderMonkeyInterface()
18136 or type.isUnion()
18137 or type.isRecord()
18139 if type.nullable():
18140 memberNullable = memberData
18141 memberData = "%s.Value()" % memberData
18142 if type.isSequence():
18143 trace = CGGeneric("DoTraceSequence(trc, %s);\n" % memberData)
18144 elif type.isDictionary():
18145 trace = CGGeneric("%s.TraceDictionary(trc);\n" % memberData)
18146 elif type.isUnion():
18147 trace = CGGeneric("%s.TraceUnion(trc);\n" % memberData)
18148 elif type.isRecord():
18149 trace = CGGeneric("TraceRecord(trc, %s);\n" % memberData)
18150 else:
18151 assert type.isSpiderMonkeyInterface()
18152 trace = CGGeneric("%s.TraceSelf(trc);\n" % memberData)
18153 if type.nullable():
18154 trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable)
18155 else:
18156 assert False # unknown type
18158 if member.canHaveMissingValue():
18159 trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc)
18161 return trace.define()
18163 def getMemberInitializer(self, memberInfo):
18165 Get the right initializer for the member. Most members don't need one,
18166 but we need to pre-initialize 'object' that have a default value or are
18167 required (and hence are not inside Optional), so they're safe to trace
18168 at all times. And we can optimize a bit for dictionary-typed members.
18170 member, _ = memberInfo
18171 if member.canHaveMissingValue():
18172 # Allowed missing value means no need to set it up front, since it's
18173 # inside an Optional and won't get traced until it's actually set
18174 # up.
18175 return None
18176 type = member.type
18177 if type.isDictionary():
18178 # When we construct ourselves, we don't want to init our member
18179 # dictionaries. Either we're being constructed-but-not-initialized
18180 # ourselves (and then we don't want to init them) or we're about to
18181 # init ourselves and then we'll init them anyway.
18182 return CGDictionary.getNonInitializingCtorArg()
18183 return initializerForType(type)
18185 def getMemberSourceDescription(self, member):
18186 return "'%s' member of %s" % (
18187 member.identifier.name,
18188 self.dictionary.identifier.name,
18191 @staticmethod
18192 def makeIdName(name):
18193 return IDLToCIdentifier(name) + "_id"
18195 @staticmethod
18196 def getNonInitializingCtorArg():
18197 return "FastDictionaryInitializer()"
18199 @staticmethod
18200 def isDictionaryCopyConstructible(dictionary):
18201 if dictionary.parent and not CGDictionary.isDictionaryCopyConstructible(
18202 dictionary.parent
18204 return False
18205 return all(isTypeCopyConstructible(m.type) for m in dictionary.members)
18207 @staticmethod
18208 def typeSafeToJSONify(type):
18210 Determine whether the given type is safe to convert to JSON. The
18211 restriction is that this needs to be safe while in a global controlled
18212 by an adversary, and "safe" means no side-effects when the JS
18213 representation of this type is converted to JSON. That means that we
18214 have to be pretty restrictive about what things we can allow. For
18215 example, "object" is out, because it may have accessor properties on it.
18217 if type.nullable():
18218 # Converting null to JSON is always OK.
18219 return CGDictionary.typeSafeToJSONify(type.inner)
18221 if type.isSequence():
18222 # Sequences are arrays we create ourselves, with no holes. They
18223 # should be safe if their contents are safe, as long as we suppress
18224 # invocation of .toJSON on objects.
18225 return CGDictionary.typeSafeToJSONify(type.inner)
18227 if type.isUnion():
18228 # OK if everything in it is ok.
18229 return all(CGDictionary.typeSafeToJSONify(t) for t in type.flatMemberTypes)
18231 if type.isDictionary():
18232 # OK if the dictionary is OK
18233 return CGDictionary.dictionarySafeToJSONify(type.inner)
18235 if type.isUndefined() or type.isString() or type.isEnum():
18236 # Strings are always OK.
18237 return True
18239 if type.isPrimitive():
18240 # Primitives (numbers and booleans) are ok, as long as
18241 # they're not unrestricted float/double.
18242 return not type.isFloat() or not type.isUnrestricted()
18244 if type.isRecord():
18245 # Records are okay, as long as the value type is.
18246 # Per spec, only strings are allowed as keys.
18247 return CGDictionary.typeSafeToJSONify(type.inner)
18249 return False
18251 @staticmethod
18252 def dictionarySafeToJSONify(dictionary):
18253 # The dictionary itself is OK, so we're good if all our types are.
18254 return all(CGDictionary.typeSafeToJSONify(m.type) for m in dictionary.members)
18257 class CGRegisterWorkerBindings(CGAbstractMethod):
18258 def __init__(self, config):
18259 CGAbstractMethod.__init__(
18260 self,
18261 None,
18262 "RegisterWorkerBindings",
18263 "bool",
18264 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
18266 self.config = config
18268 def definition_body(self):
18269 descriptors = self.config.getDescriptors(
18270 hasInterfaceObject=True, isExposedInAnyWorker=True, register=True
18272 conditions = []
18273 for desc in descriptors:
18274 bindingNS = toBindingNamespace(desc.name)
18275 condition = "!%s::GetConstructorObject(aCx)" % bindingNS
18276 if desc.isExposedConditionally():
18277 condition = (
18278 "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
18280 conditions.append(condition)
18281 lines = [
18282 CGIfWrapper(CGGeneric("return false;\n"), condition)
18283 for condition in conditions
18285 lines.append(CGGeneric("return true;\n"))
18286 return CGList(lines, "\n").define()
18289 class CGRegisterWorkerDebuggerBindings(CGAbstractMethod):
18290 def __init__(self, config):
18291 CGAbstractMethod.__init__(
18292 self,
18293 None,
18294 "RegisterWorkerDebuggerBindings",
18295 "bool",
18296 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
18298 self.config = config
18300 def definition_body(self):
18301 descriptors = self.config.getDescriptors(
18302 hasInterfaceObject=True, isExposedInWorkerDebugger=True, register=True
18304 conditions = []
18305 for desc in descriptors:
18306 bindingNS = toBindingNamespace(desc.name)
18307 condition = "!%s::GetConstructorObject(aCx)" % bindingNS
18308 if desc.isExposedConditionally():
18309 condition = (
18310 "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
18312 conditions.append(condition)
18313 lines = [
18314 CGIfWrapper(CGGeneric("return false;\n"), condition)
18315 for condition in conditions
18317 lines.append(CGGeneric("return true;\n"))
18318 return CGList(lines, "\n").define()
18321 class CGRegisterWorkletBindings(CGAbstractMethod):
18322 def __init__(self, config):
18323 CGAbstractMethod.__init__(
18324 self,
18325 None,
18326 "RegisterWorkletBindings",
18327 "bool",
18328 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
18330 self.config = config
18332 def definition_body(self):
18333 descriptors = self.config.getDescriptors(
18334 hasInterfaceObject=True, isExposedInAnyWorklet=True, register=True
18336 conditions = []
18337 for desc in descriptors:
18338 bindingNS = toBindingNamespace(desc.name)
18339 condition = "!%s::GetConstructorObject(aCx)" % bindingNS
18340 if desc.isExposedConditionally():
18341 condition = (
18342 "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
18344 conditions.append(condition)
18345 lines = [
18346 CGIfWrapper(CGGeneric("return false;\n"), condition)
18347 for condition in conditions
18349 lines.append(CGGeneric("return true;\n"))
18350 return CGList(lines, "\n").define()
18353 class CGRegisterShadowRealmBindings(CGAbstractMethod):
18354 def __init__(self, config):
18355 CGAbstractMethod.__init__(
18356 self,
18357 None,
18358 "RegisterShadowRealmBindings",
18359 "bool",
18360 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
18362 self.config = config
18364 def definition_body(self):
18365 descriptors = self.config.getDescriptors(
18366 hasInterfaceObject=True, isExposedInShadowRealms=True, register=True
18368 conditions = []
18369 for desc in descriptors:
18370 bindingNS = toBindingNamespace(desc.name)
18371 condition = "!%s::GetConstructorObject(aCx)" % bindingNS
18372 if desc.isExposedConditionally():
18373 condition = (
18374 "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
18376 conditions.append(condition)
18377 lines = [
18378 CGIfWrapper(CGGeneric("return false;\n"), condition)
18379 for condition in conditions
18381 lines.append(CGGeneric("return true;\n"))
18382 return CGList(lines, "\n").define()
18385 def BindingNamesOffsetEnum(name):
18386 return CppKeywords.checkMethodName(name.replace(" ", "_"))
18389 class CGGlobalNames(CGGeneric):
18390 def __init__(self, names):
18392 names is expected to be a list of tuples of the name and the descriptor it refers to.
18395 strings = []
18396 entries = []
18397 for name, desc in names:
18398 # Generate the entry declaration
18399 # XXX(nika): mCreate & mEnabled require relocations. If we want to
18400 # reduce those, we could move them into separate tables.
18401 nativeEntry = fill(
18404 /* mNameOffset */ BindingNamesOffset::${nameOffset},
18405 /* mNameLength */ ${nameLength},
18406 /* mConstructorId */ constructors::id::${realname},
18407 /* mCreate */ ${realname}_Binding::CreateInterfaceObjects,
18408 /* mEnabled */ ${enabled}
18410 """,
18411 nameOffset=BindingNamesOffsetEnum(name),
18412 nameLength=len(name),
18413 name=name,
18414 realname=desc.name,
18415 enabled=(
18416 "%s_Binding::ConstructorEnabled" % desc.name
18417 if desc.isExposedConditionally()
18418 else "nullptr"
18422 entries.append((name.encode(), nativeEntry))
18424 # Unfortunately, when running tests, we may have no entries.
18425 # PerfectHash will assert if we give it an empty set of entries, so we
18426 # just generate a dummy value.
18427 if len(entries) == 0:
18428 CGGeneric.__init__(
18429 self,
18430 define=dedent(
18432 static_assert(false, "No WebIDL global name entries!");
18436 return
18438 # Build the perfect hash function.
18439 phf = PerfectHash(entries, GLOBAL_NAMES_PHF_SIZE)
18441 # Generate code for the PHF
18442 phfCodegen = phf.codegen(
18443 "WebIDLGlobalNameHash::sEntries", "WebIDLNameTableEntry"
18445 entries = phfCodegen.gen_entries(lambda e: e[1])
18446 getter = phfCodegen.gen_jslinearstr_getter(
18447 name="WebIDLGlobalNameHash::GetEntry",
18448 return_type="const WebIDLNameTableEntry*",
18449 return_entry=dedent(
18451 if (JS_LinearStringEqualsAscii(aKey, BindingName(entry.mNameOffset), entry.mNameLength)) {
18452 return &entry;
18454 return nullptr;
18459 define = fill(
18461 const uint32_t WebIDLGlobalNameHash::sCount = ${count};
18463 $*{entries}
18465 $*{getter}
18466 """,
18467 count=len(phf.entries),
18468 strings="\n".join(strings) + ";\n",
18469 entries=entries,
18470 getter=getter,
18472 CGGeneric.__init__(self, define=define)
18475 def dependencySortObjects(objects, dependencyGetter, nameGetter):
18477 Sort IDL objects with dependencies on each other such that if A
18478 depends on B then B will come before A. This is needed for
18479 declaring C++ classes in the right order, for example. Objects
18480 that have no dependencies are just sorted by name.
18482 objects should be something that can produce a set of objects
18483 (e.g. a set, iterator, list, etc).
18485 dependencyGetter is something that, given an object, should return
18486 the set of objects it depends on.
18488 # XXXbz this will fail if we have two webidl files F1 and F2 such that F1
18489 # declares an object which depends on an object in F2, and F2 declares an
18490 # object (possibly a different one!) that depends on an object in F1. The
18491 # good news is that I expect this to never happen.
18492 sortedObjects = []
18493 objects = set(objects)
18494 while len(objects) != 0:
18495 # Find the dictionaries that don't depend on anything else
18496 # anymore and move them over.
18497 toMove = [o for o in objects if len(dependencyGetter(o) & objects) == 0]
18498 if len(toMove) == 0:
18499 raise TypeError(
18500 "Loop in dependency graph\n" + "\n".join(o.location for o in objects)
18502 objects = objects - set(toMove)
18503 sortedObjects.extend(sorted(toMove, key=nameGetter))
18504 return sortedObjects
18507 class ForwardDeclarationBuilder:
18509 Create a canonical representation of a set of namespaced forward
18510 declarations.
18513 def __init__(self):
18515 The set of declarations is represented as a tree of nested namespaces.
18516 Each tree node has a set of declarations |decls| and a dict |children|.
18517 Each declaration is a pair consisting of the class name and a boolean
18518 that is true iff the class is really a struct. |children| maps the
18519 names of inner namespaces to the declarations in that namespace.
18521 self.decls = set()
18522 self.children = {}
18524 def _ensureNonTemplateType(self, type):
18525 if "<" in type:
18526 # This is a templated type. We don't really know how to
18527 # forward-declare those, and trying to do it naively is not going to
18528 # go well (e.g. we may have :: characters inside the type we're
18529 # templated on!). Just bail out.
18530 raise TypeError(
18531 "Attempt to use ForwardDeclarationBuilder on "
18532 "templated type %s. We don't know how to do that "
18533 "yet." % type
18536 def _listAdd(self, namespaces, name, isStruct=False):
18538 Add a forward declaration, where |namespaces| is a list of namespaces.
18539 |name| should not contain any other namespaces.
18541 if namespaces:
18542 child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder())
18543 child._listAdd(namespaces[1:], name, isStruct)
18544 else:
18545 assert "::" not in name
18546 self.decls.add((name, isStruct))
18548 def addInMozillaDom(self, name, isStruct=False):
18550 Add a forward declaration to the mozilla::dom:: namespace. |name| should not
18551 contain any other namespaces.
18553 self._ensureNonTemplateType(name)
18554 self._listAdd(["mozilla", "dom"], name, isStruct)
18556 def add(self, nativeType, isStruct=False):
18558 Add a forward declaration, where |nativeType| is a string containing
18559 the type and its namespaces, in the usual C++ way.
18561 self._ensureNonTemplateType(nativeType)
18562 components = nativeType.split("::")
18563 self._listAdd(components[:-1], components[-1], isStruct)
18565 def _build(self, atTopLevel):
18567 Return a codegenerator for the forward declarations.
18569 decls = []
18570 if self.decls:
18571 decls.append(
18572 CGList(
18574 CGClassForwardDeclare(cname, isStruct)
18575 for cname, isStruct in sorted(self.decls)
18579 for namespace, child in sorted(self.children.items()):
18580 decls.append(CGNamespace(namespace, child._build(atTopLevel=False)))
18582 cg = CGList(decls, "\n")
18583 if not atTopLevel and len(decls) + len(self.decls) > 1:
18584 cg = CGWrapper(cg, pre="\n", post="\n")
18585 return cg
18587 def build(self):
18588 return self._build(atTopLevel=True)
18590 def forwardDeclareForType(self, t, config):
18591 t = t.unroll()
18592 if t.isGeckoInterface():
18593 name = t.inner.identifier.name
18594 try:
18595 desc = config.getDescriptor(name)
18596 self.add(desc.nativeType)
18597 except NoSuchDescriptorError:
18598 pass
18600 # Note: SpiderMonkey interfaces are typedefs, so can't be
18601 # forward-declared
18602 elif t.isPromise():
18603 self.addInMozillaDom("Promise")
18604 elif t.isCallback():
18605 self.addInMozillaDom(t.callback.identifier.name)
18606 elif t.isDictionary():
18607 self.addInMozillaDom(t.inner.identifier.name, isStruct=True)
18608 elif t.isCallbackInterface():
18609 self.addInMozillaDom(t.inner.identifier.name)
18610 elif t.isUnion():
18611 # Forward declare both the owning and non-owning version,
18612 # since we don't know which one we might want
18613 self.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
18614 self.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
18615 elif t.isRecord():
18616 self.forwardDeclareForType(t.inner, config)
18617 # Don't need to do anything for void, primitive, string, any or object.
18618 # There may be some other cases we are missing.
18621 class CGForwardDeclarations(CGWrapper):
18623 Code generate the forward declarations for a header file.
18624 additionalDeclarations is a list of tuples containing a classname and a
18625 boolean. If the boolean is true we will declare a struct, otherwise we'll
18626 declare a class.
18629 def __init__(
18630 self,
18631 config,
18632 descriptors,
18633 callbacks,
18634 dictionaries,
18635 callbackInterfaces,
18636 additionalDeclarations=[],
18638 builder = ForwardDeclarationBuilder()
18640 # Needed for at least Wrap.
18641 for d in descriptors:
18642 # If this is a generated iterator interface, we only create these
18643 # in the generated bindings, and don't need to forward declare.
18644 if (
18645 d.interface.isIteratorInterface()
18646 or d.interface.isAsyncIteratorInterface()
18648 continue
18649 builder.add(d.nativeType)
18650 if d.interface.isSerializable():
18651 builder.add("nsIGlobalObject")
18652 # If we're an interface and we have a maplike/setlike declaration,
18653 # we'll have helper functions exposed to the native side of our
18654 # bindings, which will need to show up in the header. If either of
18655 # our key/value types are interfaces, they'll be passed as
18656 # arguments to helper functions, and they'll need to be forward
18657 # declared in the header.
18658 if d.interface.maplikeOrSetlikeOrIterable:
18659 if d.interface.maplikeOrSetlikeOrIterable.hasKeyType():
18660 builder.forwardDeclareForType(
18661 d.interface.maplikeOrSetlikeOrIterable.keyType, config
18663 if d.interface.maplikeOrSetlikeOrIterable.hasValueType():
18664 builder.forwardDeclareForType(
18665 d.interface.maplikeOrSetlikeOrIterable.valueType, config
18668 for m in d.interface.members:
18669 if m.isAttr() and m.type.isObservableArray():
18670 builder.forwardDeclareForType(m.type, config)
18672 # We just about always need NativePropertyHooks
18673 builder.addInMozillaDom("NativePropertyHooks", isStruct=True)
18674 builder.addInMozillaDom("ProtoAndIfaceCache")
18676 for callback in callbacks:
18677 builder.addInMozillaDom(callback.identifier.name)
18678 for t in getTypesFromCallback(callback):
18679 builder.forwardDeclareForType(t, config)
18681 for d in callbackInterfaces:
18682 builder.add(d.nativeType)
18683 builder.add(d.nativeType + "Atoms", isStruct=True)
18684 for t in getTypesFromDescriptor(d):
18685 builder.forwardDeclareForType(t, config)
18686 if d.hasCEReactions():
18687 builder.addInMozillaDom("DocGroup")
18689 for d in dictionaries:
18690 if len(d.members) > 0:
18691 builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True)
18692 for t in getTypesFromDictionary(d):
18693 builder.forwardDeclareForType(t, config)
18695 for className, isStruct in additionalDeclarations:
18696 builder.add(className, isStruct=isStruct)
18698 CGWrapper.__init__(self, builder.build())
18701 def dependencySortDictionariesAndUnionsAndCallbacks(types):
18702 def getDependenciesFromType(type):
18703 if type.isDictionary():
18704 return set([type.unroll().inner])
18705 if type.isSequence():
18706 return getDependenciesFromType(type.unroll())
18707 if type.isUnion():
18708 return set([type.unroll()])
18709 if type.isRecord():
18710 return set([type.unroll().inner])
18711 if type.isCallback():
18712 return set([type.unroll()])
18713 return set()
18715 def getDependencies(unionTypeOrDictionaryOrCallback):
18716 if isinstance(unionTypeOrDictionaryOrCallback, IDLDictionary):
18717 deps = set()
18718 if unionTypeOrDictionaryOrCallback.parent:
18719 deps.add(unionTypeOrDictionaryOrCallback.parent)
18720 for member in unionTypeOrDictionaryOrCallback.members:
18721 deps |= getDependenciesFromType(member.type)
18722 return deps
18724 if (
18725 unionTypeOrDictionaryOrCallback.isType()
18726 and unionTypeOrDictionaryOrCallback.isUnion()
18728 deps = set()
18729 for member in unionTypeOrDictionaryOrCallback.flatMemberTypes:
18730 deps |= getDependenciesFromType(member)
18731 return deps
18733 assert unionTypeOrDictionaryOrCallback.isCallback()
18734 return set()
18736 def getName(unionTypeOrDictionaryOrCallback):
18737 if isinstance(unionTypeOrDictionaryOrCallback, IDLDictionary):
18738 return unionTypeOrDictionaryOrCallback.identifier.name
18740 if (
18741 unionTypeOrDictionaryOrCallback.isType()
18742 and unionTypeOrDictionaryOrCallback.isUnion()
18744 return unionTypeOrDictionaryOrCallback.name
18746 assert unionTypeOrDictionaryOrCallback.isCallback()
18747 return unionTypeOrDictionaryOrCallback.identifier.name
18749 return dependencySortObjects(types, getDependencies, getName)
18752 class CGBindingRoot(CGThing):
18754 Root codegen class for binding generation. Instantiate the class, and call
18755 declare or define to generate header or cpp code (respectively).
18758 def __init__(self, config, prefix, webIDLFile):
18759 bindingHeaders = dict.fromkeys(
18760 ("mozilla/dom/NonRefcountedDOMObject.h", "MainThreadUtils.h"), True
18762 bindingDeclareHeaders = dict.fromkeys(
18764 "mozilla/dom/BindingDeclarations.h",
18765 "mozilla/dom/Nullable.h",
18767 True,
18770 descriptors = config.getDescriptors(
18771 webIDLFile=webIDLFile, hasInterfaceOrInterfacePrototypeObject=True
18774 unionTypes = UnionsForFile(config, webIDLFile)
18777 unionHeaders,
18778 unionImplheaders,
18779 unionDeclarations,
18780 traverseMethods,
18781 unlinkMethods,
18782 unionStructs,
18783 ) = UnionTypes(unionTypes, config)
18785 bindingDeclareHeaders.update(dict.fromkeys(unionHeaders, True))
18786 bindingHeaders.update(dict.fromkeys(unionImplheaders, True))
18787 bindingDeclareHeaders["mozilla/dom/UnionMember.h"] = len(unionStructs) > 0
18788 bindingDeclareHeaders["mozilla/dom/FakeString.h"] = len(unionStructs) > 0
18789 # BindingUtils.h is only needed for SetToObject.
18790 # If it stops being inlined or stops calling CallerSubsumes
18791 # both this bit and the bit in UnionTypes can be removed.
18792 bindingDeclareHeaders["mozilla/dom/BindingUtils.h"] = any(
18793 d.isObject() for t in unionTypes for d in t.flatMemberTypes
18795 bindingHeaders["mozilla/dom/IterableIterator.h"] = any(
18797 d.interface.isIteratorInterface()
18798 and d.interface.maplikeOrSetlikeOrIterable.isPairIterator()
18800 or d.interface.isAsyncIteratorInterface()
18801 or d.interface.isIterable()
18802 or d.interface.isAsyncIterable()
18803 for d in descriptors
18806 def memberNeedsSubjectPrincipal(d, m):
18807 if m.isAttr():
18808 return (
18809 "needsSubjectPrincipal" in d.getExtendedAttributes(m, getter=True)
18810 ) or (
18811 not m.readonly
18812 and "needsSubjectPrincipal"
18813 in d.getExtendedAttributes(m, setter=True)
18815 return m.isMethod() and "needsSubjectPrincipal" in d.getExtendedAttributes(
18819 if any(
18820 memberNeedsSubjectPrincipal(d, m)
18821 for d in descriptors
18822 for m in d.interface.members
18824 bindingHeaders["mozilla/BasePrincipal.h"] = True
18825 bindingHeaders["nsJSPrincipals.h"] = True
18827 # The conditions for which we generate profiler labels are fairly
18828 # complicated. The check below is a little imprecise to make it simple.
18829 # It includes the profiler header in all cases where it is necessary and
18830 # generates only a few false positives.
18831 bindingHeaders["mozilla/ProfilerLabels.h"] = any(
18832 # constructor profiler label
18833 d.interface.legacyFactoryFunctions
18834 or (d.interface.hasInterfaceObject() and d.interface.ctor())
18835 or any(
18836 # getter/setter profiler labels
18837 m.isAttr()
18838 # method profiler label
18839 or m.isMethod()
18840 for m in d.interface.members
18842 for d in descriptors
18845 def descriptorHasCrossOriginProperties(desc):
18846 def hasCrossOriginProperty(m):
18847 props = memberProperties(m, desc)
18848 return (
18849 props.isCrossOriginMethod
18850 or props.isCrossOriginGetter
18851 or props.isCrossOriginSetter
18854 return any(hasCrossOriginProperty(m) for m in desc.interface.members)
18856 def descriptorHasObservableArrayTypes(desc):
18857 def hasObservableArrayTypes(m):
18858 return m.isAttr() and m.type.isObservableArray()
18860 return any(hasObservableArrayTypes(m) for m in desc.interface.members)
18862 bindingDeclareHeaders["mozilla/dom/RemoteObjectProxy.h"] = any(
18863 descriptorHasCrossOriginProperties(d) for d in descriptors
18865 bindingDeclareHeaders["jsapi.h"] = any(
18866 descriptorHasCrossOriginProperties(d)
18867 or descriptorHasObservableArrayTypes(d)
18868 for d in descriptors
18870 bindingDeclareHeaders["js/TypeDecls.h"] = not bindingDeclareHeaders["jsapi.h"]
18871 bindingDeclareHeaders["js/RootingAPI.h"] = not bindingDeclareHeaders["jsapi.h"]
18873 # JS::IsCallable
18874 bindingDeclareHeaders["js/CallAndConstruct.h"] = True
18876 def descriptorHasIteratorAlias(desc):
18877 def hasIteratorAlias(m):
18878 return m.isMethod() and (
18879 ("@@iterator" in m.aliases) or ("@@asyncIterator" in m.aliases)
18882 return any(hasIteratorAlias(m) for m in desc.interface.members)
18884 bindingHeaders["js/Symbol.h"] = any(
18885 descriptorHasIteratorAlias(d) for d in descriptors
18888 bindingHeaders["js/shadow/Object.h"] = any(
18889 d.interface.hasMembersInSlots() for d in descriptors
18892 # The symbols supplied by this header are used so ubiquitously it's not
18893 # worth the effort delineating the exact dependency, if it can't be done
18894 # *at* the places where their definitions are required.
18895 bindingHeaders["js/experimental/JitInfo.h"] = True
18897 # JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, and
18898 # JS::SetReservedSlot are also used too many places to restate
18899 # dependency logic.
18900 bindingHeaders["js/Object.h"] = True
18902 # JS::IsCallable, JS::Call, JS::Construct
18903 bindingHeaders["js/CallAndConstruct.h"] = True
18905 # JS_IsExceptionPending
18906 bindingHeaders["js/Exception.h"] = True
18908 # JS::Map{Clear, Delete, Has, Get, Set}
18909 bindingHeaders["js/MapAndSet.h"] = True
18911 # JS_DefineElement, JS_DefineProperty, JS_DefinePropertyById,
18912 # JS_DefineUCProperty, JS_ForwardGetPropertyTo, JS_GetProperty,
18913 # JS_GetPropertyById, JS_HasPropertyById, JS_SetProperty,
18914 # JS_SetPropertyById
18915 bindingHeaders["js/PropertyAndElement.h"] = True
18917 # JS_GetOwnPropertyDescriptorById
18918 bindingHeaders["js/PropertyDescriptor.h"] = True
18920 def descriptorDeprecated(desc):
18921 iface = desc.interface
18922 return any(
18923 m.getExtendedAttribute("Deprecated") for m in iface.members + [iface]
18926 bindingHeaders["mozilla/dom/Document.h"] = any(
18927 descriptorDeprecated(d) for d in descriptors
18930 bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any(
18931 d.concrete and d.proxy for d in descriptors
18934 bindingHeaders["mozilla/dom/ProxyHandlerUtils.h"] = any(
18935 d.concrete and d.proxy for d in descriptors
18938 bindingHeaders["js/String.h"] = any(
18939 d.needsMissingPropUseCounters for d in descriptors
18942 hasCrossOriginObjects = any(
18943 d.concrete and d.isMaybeCrossOriginObject() for d in descriptors
18945 bindingHeaders["mozilla/dom/MaybeCrossOriginObject.h"] = hasCrossOriginObjects
18946 bindingHeaders["AccessCheck.h"] = hasCrossOriginObjects
18947 hasCEReactions = any(d.hasCEReactions() for d in descriptors)
18948 bindingHeaders["mozilla/dom/CustomElementRegistry.h"] = hasCEReactions
18949 bindingHeaders["mozilla/dom/DocGroup.h"] = hasCEReactions
18951 def descriptorHasChromeOnly(desc):
18952 ctor = desc.interface.ctor()
18954 return (
18955 any(
18956 isChromeOnly(a) or needsCallerType(a)
18957 for a in desc.interface.members
18959 or desc.interface.getExtendedAttribute("ChromeOnly") is not None
18961 # JS-implemented interfaces with an interface object get a
18962 # chromeonly _create method. And interfaces with an
18963 # interface object might have a ChromeOnly constructor.
18965 desc.interface.hasInterfaceObject()
18966 and (
18967 desc.interface.isJSImplemented()
18968 or (ctor and isChromeOnly(ctor))
18973 # XXXkhuey ugly hack but this is going away soon.
18974 bindingHeaders["xpcprivate.h"] = webIDLFile.endswith("EventTarget.webidl")
18976 hasThreadChecks = any(d.hasThreadChecks() for d in descriptors)
18977 bindingHeaders["nsThreadUtils.h"] = hasThreadChecks
18979 dictionaries = config.getDictionaries(webIDLFile)
18981 def dictionaryHasChromeOnly(dictionary):
18982 while dictionary:
18983 if any(isChromeOnly(m) for m in dictionary.members):
18984 return True
18985 dictionary = dictionary.parent
18986 return False
18988 def needsNonSystemPrincipal(member):
18989 return (
18990 member.getExtendedAttribute("NeedsSubjectPrincipal") == ["NonSystem"]
18991 or member.getExtendedAttribute("SetterNeedsSubjectPrincipal")
18992 == ["NonSystem"]
18993 or member.getExtendedAttribute("GetterNeedsSubjectPrincipal")
18994 == ["NonSystem"]
18997 def descriptorNeedsNonSystemPrincipal(d):
18998 return any(needsNonSystemPrincipal(m) for m in d.interface.members)
19000 def descriptorHasPrefDisabler(desc):
19001 iface = desc.interface
19002 return any(
19003 PropertyDefiner.getControllingCondition(m, desc).hasDisablers()
19004 for m in iface.members
19005 if (m.isMethod() or m.isAttr() or m.isConst())
19008 def addPrefHeaderForObject(bindingHeaders, obj):
19010 obj might be a dictionary member or an interface.
19012 if obj is not None:
19013 pref = PropertyDefiner.getStringAttr(obj, "Pref")
19014 if pref:
19015 bindingHeaders[prefHeader(pref)] = True
19017 def addPrefHeadersForDictionary(bindingHeaders, dictionary):
19018 while dictionary:
19019 for m in dictionary.members:
19020 addPrefHeaderForObject(bindingHeaders, m)
19021 dictionary = dictionary.parent
19023 for d in dictionaries:
19024 addPrefHeadersForDictionary(bindingHeaders, d)
19025 for d in descriptors:
19026 interface = d.interface
19027 addPrefHeaderForObject(bindingHeaders, interface)
19028 addPrefHeaderForObject(bindingHeaders, interface.ctor())
19030 bindingHeaders["mozilla/dom/WebIDLPrefs.h"] = any(
19031 descriptorHasPrefDisabler(d) for d in descriptors
19033 bindingHeaders["nsContentUtils.h"] = (
19034 any(descriptorHasChromeOnly(d) for d in descriptors)
19035 or any(descriptorNeedsNonSystemPrincipal(d) for d in descriptors)
19036 or any(dictionaryHasChromeOnly(d) for d in dictionaries)
19038 hasNonEmptyDictionaries = any(len(dict.members) > 0 for dict in dictionaries)
19039 callbacks = config.getCallbacks(webIDLFile)
19040 callbackDescriptors = config.getDescriptors(
19041 webIDLFile=webIDLFile, isCallback=True
19043 jsImplemented = config.getDescriptors(
19044 webIDLFile=webIDLFile, isJSImplemented=True
19046 bindingDeclareHeaders["nsWeakReference.h"] = jsImplemented
19047 bindingDeclareHeaders["mozilla/dom/PrototypeList.h"] = descriptors
19048 bindingHeaders["nsIGlobalObject.h"] = jsImplemented
19049 bindingHeaders["AtomList.h"] = (
19050 hasNonEmptyDictionaries or jsImplemented or callbackDescriptors
19053 if callbackDescriptors:
19054 bindingDeclareHeaders["mozilla/ErrorResult.h"] = True
19056 def descriptorClearsPropsInSlots(descriptor):
19057 if not descriptor.wrapperCache:
19058 return False
19059 return any(
19060 m.isAttr() and m.getExtendedAttribute("StoreInSlot")
19061 for m in descriptor.interface.members
19064 bindingHeaders["nsJSUtils.h"] = any(
19065 descriptorClearsPropsInSlots(d) for d in descriptors
19068 # Make sure we can sanely use binding_detail in generated code.
19069 cgthings = [
19070 CGGeneric(
19071 dedent(
19073 namespace binding_detail {}; // Just to make sure it's known as a namespace
19074 using namespace mozilla::dom::binding_detail;
19080 # Do codegen for all the enums
19081 enums = config.getEnums(webIDLFile)
19082 cgthings.extend(CGEnum(e) for e in enums)
19083 maxEnumValues = CGList([CGMaxContiguousEnumValue(e) for e in enums], "\n")
19085 bindingDeclareHeaders["mozilla/Span.h"] = enums
19086 bindingDeclareHeaders["mozilla/ArrayUtils.h"] = enums
19087 bindingDeclareHeaders["mozilla/EnumTypeTraits.h"] = enums
19089 hasCode = descriptors or callbackDescriptors or dictionaries or callbacks
19090 bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode
19091 bindingHeaders["mozilla/OwningNonNull.h"] = hasCode
19092 bindingHeaders["<type_traits>"] = hasCode
19093 bindingHeaders["mozilla/dom/BindingDeclarations.h"] = not hasCode and enums
19095 bindingHeaders["WrapperFactory.h"] = descriptors
19096 bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors
19097 bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries # AutoJSAPI
19098 # Ensure we see our enums in the generated .cpp file, for the ToJSValue
19099 # method body. Also ensure that we see jsapi.h.
19100 if enums:
19101 bindingHeaders[CGHeaders.getDeclarationFilename(enums[0])] = True
19102 bindingHeaders["jsapi.h"] = True
19104 # For things that have [UseCounter] or [InstrumentedProps] or [Trial]
19105 for d in descriptors:
19106 if d.concrete:
19107 if d.instrumentedProps:
19108 bindingHeaders["mozilla/UseCounter.h"] = True
19109 if d.needsMissingPropUseCounters:
19110 bindingHeaders[prefHeader(MISSING_PROP_PREF)] = True
19111 if d.interface.isSerializable():
19112 bindingHeaders["mozilla/dom/StructuredCloneTags.h"] = True
19113 if d.wantsXrays:
19114 bindingHeaders["mozilla/Atomics.h"] = True
19115 bindingHeaders["mozilla/dom/XrayExpandoClass.h"] = True
19116 if d.wantsXrayExpandoClass:
19117 bindingHeaders["XrayWrapper.h"] = True
19118 for m in d.interface.members:
19119 if m.getExtendedAttribute("UseCounter"):
19120 bindingHeaders["mozilla/UseCounter.h"] = True
19121 if m.getExtendedAttribute("Trial"):
19122 bindingHeaders["mozilla/OriginTrials.h"] = True
19124 bindingHeaders["mozilla/dom/SimpleGlobalObject.h"] = any(
19125 CGDictionary.dictionarySafeToJSONify(d) for d in dictionaries
19128 for ancestor in (findAncestorWithInstrumentedProps(d) for d in descriptors):
19129 if not ancestor:
19130 continue
19131 bindingHeaders[CGHeaders.getDeclarationFilename(ancestor)] = True
19133 cgthings.extend(traverseMethods)
19134 cgthings.extend(unlinkMethods)
19136 # Do codegen for all the dictionaries. We have to be a bit careful
19137 # here, because we have to generate these in order from least derived
19138 # to most derived so that class inheritance works out. We also have to
19139 # generate members before the dictionary that contains them.
19141 for t in dependencySortDictionariesAndUnionsAndCallbacks(
19142 dictionaries + unionStructs + callbacks
19144 if t.isDictionary():
19145 cgthings.append(CGDictionary(t, config))
19146 elif t.isUnion():
19147 cgthings.append(CGUnionStruct(t, config))
19148 cgthings.append(CGUnionStruct(t, config, True))
19149 else:
19150 assert t.isCallback()
19151 cgthings.append(CGCallbackFunction(t, config))
19152 cgthings.append(CGNamespace("binding_detail", CGFastCallback(t)))
19154 # Do codegen for all the descriptors
19155 cgthings.extend(
19156 [CGDescriptor(x, config.attributeTemplates) for x in descriptors]
19159 # Do codegen for all the callback interfaces.
19160 cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors])
19162 cgthings.extend(
19164 CGNamespace("binding_detail", CGFastCallback(x.interface))
19165 for x in callbackDescriptors
19169 # Do codegen for JS implemented classes
19170 def getParentDescriptor(desc):
19171 if not desc.interface.parent:
19172 return set()
19173 return {desc.getDescriptor(desc.interface.parent.identifier.name)}
19175 for x in dependencySortObjects(
19176 jsImplemented, getParentDescriptor, lambda d: d.interface.identifier.name
19178 cgthings.append(
19179 CGCallbackInterface(x, spiderMonkeyInterfacesAreStructs=True)
19181 cgthings.append(CGJSImplClass(x))
19183 # And make sure we have the right number of newlines at the end
19184 curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
19186 # Wrap all of that in our namespaces.
19188 if len(maxEnumValues) > 0:
19189 curr = CGNamespace("dom", CGWrapper(curr, pre="\n"))
19190 curr = CGWrapper(CGList([curr, maxEnumValues], "\n\n"), post="\n\n")
19191 curr = CGNamespace("mozilla", CGWrapper(curr, pre="\n"))
19192 else:
19193 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, pre="\n"))
19195 curr = CGList(
19197 CGForwardDeclarations(
19198 config,
19199 descriptors,
19200 callbacks,
19201 dictionaries,
19202 callbackDescriptors + jsImplemented,
19203 additionalDeclarations=unionDeclarations,
19205 curr,
19207 "\n",
19210 # Add header includes.
19211 bindingHeaders = [
19212 header for header, include in bindingHeaders.items() if include
19214 bindingDeclareHeaders = [
19215 header for header, include in bindingDeclareHeaders.items() if include
19218 curr = CGHeaders(
19219 descriptors,
19220 dictionaries,
19221 callbacks,
19222 callbackDescriptors,
19223 bindingDeclareHeaders,
19224 bindingHeaders,
19225 prefix,
19226 curr,
19227 config,
19228 jsImplemented,
19231 # Add include guards.
19232 curr = CGIncludeGuard(prefix, curr)
19234 # Add the auto-generated comment.
19235 curr = CGWrapper(
19236 curr,
19237 pre=(
19238 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT % os.path.basename(webIDLFile)
19242 # Store the final result.
19243 self.root = curr
19245 def declare(self):
19246 return stripTrailingWhitespace(self.root.declare())
19248 def define(self):
19249 return stripTrailingWhitespace(self.root.define())
19251 def deps(self):
19252 return self.root.deps()
19255 class CGNativeMember(ClassMethod):
19256 def __init__(
19257 self,
19258 descriptorProvider,
19259 member,
19260 name,
19261 signature,
19262 extendedAttrs,
19263 breakAfter=True,
19264 passJSBitsAsNeeded=True,
19265 visibility="public",
19266 spiderMonkeyInterfacesAreStructs=True,
19267 variadicIsSequence=False,
19268 resultNotAddRefed=False,
19269 virtual=False,
19270 override=False,
19271 canRunScript=False,
19274 If spiderMonkeyInterfacesAreStructs is false, SpiderMonkey interfaces
19275 will be passed as JS::Handle<JSObject*>. If it's true they will be
19276 passed as one of the dom::SpiderMonkeyInterfaceObjectStorage subclasses.
19278 If passJSBitsAsNeeded is false, we don't automatically pass in a
19279 JSContext* or a JSObject* based on the return and argument types. We
19280 can still pass it based on 'implicitJSContext' annotations.
19282 self.descriptorProvider = descriptorProvider
19283 self.member = member
19284 self.extendedAttrs = extendedAttrs
19285 self.resultAlreadyAddRefed = not resultNotAddRefed
19286 self.passJSBitsAsNeeded = passJSBitsAsNeeded
19287 self.spiderMonkeyInterfacesAreStructs = spiderMonkeyInterfacesAreStructs
19288 self.variadicIsSequence = variadicIsSequence
19289 breakAfterSelf = "\n" if breakAfter else ""
19290 ClassMethod.__init__(
19291 self,
19292 name,
19293 self.getReturnType(signature[0], False),
19294 self.getArgs(signature[0], signature[1]),
19295 static=member.isStatic(),
19296 # Mark our getters, which are attrs that
19297 # have a non-void return type, as const.
19298 const=(
19299 not member.isStatic()
19300 and member.isAttr()
19301 and not signature[0].isUndefined()
19303 breakAfterReturnDecl=" ",
19304 breakAfterSelf=breakAfterSelf,
19305 visibility=visibility,
19306 virtual=virtual,
19307 override=override,
19308 canRunScript=canRunScript,
19311 def getReturnType(self, type, isMember):
19312 return self.getRetvalInfo(type, isMember)[0]
19314 def getRetvalInfo(self, type, isMember):
19316 Returns a tuple:
19318 The first element is the type declaration for the retval
19320 The second element is a default value that can be used on error returns.
19321 For cases whose behavior depends on isMember, the second element will be
19322 None if isMember is true.
19324 The third element is a template for actually returning a value stored in
19325 "${declName}" and "${holderName}". This means actually returning it if
19326 we're not outparam, else assigning to the "retval" outparam. If
19327 isMember is true, this can be None, since in that case the caller will
19328 never examine this value.
19330 if type.isUndefined():
19331 return "void", "", ""
19332 if type.isPrimitive() and type.tag() in builtinNames:
19333 result = CGGeneric(builtinNames[type.tag()])
19334 defaultReturnArg = "0"
19335 if type.nullable():
19336 result = CGTemplatedType("Nullable", result)
19337 defaultReturnArg = ""
19338 return (
19339 result.define(),
19340 "%s(%s)" % (result.define(), defaultReturnArg),
19341 "return ${declName};\n",
19343 if type.isJSString():
19344 if isMember:
19345 raise TypeError("JSString not supported as return type member")
19346 # Outparam
19347 return "void", "", "aRetVal.set(${declName});\n"
19348 if type.isDOMString() or type.isUSVString():
19349 if isMember:
19350 # No need for a third element in the isMember case
19351 return "nsString", None, None
19352 # Outparam
19353 return "void", "", "aRetVal = ${declName};\n"
19354 if type.isByteString() or type.isUTF8String():
19355 if isMember:
19356 # No need for a third element in the isMember case
19357 return "nsCString", None, None
19358 # Outparam
19359 return "void", "", "aRetVal = ${declName};\n"
19360 if type.isEnum():
19361 enumName = type.unroll().inner.identifier.name
19362 if type.nullable():
19363 enumName = CGTemplatedType("Nullable", CGGeneric(enumName)).define()
19364 defaultValue = "%s()" % enumName
19365 else:
19366 defaultValue = "%s(0)" % enumName
19367 return enumName, defaultValue, "return ${declName};\n"
19368 if type.isGeckoInterface() or type.isPromise():
19369 if type.isGeckoInterface():
19370 iface = type.unroll().inner
19371 result = CGGeneric(
19372 self.descriptorProvider.getDescriptor(
19373 iface.identifier.name
19374 ).prettyNativeType
19376 else:
19377 result = CGGeneric("Promise")
19378 if self.resultAlreadyAddRefed:
19379 if isMember:
19380 holder = "RefPtr"
19381 else:
19382 holder = "already_AddRefed"
19383 if memberReturnsNewObject(self.member) or isMember:
19384 warning = ""
19385 else:
19386 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"
19387 result = CGWrapper(result, pre=("%s%s<" % (warning, holder)), post=">")
19388 else:
19389 result = CGWrapper(result, post="*")
19390 # Since we always force an owning type for callback return values,
19391 # our ${declName} is an OwningNonNull or RefPtr. So we can just
19392 # .forget() to get our already_AddRefed.
19393 return result.define(), "nullptr", "return ${declName}.forget();\n"
19394 if type.isCallback():
19395 return (
19396 "already_AddRefed<%s>" % type.unroll().callback.identifier.name,
19397 "nullptr",
19398 "return ${declName}.forget();\n",
19400 if type.isAny():
19401 if isMember:
19402 # No need for a third element in the isMember case
19403 return "JS::Value", None, None
19404 # Outparam
19405 return "void", "", "aRetVal.set(${declName});\n"
19407 if type.isObject():
19408 if isMember:
19409 # No need for a third element in the isMember case
19410 return "JSObject*", None, None
19411 return "void", "", "aRetVal.set(${declName});\n"
19412 if type.isSpiderMonkeyInterface():
19413 if isMember:
19414 # No need for a third element in the isMember case
19415 return "JSObject*", None, None
19416 if type.nullable():
19417 returnCode = (
19418 "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj()"
19420 else:
19421 returnCode = "${declName}.Obj()"
19422 return "void", "", "aRetVal.set(%s);\n" % returnCode
19423 if type.isSequence():
19424 # If we want to handle sequence-of-sequences return values, we're
19425 # going to need to fix example codegen to not produce nsTArray<void>
19426 # for the relevant argument...
19427 assert not isMember
19428 # Outparam.
19429 if type.nullable():
19430 returnCode = dedent(
19432 if (${declName}.IsNull()) {
19433 aRetVal.SetNull();
19434 } else {
19435 aRetVal.SetValue() = std::move(${declName}.Value());
19439 else:
19440 returnCode = "aRetVal = std::move(${declName});\n"
19441 return "void", "", returnCode
19442 if type.isRecord():
19443 # If we want to handle record-of-record return values, we're
19444 # going to need to fix example codegen to not produce record<void>
19445 # for the relevant argument...
19446 assert not isMember
19447 # In this case we convert directly into our outparam to start with
19448 return "void", "", ""
19449 if type.isDictionary():
19450 if isMember:
19451 # Only the first member of the tuple matters here, but return
19452 # bogus values for the others in case someone decides to use
19453 # them.
19454 return CGDictionary.makeDictionaryName(type.inner), None, None
19455 # In this case we convert directly into our outparam to start with
19456 return "void", "", ""
19457 if type.isUnion():
19458 if isMember:
19459 # Only the first member of the tuple matters here, but return
19460 # bogus values for the others in case someone decides to use
19461 # them.
19462 return CGUnionStruct.unionTypeDecl(type, True), None, None
19463 # In this case we convert directly into our outparam to start with
19464 return "void", "", ""
19466 raise TypeError("Don't know how to declare return value for %s" % type)
19468 def getArgs(self, returnType, argList):
19469 args = [self.getArg(arg) for arg in argList]
19470 # Now the outparams
19471 if returnType.isJSString():
19472 args.append(Argument("JS::MutableHandle<JSString*>", "aRetVal"))
19473 elif returnType.isDOMString() or returnType.isUSVString():
19474 args.append(Argument("nsString&", "aRetVal"))
19475 elif returnType.isByteString() or returnType.isUTF8String():
19476 args.append(Argument("nsCString&", "aRetVal"))
19477 elif returnType.isSequence():
19478 nullable = returnType.nullable()
19479 if nullable:
19480 returnType = returnType.inner
19481 # And now the actual underlying type
19482 elementDecl = self.getReturnType(returnType.inner, True)
19483 type = CGTemplatedType("nsTArray", CGGeneric(elementDecl))
19484 if nullable:
19485 type = CGTemplatedType("Nullable", type)
19486 args.append(Argument("%s&" % type.define(), "aRetVal"))
19487 elif returnType.isRecord():
19488 nullable = returnType.nullable()
19489 if nullable:
19490 returnType = returnType.inner
19491 # And now the actual underlying type
19492 elementDecl = self.getReturnType(returnType.inner, True)
19493 type = CGTemplatedType(
19494 "Record", [recordKeyDeclType(returnType), CGGeneric(elementDecl)]
19496 if nullable:
19497 type = CGTemplatedType("Nullable", type)
19498 args.append(Argument("%s&" % type.define(), "aRetVal"))
19499 elif returnType.isDictionary():
19500 nullable = returnType.nullable()
19501 if nullable:
19502 returnType = returnType.inner
19503 dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner))
19504 if nullable:
19505 dictType = CGTemplatedType("Nullable", dictType)
19506 args.append(Argument("%s&" % dictType.define(), "aRetVal"))
19507 elif returnType.isUnion():
19508 args.append(
19509 Argument(
19510 "%s&" % CGUnionStruct.unionTypeDecl(returnType, True), "aRetVal"
19513 elif returnType.isAny():
19514 args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal"))
19515 elif returnType.isObject() or returnType.isSpiderMonkeyInterface():
19516 args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal"))
19518 # And the nsIPrincipal
19519 if "needsSubjectPrincipal" in self.extendedAttrs:
19520 if "needsNonSystemSubjectPrincipal" in self.extendedAttrs:
19521 args.append(Argument("nsIPrincipal*", "aPrincipal"))
19522 else:
19523 args.append(Argument("nsIPrincipal&", "aPrincipal"))
19524 # And the caller type, if desired.
19525 if needsCallerType(self.member):
19526 args.append(Argument("CallerType", "aCallerType"))
19527 # And the ErrorResult or OOMReporter
19528 if "needsErrorResult" in self.extendedAttrs:
19529 # Use aRv so it won't conflict with local vars named "rv"
19530 args.append(Argument("ErrorResult&", "aRv"))
19531 elif "canOOM" in self.extendedAttrs:
19532 args.append(Argument("OOMReporter&", "aRv"))
19534 # The legacycaller thisval
19535 if self.member.isMethod() and self.member.isLegacycaller():
19536 # If it has an identifier, we can't deal with it yet
19537 assert self.member.isIdentifierLess()
19538 args.insert(0, Argument("const JS::Value&", "aThisVal"))
19539 # And jscontext bits.
19540 if needCx(
19541 returnType,
19542 argList,
19543 self.extendedAttrs,
19544 self.passJSBitsAsNeeded,
19545 self.member.isStatic(),
19547 args.insert(0, Argument("JSContext*", "cx"))
19548 if needScopeObject(
19549 returnType,
19550 argList,
19551 self.extendedAttrs,
19552 self.descriptorProvider.wrapperCache,
19553 self.passJSBitsAsNeeded,
19554 self.member.getExtendedAttribute("StoreInSlot"),
19556 args.insert(1, Argument("JS::Handle<JSObject*>", "obj"))
19557 # And if we're static, a global
19558 if self.member.isStatic():
19559 args.insert(0, Argument("const GlobalObject&", "global"))
19560 return args
19562 def doGetArgType(self, type, optional, isMember):
19564 The main work of getArgType. Returns a string type decl, whether this
19565 is a const ref, as well as whether the type should be wrapped in
19566 Nullable as needed.
19568 isMember can be false or one of the strings "Sequence", "Variadic",
19569 "Record"
19571 if type.isSequence():
19572 nullable = type.nullable()
19573 if nullable:
19574 type = type.inner
19575 elementType = type.inner
19576 argType = self.getArgType(elementType, False, "Sequence")[0]
19577 decl = CGTemplatedType("Sequence", argType)
19578 return decl.define(), True, True
19580 if type.isRecord():
19581 nullable = type.nullable()
19582 if nullable:
19583 type = type.inner
19584 elementType = type.inner
19585 argType = self.getArgType(elementType, False, "Record")[0]
19586 decl = CGTemplatedType("Record", [recordKeyDeclType(type), argType])
19587 return decl.define(), True, True
19589 if type.isUnion():
19590 # unionTypeDecl will handle nullable types, so return False for
19591 # auto-wrapping in Nullable
19592 return CGUnionStruct.unionTypeDecl(type, isMember), True, False
19594 if type.isPromise():
19595 assert not type.nullable()
19596 if optional or isMember:
19597 typeDecl = "OwningNonNull<Promise>"
19598 else:
19599 typeDecl = "Promise&"
19600 return (typeDecl, False, False)
19602 if type.isGeckoInterface() and not type.isCallbackInterface():
19603 iface = type.unroll().inner
19604 if iface.identifier.name == "WindowProxy":
19605 return "WindowProxyHolder", True, False
19607 argIsPointer = type.nullable() or iface.isExternal()
19608 forceOwningType = iface.isCallback() or isMember
19609 if argIsPointer:
19610 if (optional or isMember) and forceOwningType:
19611 typeDecl = "RefPtr<%s>"
19612 else:
19613 typeDecl = "%s*"
19614 else:
19615 if optional or isMember:
19616 if forceOwningType:
19617 typeDecl = "OwningNonNull<%s>"
19618 else:
19619 typeDecl = "NonNull<%s>"
19620 else:
19621 typeDecl = "%s&"
19622 return (
19624 typeDecl
19625 % self.descriptorProvider.getDescriptor(
19626 iface.identifier.name
19627 ).prettyNativeType
19629 False,
19630 False,
19633 if type.isSpiderMonkeyInterface():
19634 if not self.spiderMonkeyInterfacesAreStructs:
19635 return "JS::Handle<JSObject*>", False, False
19637 # Unroll for the name, in case we're nullable.
19638 return type.unroll().name, True, True
19640 if type.isJSString():
19641 if isMember:
19642 raise TypeError("JSString not supported as member")
19643 return "JS::Handle<JSString*>", False, False
19645 if type.isDOMString() or type.isUSVString():
19646 if isMember:
19647 declType = "nsString"
19648 else:
19649 declType = "nsAString"
19650 return declType, True, False
19652 if type.isByteString() or type.isUTF8String():
19653 # TODO(emilio): Maybe bytestrings could benefit from nsAutoCString
19654 # or such too.
19655 if type.isUTF8String() and not isMember:
19656 declType = "nsACString"
19657 else:
19658 declType = "nsCString"
19659 return declType, True, False
19661 if type.isEnum():
19662 return type.unroll().inner.identifier.name, False, True
19664 if type.isCallback() or type.isCallbackInterface():
19665 forceOwningType = optional or isMember
19666 if type.nullable():
19667 if forceOwningType:
19668 declType = "RefPtr<%s>"
19669 else:
19670 declType = "%s*"
19671 else:
19672 if forceOwningType:
19673 declType = "OwningNonNull<%s>"
19674 else:
19675 declType = "%s&"
19676 if type.isCallback():
19677 name = type.unroll().callback.identifier.name
19678 else:
19679 name = type.unroll().inner.identifier.name
19680 return declType % name, False, False
19682 if type.isAny():
19683 # Don't do the rooting stuff for variadics for now
19684 if isMember:
19685 declType = "JS::Value"
19686 else:
19687 declType = "JS::Handle<JS::Value>"
19688 return declType, False, False
19690 if type.isObject():
19691 if isMember:
19692 declType = "JSObject*"
19693 else:
19694 declType = "JS::Handle<JSObject*>"
19695 return declType, False, False
19697 if type.isDictionary():
19698 typeName = CGDictionary.makeDictionaryName(type.inner)
19699 return typeName, True, True
19701 assert type.isPrimitive()
19703 return builtinNames[type.tag()], False, True
19705 def getArgType(self, type, optional, isMember):
19707 Get the type of an argument declaration. Returns the type CGThing, and
19708 whether this should be a const ref.
19710 isMember can be False, "Sequence", or "Variadic"
19712 decl, ref, handleNullable = self.doGetArgType(type, optional, isMember)
19713 decl = CGGeneric(decl)
19714 if handleNullable and type.nullable():
19715 decl = CGTemplatedType("Nullable", decl)
19716 ref = True
19717 if isMember == "Variadic":
19718 arrayType = "Sequence" if self.variadicIsSequence else "nsTArray"
19719 decl = CGTemplatedType(arrayType, decl)
19720 ref = True
19721 elif optional:
19722 # Note: All variadic args claim to be optional, but we can just use
19723 # empty arrays to represent them not being present.
19724 decl = CGTemplatedType("Optional", decl)
19725 ref = True
19726 return (decl, ref)
19728 def getArg(self, arg):
19730 Get the full argument declaration for an argument
19732 decl, ref = self.getArgType(
19733 arg.type, arg.canHaveMissingValue(), "Variadic" if arg.variadic else False
19735 if ref:
19736 decl = CGWrapper(decl, pre="const ", post="&")
19738 return Argument(decl.define(), arg.identifier.name)
19740 def arguments(self):
19741 return self.member.signatures()[0][1]
19744 class CGExampleMethod(CGNativeMember):
19745 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
19746 CGNativeMember.__init__(
19747 self,
19748 descriptor,
19749 method,
19750 CGSpecializedMethod.makeNativeName(descriptor, method),
19751 signature,
19752 descriptor.getExtendedAttributes(method),
19753 breakAfter=breakAfter,
19754 variadicIsSequence=True,
19757 def declare(self, cgClass):
19758 assert self.member.isMethod()
19759 # We skip declaring ourselves if this is a maplike/setlike/iterable
19760 # method, because those get implemented automatically by the binding
19761 # machinery, so the implementor of the interface doesn't have to worry
19762 # about it.
19763 if self.member.isMaplikeOrSetlikeOrIterableMethod():
19764 return ""
19765 return CGNativeMember.declare(self, cgClass)
19767 def define(self, cgClass):
19768 return ""
19771 class CGExampleGetter(CGNativeMember):
19772 def __init__(self, descriptor, attr):
19773 CGNativeMember.__init__(
19774 self,
19775 descriptor,
19776 attr,
19777 CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
19778 (attr.type, []),
19779 descriptor.getExtendedAttributes(attr, getter=True),
19782 def declare(self, cgClass):
19783 assert self.member.isAttr()
19784 # We skip declaring ourselves if this is a maplike/setlike attr (in
19785 # practice, "size"), because those get implemented automatically by the
19786 # binding machinery, so the implementor of the interface doesn't have to
19787 # worry about it.
19788 if self.member.isMaplikeOrSetlikeAttr():
19789 return ""
19790 return CGNativeMember.declare(self, cgClass)
19792 def define(self, cgClass):
19793 return ""
19796 class CGExampleSetter(CGNativeMember):
19797 def __init__(self, descriptor, attr):
19798 CGNativeMember.__init__(
19799 self,
19800 descriptor,
19801 attr,
19802 CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
19804 BuiltinTypes[IDLBuiltinType.Types.undefined],
19805 [FakeArgument(attr.type)],
19807 descriptor.getExtendedAttributes(attr, setter=True),
19810 def define(self, cgClass):
19811 return ""
19814 class CGBindingImplClass(CGClass):
19816 Common codegen for generating a C++ implementation of a WebIDL interface
19819 def __init__(
19820 self,
19821 descriptor,
19822 cgMethod,
19823 cgGetter,
19824 cgSetter,
19825 wantGetParent=True,
19826 wrapMethodName="WrapObject",
19827 skipStaticMethods=False,
19830 cgMethod, cgGetter and cgSetter are classes used to codegen methods,
19831 getters and setters.
19833 self.descriptor = descriptor
19834 self._deps = descriptor.interface.getDeps()
19836 iface = descriptor.interface
19838 self.methodDecls = []
19840 def appendMethod(m, isConstructor=False):
19841 sigs = m.signatures()
19842 for s in sigs[:-1]:
19843 # Don't put a blank line after overloads, until we
19844 # get to the last one.
19845 self.methodDecls.append(
19846 cgMethod(descriptor, m, s, isConstructor, breakAfter=False)
19848 self.methodDecls.append(cgMethod(descriptor, m, sigs[-1], isConstructor))
19850 if iface.ctor():
19851 appendMethod(iface.ctor(), isConstructor=True)
19852 for n in iface.legacyFactoryFunctions:
19853 appendMethod(n, isConstructor=True)
19854 for m in iface.members:
19855 if m.isMethod():
19856 if m.isIdentifierLess():
19857 continue
19858 if m.isMaplikeOrSetlikeOrIterableMethod():
19859 # Handled by generated code already
19860 continue
19861 if not m.isStatic() or not skipStaticMethods:
19862 appendMethod(m)
19863 elif m.isAttr():
19864 if m.isMaplikeOrSetlikeAttr() or m.type.isObservableArray():
19865 # Handled by generated code already
19866 continue
19867 self.methodDecls.append(cgGetter(descriptor, m))
19868 if not m.readonly:
19869 self.methodDecls.append(cgSetter(descriptor, m))
19871 # Now do the special operations
19872 def appendSpecialOperation(name, op):
19873 if op is None:
19874 return
19875 assert len(op.signatures()) == 1
19876 returnType, args = op.signatures()[0]
19877 # Make a copy of the args, since we plan to modify them.
19878 args = list(args)
19879 if op.isGetter() or op.isDeleter():
19880 # This is a total hack. The '&' belongs with the
19881 # type, not the name! But it works, and is simpler
19882 # than trying to somehow make this pretty.
19883 args.append(
19884 FakeArgument(
19885 BuiltinTypes[IDLBuiltinType.Types.boolean], name="&found"
19888 if name == "Stringifier":
19889 if op.isIdentifierLess():
19890 # XXXbz I wish we were consistent about our renaming here.
19891 name = "Stringify"
19892 else:
19893 # We already added this method
19894 return
19895 if name == "LegacyCaller":
19896 if op.isIdentifierLess():
19897 # XXXbz I wish we were consistent about our renaming here.
19898 name = "LegacyCall"
19899 else:
19900 # We already added this method
19901 return
19902 self.methodDecls.append(
19903 CGNativeMember(
19904 descriptor,
19906 name,
19907 (returnType, args),
19908 descriptor.getExtendedAttributes(op),
19912 # Sort things by name so we get stable ordering in the output.
19913 ops = sorted(descriptor.operations.items(), key=lambda x: x[0])
19914 for name, op in ops:
19915 appendSpecialOperation(name, op)
19916 # If we support indexed properties, then we need a Length()
19917 # method so we know which indices are supported.
19918 if descriptor.supportsIndexedProperties():
19919 # But we don't need it if we already have an infallible
19920 # "length" attribute, which we often do.
19921 haveLengthAttr = any(
19923 for m in iface.members
19924 if m.isAttr()
19925 and CGSpecializedGetterCommon.makeNativeName(descriptor, m) == "Length"
19927 if not haveLengthAttr:
19928 self.methodDecls.append(
19929 CGNativeMember(
19930 descriptor,
19931 FakeMember(),
19932 "Length",
19933 (BuiltinTypes[IDLBuiltinType.Types.unsigned_long], []),
19937 # And if we support named properties we need to be able to
19938 # enumerate the supported names.
19939 if descriptor.supportsNamedProperties():
19940 self.methodDecls.append(
19941 CGNativeMember(
19942 descriptor,
19943 FakeMember(),
19944 "GetSupportedNames",
19946 IDLSequenceType(
19947 None, BuiltinTypes[IDLBuiltinType.Types.domstring]
19955 if descriptor.concrete:
19956 wrapArgs = [
19957 Argument("JSContext*", "aCx"),
19958 Argument("JS::Handle<JSObject*>", "aGivenProto"),
19960 if not descriptor.wrapperCache:
19961 wrapReturnType = "bool"
19962 wrapArgs.append(Argument("JS::MutableHandle<JSObject*>", "aReflector"))
19963 else:
19964 wrapReturnType = "JSObject*"
19965 self.methodDecls.insert(
19967 ClassMethod(
19968 wrapMethodName,
19969 wrapReturnType,
19970 wrapArgs,
19971 virtual=descriptor.wrapperCache,
19972 breakAfterReturnDecl=" ",
19973 override=descriptor.wrapperCache,
19974 body=self.getWrapObjectBody(),
19977 if descriptor.hasCEReactions():
19978 self.methodDecls.insert(
19980 ClassMethod(
19981 "GetDocGroup",
19982 "DocGroup*",
19984 const=True,
19985 breakAfterReturnDecl=" ",
19986 body=self.getGetDocGroupBody(),
19989 if wantGetParent:
19990 self.methodDecls.insert(
19992 ClassMethod(
19993 "GetParentObject",
19994 self.getGetParentObjectReturnType(),
19996 const=True,
19997 breakAfterReturnDecl=" ",
19998 body=self.getGetParentObjectBody(),
20002 # Invoke CGClass.__init__ in any subclasses afterwards to do the actual codegen.
20004 def getWrapObjectBody(self):
20005 return None
20007 def getGetParentObjectReturnType(self):
20008 # The lack of newline before the end of the string is on purpose.
20009 return dedent(
20011 // This should return something that eventually allows finding a
20012 // path to the global this object is associated with. Most simply,
20013 // returning an actual global works.
20014 nsIGlobalObject*"""
20017 def getGetParentObjectBody(self):
20018 return None
20020 def getGetDocGroupBody(self):
20021 return None
20023 def deps(self):
20024 return self._deps
20027 class CGExampleObservableArrayCallback(CGNativeMember):
20028 def __init__(self, descriptor, attr, callbackName):
20029 assert attr.isAttr()
20030 assert attr.type.isObservableArray()
20031 CGNativeMember.__init__(
20032 self,
20033 descriptor,
20034 attr,
20035 self.makeNativeName(attr, callbackName),
20037 BuiltinTypes[IDLBuiltinType.Types.undefined],
20039 FakeArgument(attr.type.inner, "aValue"),
20040 FakeArgument(
20041 BuiltinTypes[IDLBuiltinType.Types.unsigned_long], "aIndex"
20045 ["needsErrorResult"],
20048 def declare(self, cgClass):
20049 assert self.member.isAttr()
20050 assert self.member.type.isObservableArray()
20051 return CGNativeMember.declare(self, cgClass)
20053 def define(self, cgClass):
20054 return ""
20056 @staticmethod
20057 def makeNativeName(attr, callbackName):
20058 assert attr.isAttr()
20059 nativeName = MakeNativeName(attr.identifier.name)
20060 return "On" + callbackName + nativeName
20063 class CGExampleClass(CGBindingImplClass):
20065 Codegen for the actual example class implementation for this descriptor
20068 def __init__(self, descriptor):
20069 CGBindingImplClass.__init__(
20070 self,
20071 descriptor,
20072 CGExampleMethod,
20073 CGExampleGetter,
20074 CGExampleSetter,
20075 wantGetParent=descriptor.wrapperCache,
20078 self.parentIface = descriptor.interface.parent
20079 if self.parentIface:
20080 self.parentDesc = descriptor.getDescriptor(self.parentIface.identifier.name)
20081 bases = [ClassBase(self.nativeLeafName(self.parentDesc))]
20082 else:
20083 bases = [
20084 ClassBase(
20085 "nsISupports /* or NonRefcountedDOMObject if this is a non-refcounted object */"
20088 if descriptor.wrapperCache:
20089 bases.append(
20090 ClassBase(
20091 "nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */"
20095 destructorVisibility = "protected"
20096 if self.parentIface:
20097 extradeclarations = (
20098 "public:\n"
20099 " NS_DECL_ISUPPORTS_INHERITED\n"
20100 " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n"
20101 "\n"
20103 self.nativeLeafName(descriptor),
20104 self.nativeLeafName(self.parentDesc),
20107 else:
20108 extradeclarations = (
20109 "public:\n"
20110 " NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
20111 " NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(%s)\n"
20112 "\n" % self.nativeLeafName(descriptor)
20115 if descriptor.interface.hasChildInterfaces():
20116 decorators = ""
20117 else:
20118 decorators = "final"
20120 for m in descriptor.interface.members:
20121 if m.isAttr() and m.type.isObservableArray():
20122 self.methodDecls.append(
20123 CGExampleObservableArrayCallback(descriptor, m, "Set")
20125 self.methodDecls.append(
20126 CGExampleObservableArrayCallback(descriptor, m, "Delete")
20129 CGClass.__init__(
20130 self,
20131 self.nativeLeafName(descriptor),
20132 bases=bases,
20133 constructors=[ClassConstructor([], visibility="public")],
20134 destructor=ClassDestructor(visibility=destructorVisibility),
20135 methods=self.methodDecls,
20136 decorators=decorators,
20137 extradeclarations=extradeclarations,
20140 def define(self):
20141 # Just override CGClass and do our own thing
20142 nativeType = self.nativeLeafName(self.descriptor)
20144 ctordtor = fill(
20146 ${nativeType}::${nativeType}()
20148 // Add |MOZ_COUNT_CTOR(${nativeType});| for a non-refcounted object.
20151 ${nativeType}::~${nativeType}()
20153 // Add |MOZ_COUNT_DTOR(${nativeType});| for a non-refcounted object.
20155 """,
20156 nativeType=nativeType,
20159 if self.parentIface:
20160 ccImpl = fill(
20163 // Only needed for refcounted objects.
20164 # error "If you don't have members that need cycle collection,
20165 # then remove all the cycle collection bits from this
20166 # implementation and the corresponding header. If you do, you
20167 # want NS_IMPL_CYCLE_COLLECTION_INHERITED(${nativeType},
20168 # ${parentType}, your, members, here)"
20169 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
20170 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
20171 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
20172 NS_INTERFACE_MAP_END_INHERITING(${parentType})
20174 """,
20175 nativeType=nativeType,
20176 parentType=self.nativeLeafName(self.parentDesc),
20178 else:
20179 ccImpl = fill(
20182 // Only needed for refcounted objects.
20183 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType})
20184 NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType})
20185 NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType})
20186 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
20187 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
20188 NS_INTERFACE_MAP_ENTRY(nsISupports)
20189 NS_INTERFACE_MAP_END
20191 """,
20192 nativeType=nativeType,
20195 classImpl = ccImpl + ctordtor + "\n"
20196 if self.descriptor.concrete:
20197 if self.descriptor.wrapperCache:
20198 reflectorArg = ""
20199 reflectorPassArg = ""
20200 returnType = "JSObject*"
20201 else:
20202 reflectorArg = ", JS::MutableHandle<JSObject*> aReflector"
20203 reflectorPassArg = ", aReflector"
20204 returnType = "bool"
20205 classImpl += fill(
20207 ${returnType}
20208 ${nativeType}::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto${reflectorArg})
20210 return ${ifaceName}_Binding::Wrap(aCx, this, aGivenProto${reflectorPassArg});
20213 """,
20214 returnType=returnType,
20215 nativeType=nativeType,
20216 reflectorArg=reflectorArg,
20217 ifaceName=self.descriptor.name,
20218 reflectorPassArg=reflectorPassArg,
20221 return classImpl
20223 @staticmethod
20224 def nativeLeafName(descriptor):
20225 return descriptor.nativeType.split("::")[-1]
20228 class CGExampleRoot(CGThing):
20230 Root codegen class for example implementation generation. Instantiate the
20231 class and call declare or define to generate header or cpp code,
20232 respectively.
20235 def __init__(self, config, interfaceName):
20236 descriptor = config.getDescriptor(interfaceName)
20238 self.root = CGWrapper(CGExampleClass(descriptor), pre="\n", post="\n")
20240 self.root = CGNamespace.build(["mozilla", "dom"], self.root)
20242 builder = ForwardDeclarationBuilder()
20243 if descriptor.hasCEReactions():
20244 builder.addInMozillaDom("DocGroup")
20245 for member in descriptor.interface.members:
20246 if not member.isAttr() and not member.isMethod():
20247 continue
20248 if member.isStatic():
20249 builder.addInMozillaDom("GlobalObject")
20250 if member.isAttr():
20251 if not member.isMaplikeOrSetlikeAttr():
20252 builder.forwardDeclareForType(member.type, config)
20253 else:
20254 assert member.isMethod()
20255 if not member.isMaplikeOrSetlikeOrIterableMethod():
20256 for sig in member.signatures():
20257 builder.forwardDeclareForType(sig[0], config)
20258 for arg in sig[1]:
20259 builder.forwardDeclareForType(arg.type, config)
20261 self.root = CGList([builder.build(), self.root], "\n")
20263 # Throw in our #includes
20264 self.root = CGHeaders(
20270 "nsWrapperCache.h",
20271 "nsCycleCollectionParticipant.h",
20272 "mozilla/Attributes.h",
20273 "mozilla/ErrorResult.h",
20274 "mozilla/dom/BindingDeclarations.h",
20275 "js/TypeDecls.h",
20278 "mozilla/dom/%s.h" % interfaceName,
20280 "mozilla/dom/%s"
20281 % CGHeaders.getDeclarationFilename(descriptor.interface)
20285 self.root,
20288 # And now some include guards
20289 self.root = CGIncludeGuard(interfaceName, self.root)
20291 # And our license block comes before everything else
20292 self.root = CGWrapper(
20293 self.root,
20294 pre=dedent(
20296 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
20297 /* vim:set ts=2 sw=2 sts=2 et cindent: */
20298 /* This Source Code Form is subject to the terms of the Mozilla Public
20299 * License, v. 2.0. If a copy of the MPL was not distributed with this
20300 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
20306 def declare(self):
20307 return self.root.declare()
20309 def define(self):
20310 return self.root.define()
20313 def jsImplName(name):
20314 return name + "JSImpl"
20317 class CGJSImplMember(CGNativeMember):
20319 Base class for generating code for the members of the implementation class
20320 for a JS-implemented WebIDL interface.
20323 def __init__(
20324 self,
20325 descriptorProvider,
20326 member,
20327 name,
20328 signature,
20329 extendedAttrs,
20330 breakAfter=True,
20331 passJSBitsAsNeeded=True,
20332 visibility="public",
20333 variadicIsSequence=False,
20334 virtual=False,
20335 override=False,
20337 CGNativeMember.__init__(
20338 self,
20339 descriptorProvider,
20340 member,
20341 name,
20342 signature,
20343 extendedAttrs,
20344 breakAfter=breakAfter,
20345 passJSBitsAsNeeded=passJSBitsAsNeeded,
20346 visibility=visibility,
20347 variadicIsSequence=variadicIsSequence,
20348 virtual=virtual,
20349 override=override,
20351 self.body = self.getImpl()
20353 def getArgs(self, returnType, argList):
20354 args = CGNativeMember.getArgs(self, returnType, argList)
20355 args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
20356 return args
20359 class CGJSImplMethod(CGJSImplMember):
20361 Class for generating code for the methods for a JS-implemented WebIDL
20362 interface.
20365 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
20366 self.signature = signature
20367 self.descriptor = descriptor
20368 self.isConstructor = isConstructor
20369 CGJSImplMember.__init__(
20370 self,
20371 descriptor,
20372 method,
20373 CGSpecializedMethod.makeNativeName(descriptor, method),
20374 signature,
20375 descriptor.getExtendedAttributes(method),
20376 breakAfter=breakAfter,
20377 variadicIsSequence=True,
20378 passJSBitsAsNeeded=False,
20381 def getArgs(self, returnType, argList):
20382 if self.isConstructor:
20383 # Skip the JS::Compartment bits for constructors; it's handled
20384 # manually in getImpl. But we do need our aGivenProto argument. We
20385 # allow it to be omitted if the default proto is desired.
20386 return CGNativeMember.getArgs(self, returnType, argList) + [
20387 Argument("JS::Handle<JSObject*>", "aGivenProto", "nullptr")
20389 return CGJSImplMember.getArgs(self, returnType, argList)
20391 def getImpl(self):
20392 args = self.getArgs(self.signature[0], self.signature[1])
20393 if not self.isConstructor:
20394 return "return mImpl->%s(%s);\n" % (
20395 self.name,
20396 ", ".join(arg.name for arg in args),
20399 assert self.descriptor.interface.isJSImplemented()
20400 if self.name != "Constructor":
20401 raise TypeError(
20402 "Legacy factory functions are not supported for JS implemented WebIDL."
20404 if len(self.signature[1]) != 0:
20405 # The first two arguments to the constructor implementation are not
20406 # arguments to the WebIDL constructor, so don't pass them to
20407 # __Init(). The last argument is the prototype we're supposed to
20408 # use, and shouldn't get passed to __Init() either.
20409 assert args[0].argType == "const GlobalObject&"
20410 assert args[1].argType == "JSContext*"
20411 assert args[-1].argType == "JS::Handle<JSObject*>"
20412 assert args[-1].name == "aGivenProto"
20413 constructorArgs = [arg.name for arg in args[2:-1]]
20414 constructorArgs.append("js::GetNonCCWObjectRealm(scopeObj)")
20415 initCall = fill(
20417 // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
20418 JS::Rooted<JSObject*> scopeObj(cx, global.Get());
20419 MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx));
20420 JS::Rooted<JS::Value> wrappedVal(cx);
20421 if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal, aGivenProto)) {
20422 MOZ_ASSERT(JS_IsExceptionPending(cx));
20423 aRv.Throw(NS_ERROR_UNEXPECTED);
20424 return nullptr;
20426 // Initialize the object with the constructor arguments.
20427 impl->mImpl->__Init(${args});
20428 if (aRv.Failed()) {
20429 return nullptr;
20431 """,
20432 args=", ".join(constructorArgs),
20434 else:
20435 initCall = ""
20436 return fill(
20438 RefPtr<${implClass}> impl =
20439 ConstructJSImplementation<${implClass}>("${contractId}", global, aRv);
20440 if (aRv.Failed()) {
20441 return nullptr;
20443 $*{initCall}
20444 return impl.forget();
20445 """,
20446 contractId=self.descriptor.interface.getJSImplementation(),
20447 implClass=self.descriptor.name,
20448 initCall=initCall,
20452 # We're always fallible
20453 def callbackGetterName(attr, descriptor):
20454 return "Get" + MakeNativeName(
20455 descriptor.binaryNameFor(attr.identifier.name, attr.isStatic())
20459 def callbackSetterName(attr, descriptor):
20460 return "Set" + MakeNativeName(
20461 descriptor.binaryNameFor(attr.identifier.name, attr.isStatic())
20465 class CGJSImplGetter(CGJSImplMember):
20467 Class for generating code for the getters of attributes for a JS-implemented
20468 WebIDL interface.
20471 def __init__(self, descriptor, attr):
20472 CGJSImplMember.__init__(
20473 self,
20474 descriptor,
20475 attr,
20476 CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
20477 (attr.type, []),
20478 descriptor.getExtendedAttributes(attr, getter=True),
20479 passJSBitsAsNeeded=False,
20482 def getImpl(self):
20483 callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])]
20484 return "return mImpl->%s(%s);\n" % (
20485 callbackGetterName(self.member, self.descriptorProvider),
20486 ", ".join(callbackArgs),
20490 class CGJSImplSetter(CGJSImplMember):
20492 Class for generating code for the setters of attributes for a JS-implemented
20493 WebIDL interface.
20496 def __init__(self, descriptor, attr):
20497 CGJSImplMember.__init__(
20498 self,
20499 descriptor,
20500 attr,
20501 CGSpecializedSetterCommon.makeNativeName(descriptor, attr),
20503 BuiltinTypes[IDLBuiltinType.Types.undefined],
20504 [FakeArgument(attr.type)],
20506 descriptor.getExtendedAttributes(attr, setter=True),
20507 passJSBitsAsNeeded=False,
20510 def getImpl(self):
20511 callbackArgs = [
20512 arg.name
20513 for arg in self.getArgs(
20514 BuiltinTypes[IDLBuiltinType.Types.undefined],
20515 [FakeArgument(self.member.type)],
20518 return "mImpl->%s(%s);\n" % (
20519 callbackSetterName(self.member, self.descriptorProvider),
20520 ", ".join(callbackArgs),
20524 class CGJSImplClass(CGBindingImplClass):
20525 def __init__(self, descriptor):
20526 CGBindingImplClass.__init__(
20527 self,
20528 descriptor,
20529 CGJSImplMethod,
20530 CGJSImplGetter,
20531 CGJSImplSetter,
20532 skipStaticMethods=True,
20535 if descriptor.interface.parent:
20536 parentClass = descriptor.getDescriptor(
20537 descriptor.interface.parent.identifier.name
20538 ).jsImplParent
20539 baseClasses = [ClassBase(parentClass)]
20540 isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n"
20541 ccDecl = "NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" % (
20542 descriptor.name,
20543 parentClass,
20545 extradefinitions = fill(
20547 NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent)
20548 NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass})
20549 NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass})
20550 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
20551 NS_INTERFACE_MAP_END_INHERITING(${parentClass})
20552 """,
20553 ifaceName=self.descriptor.name,
20554 parentClass=parentClass,
20556 else:
20557 baseClasses = [
20558 ClassBase("nsSupportsWeakReference"),
20559 ClassBase("nsWrapperCache"),
20561 isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
20562 ccDecl = (
20563 "NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(%s)\n" % descriptor.name
20565 extradefinitions = fill(
20567 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(${ifaceName})
20568 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(${ifaceName})
20569 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl)
20570 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
20571 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
20572 tmp->ClearWeakReferences();
20573 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
20574 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(${ifaceName})
20575 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl)
20576 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
20577 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
20578 NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName})
20579 NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName})
20580 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
20581 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
20582 NS_INTERFACE_MAP_ENTRY(nsISupports)
20583 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
20584 NS_INTERFACE_MAP_END
20585 """,
20586 ifaceName=self.descriptor.name,
20589 extradeclarations = fill(
20591 public:
20592 $*{isupportsDecl}
20593 $*{ccDecl}
20595 private:
20596 RefPtr<${jsImplName}> mImpl;
20597 nsCOMPtr<nsIGlobalObject> mParent;
20599 """,
20600 isupportsDecl=isupportsDecl,
20601 ccDecl=ccDecl,
20602 jsImplName=jsImplName(descriptor.name),
20605 if descriptor.interface.getExtendedAttribute("WantsEventListenerHooks"):
20606 # No need to do too much sanity checking here; the
20607 # generated code will fail to compile if the methods we
20608 # try to overrid aren't on a superclass.
20609 self.methodDecls.extend(
20610 self.getEventHookMethod(parentClass, "EventListenerAdded")
20612 self.methodDecls.extend(
20613 self.getEventHookMethod(parentClass, "EventListenerRemoved")
20616 if descriptor.interface.hasChildInterfaces():
20617 decorators = ""
20618 # We need a protected virtual destructor our subclasses can use
20619 destructor = ClassDestructor(virtual=True, visibility="protected")
20620 else:
20621 decorators = "final"
20622 destructor = ClassDestructor(virtual=False, visibility="private")
20624 baseConstructors = [
20626 "mImpl(new %s(nullptr, aJSImplObject, aJSImplGlobal, /* aIncumbentGlobal = */ nullptr))"
20627 % jsImplName(descriptor.name)
20629 "mParent(aParent)",
20631 parentInterface = descriptor.interface.parent
20632 while parentInterface:
20633 if parentInterface.isJSImplemented():
20634 baseConstructors.insert(
20635 0, "%s(aJSImplObject, aJSImplGlobal, aParent)" % parentClass
20637 break
20638 parentInterface = parentInterface.parent
20639 if not parentInterface and descriptor.interface.parent:
20640 # We only have C++ ancestors, so only pass along the window
20641 baseConstructors.insert(0, "%s(aParent)" % parentClass)
20643 constructor = ClassConstructor(
20645 Argument("JS::Handle<JSObject*>", "aJSImplObject"),
20646 Argument("JS::Handle<JSObject*>", "aJSImplGlobal"),
20647 Argument("nsIGlobalObject*", "aParent"),
20649 visibility="public",
20650 baseConstructors=baseConstructors,
20653 self.methodDecls.append(
20654 ClassMethod(
20655 "_Create",
20656 "bool",
20657 JSNativeArguments(),
20658 static=True,
20659 body=self.getCreateFromExistingBody(),
20663 CGClass.__init__(
20664 self,
20665 descriptor.name,
20666 bases=baseClasses,
20667 constructors=[constructor],
20668 destructor=destructor,
20669 methods=self.methodDecls,
20670 decorators=decorators,
20671 extradeclarations=extradeclarations,
20672 extradefinitions=extradefinitions,
20675 def getWrapObjectBody(self):
20676 return fill(
20678 JS::Rooted<JSObject*> obj(aCx, ${name}_Binding::Wrap(aCx, this, aGivenProto));
20679 if (!obj) {
20680 return nullptr;
20683 // Now define it on our chrome object
20684 JSAutoRealm ar(aCx, mImpl->CallbackGlobalOrNull());
20685 if (!JS_WrapObject(aCx, &obj)) {
20686 return nullptr;
20688 JS::Rooted<JSObject*> callback(aCx, mImpl->CallbackOrNull());
20689 if (!JS_DefineProperty(aCx, callback, "__DOM_IMPL__", obj, 0)) {
20690 return nullptr;
20692 return obj;
20693 """,
20694 name=self.descriptor.name,
20697 def getGetParentObjectReturnType(self):
20698 return "nsISupports*"
20700 def getGetParentObjectBody(self):
20701 return "return mParent;\n"
20703 def getGetDocGroupBody(self):
20704 return dedent(
20706 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent);
20707 if (!window) {
20708 return nullptr;
20710 return window->GetDocGroup();
20714 def getCreateFromExistingBody(self):
20715 # XXXbz we could try to get parts of this (e.g. the argument
20716 # conversions) auto-generated by somehow creating an IDLMethod and
20717 # adding it to our interface, but we'd still need to special-case the
20718 # implementation slightly to have it not try to forward to the JS
20719 # object...
20720 return fill(
20722 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
20723 if (!args.requireAtLeast(cx, "${ifaceName}._create", 2)) {
20724 return false;
20726 BindingCallContext callCx(cx, "${ifaceName}._create");
20727 if (!args[0].isObject()) {
20728 return callCx.ThrowErrorMessage<MSG_NOT_OBJECT>("Argument 1");
20730 if (!args[1].isObject()) {
20731 return callCx.ThrowErrorMessage<MSG_NOT_OBJECT>("Argument 2");
20734 // GlobalObject will go through wrappers as needed for us, and
20735 // is simpler than the right UnwrapArg incantation.
20736 GlobalObject global(cx, &args[0].toObject());
20737 if (global.Failed()) {
20738 return false;
20740 nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(global.GetAsSupports());
20741 MOZ_ASSERT(globalHolder);
20742 JS::Rooted<JSObject*> arg(cx, &args[1].toObject());
20743 JS::Rooted<JSObject*> argGlobal(cx, JS::CurrentGlobalOrNull(cx));
20744 RefPtr<${implName}> impl = new ${implName}(arg, argGlobal, globalHolder);
20745 MOZ_ASSERT(js::IsObjectInContextCompartment(arg, cx));
20746 return GetOrCreateDOMReflector(cx, impl, args.rval());
20747 """,
20748 ifaceName=self.descriptor.interface.identifier.name,
20749 implName=self.descriptor.name,
20752 def getEventHookMethod(self, parentClass, methodName):
20753 body = fill(
20755 ${parentClass}::${methodName}(aType);
20756 mImpl->${methodName}(Substring(nsDependentAtomString(aType), 2), IgnoreErrors());
20757 """,
20758 parentClass=parentClass,
20759 methodName=methodName,
20761 return [
20762 ClassMethod(
20763 methodName,
20764 "void",
20765 [Argument("nsAtom*", "aType")],
20766 virtual=True,
20767 override=True,
20768 body=body,
20770 ClassUsingFromBaseDeclaration(parentClass, methodName),
20774 def isJSImplementedDescriptor(descriptorProvider):
20775 return (
20776 isinstance(descriptorProvider, Descriptor)
20777 and descriptorProvider.interface.isJSImplemented()
20781 class CGCallback(CGClass):
20782 def __init__(
20783 self, idlObject, descriptorProvider, baseName, methods, getters=[], setters=[]
20785 self.baseName = baseName
20786 self._deps = idlObject.getDeps()
20787 self.idlObject = idlObject
20788 self.name = idlObject.identifier.name
20789 if isJSImplementedDescriptor(descriptorProvider):
20790 self.name = jsImplName(self.name)
20791 # For our public methods that needThisHandling we want most of the
20792 # same args and the same return type as what CallbackMember
20793 # generates. So we want to take advantage of all its
20794 # CGNativeMember infrastructure, but that infrastructure can't deal
20795 # with templates and most especially template arguments. So just
20796 # cheat and have CallbackMember compute all those things for us.
20797 realMethods = []
20798 for method in methods:
20799 if not isinstance(method, CallbackMember) or not method.needThisHandling:
20800 realMethods.append(method)
20801 else:
20802 realMethods.extend(self.getMethodImpls(method))
20803 realMethods.append(
20804 ClassMethod(
20805 "operator==",
20806 "bool",
20807 [Argument("const %s&" % self.name, "aOther")],
20808 inline=True,
20809 bodyInHeader=True,
20810 const=True,
20811 body=("return %s::operator==(aOther);\n" % baseName),
20814 CGClass.__init__(
20815 self,
20816 self.name,
20817 bases=[ClassBase(baseName)],
20818 constructors=self.getConstructors(),
20819 methods=realMethods + getters + setters,
20822 def getConstructors(self):
20823 if (
20824 not self.idlObject.isInterface()
20825 and not self.idlObject._treatNonObjectAsNull
20827 body = "MOZ_ASSERT(JS::IsCallable(mCallback));\n"
20828 else:
20829 # Not much we can assert about it, other than not being null, and
20830 # CallbackObject does that already.
20831 body = ""
20832 return [
20833 ClassConstructor(
20835 Argument("JSContext*", "aCx"),
20836 Argument("JS::Handle<JSObject*>", "aCallback"),
20837 Argument("JS::Handle<JSObject*>", "aCallbackGlobal"),
20838 Argument("nsIGlobalObject*", "aIncumbentGlobal"),
20840 bodyInHeader=True,
20841 visibility="public",
20842 explicit=True,
20843 baseConstructors=[
20844 "%s(aCx, aCallback, aCallbackGlobal, aIncumbentGlobal)"
20845 % self.baseName,
20847 body=body,
20849 ClassConstructor(
20851 Argument("JSObject*", "aCallback"),
20852 Argument("JSObject*", "aCallbackGlobal"),
20853 Argument("const FastCallbackConstructor&", ""),
20855 bodyInHeader=True,
20856 visibility="public",
20857 explicit=True,
20858 baseConstructors=[
20859 "%s(aCallback, aCallbackGlobal, FastCallbackConstructor())"
20860 % self.baseName,
20862 body=body,
20864 ClassConstructor(
20866 Argument("JSObject*", "aCallback"),
20867 Argument("JSObject*", "aCallbackGlobal"),
20868 Argument("JSObject*", "aAsyncStack"),
20869 Argument("nsIGlobalObject*", "aIncumbentGlobal"),
20871 bodyInHeader=True,
20872 visibility="public",
20873 explicit=True,
20874 baseConstructors=[
20875 "%s(aCallback, aCallbackGlobal, aAsyncStack, aIncumbentGlobal)"
20876 % self.baseName,
20878 body=body,
20882 def getMethodImpls(self, method):
20883 assert method.needThisHandling
20884 args = list(method.args)
20885 # Strip out the BindingCallContext&/JSObject* args
20886 # that got added.
20887 assert args[0].name == "cx" and args[0].argType == "BindingCallContext&"
20888 assert args[1].name == "aThisVal" and args[1].argType == "JS::Handle<JS::Value>"
20889 args = args[2:]
20891 # Now remember which index the ErrorResult argument is at;
20892 # we'll need this below.
20893 assert args[-1].name == "aRv" and args[-1].argType == "ErrorResult&"
20894 rvIndex = len(args) - 1
20895 assert rvIndex >= 0
20897 # Record the names of all the arguments, so we can use them when we call
20898 # the private method.
20899 argnames = [arg.name for arg in args]
20900 argnamesWithThis = ["s.GetCallContext()", "thisValJS"] + argnames
20901 argnamesWithoutThis = [
20902 "s.GetCallContext()",
20903 "JS::UndefinedHandleValue",
20904 ] + argnames
20905 # Now that we've recorded the argnames for our call to our private
20906 # method, insert our optional argument for the execution reason.
20907 args.append(Argument("const char*", "aExecutionReason", "nullptr"))
20909 # Make copies of the arg list for the two "without rv" overloads. Note
20910 # that those don't need aExceptionHandling or aRealm arguments because
20911 # those would make not sense anyway: the only sane thing to do with
20912 # exceptions in the "without rv" cases is to report them.
20913 argsWithoutRv = list(args)
20914 argsWithoutRv.pop(rvIndex)
20915 argsWithoutThisAndRv = list(argsWithoutRv)
20917 # Add the potional argument for deciding whether the CallSetup should
20918 # re-throw exceptions on aRv.
20919 args.append(
20920 Argument("ExceptionHandling", "aExceptionHandling", "eReportExceptions")
20922 # And the argument for communicating when exceptions should really be
20923 # rethrown. In particular, even when aExceptionHandling is
20924 # eRethrowExceptions they won't get rethrown if aRealm is provided
20925 # and its principal doesn't subsume either the callback or the
20926 # exception.
20927 args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
20928 # And now insert our template argument.
20929 argsWithoutThis = list(args)
20930 args.insert(0, Argument("const T&", "thisVal"))
20931 argsWithoutRv.insert(0, Argument("const T&", "thisVal"))
20933 argnamesWithoutThisAndRv = [arg.name for arg in argsWithoutThisAndRv]
20934 argnamesWithoutThisAndRv.insert(rvIndex, "IgnoreErrors()")
20935 # If we just leave things like that, and have no actual arguments in the
20936 # IDL, we will end up trying to call the templated "without rv" overload
20937 # with "rv" as the thisVal. That's no good. So explicitly append the
20938 # aExceptionHandling and aRealm values we need to end up matching the
20939 # signature of our non-templated "with rv" overload.
20940 argnamesWithoutThisAndRv.extend(["eReportExceptions", "nullptr"])
20942 argnamesWithoutRv = [arg.name for arg in argsWithoutRv]
20943 # Note that we need to insert at rvIndex + 1, since we inserted a
20944 # thisVal arg at the start.
20945 argnamesWithoutRv.insert(rvIndex + 1, "IgnoreErrors()")
20947 errorReturn = method.getDefaultRetval()
20949 setupCall = fill(
20951 MOZ_ASSERT(!aRv.Failed(), "Don't pass an already-failed ErrorResult to a callback!");
20952 if (!aExecutionReason) {
20953 aExecutionReason = "${executionReason}";
20955 CallSetup s(this, aRv, aExecutionReason, aExceptionHandling, aRealm);
20956 if (!s.GetContext()) {
20957 MOZ_ASSERT(aRv.Failed());
20958 return${errorReturn};
20960 """,
20961 errorReturn=errorReturn,
20962 executionReason=method.getPrettyName(),
20965 bodyWithThis = fill(
20967 $*{setupCall}
20968 JS::Rooted<JS::Value> thisValJS(s.GetContext());
20969 if (!ToJSValue(s.GetContext(), thisVal, &thisValJS)) {
20970 aRv.Throw(NS_ERROR_FAILURE);
20971 return${errorReturn};
20973 return ${methodName}(${callArgs});
20974 """,
20975 setupCall=setupCall,
20976 errorReturn=errorReturn,
20977 methodName=method.name,
20978 callArgs=", ".join(argnamesWithThis),
20980 bodyWithoutThis = fill(
20982 $*{setupCall}
20983 return ${methodName}(${callArgs});
20984 """,
20985 setupCall=setupCall,
20986 errorReturn=errorReturn,
20987 methodName=method.name,
20988 callArgs=", ".join(argnamesWithoutThis),
20990 bodyWithThisWithoutRv = fill(
20992 return ${methodName}(${callArgs});
20993 """,
20994 methodName=method.name,
20995 callArgs=", ".join(argnamesWithoutRv),
20997 bodyWithoutThisAndRv = fill(
20999 return ${methodName}(${callArgs});
21000 """,
21001 methodName=method.name,
21002 callArgs=", ".join(argnamesWithoutThisAndRv),
21005 return [
21006 ClassMethod(
21007 method.name,
21008 method.returnType,
21009 args,
21010 bodyInHeader=True,
21011 templateArgs=["typename T"],
21012 body=bodyWithThis,
21013 canRunScript=method.canRunScript,
21015 ClassMethod(
21016 method.name,
21017 method.returnType,
21018 argsWithoutThis,
21019 bodyInHeader=True,
21020 body=bodyWithoutThis,
21021 canRunScript=method.canRunScript,
21023 ClassMethod(
21024 method.name,
21025 method.returnType,
21026 argsWithoutRv,
21027 bodyInHeader=True,
21028 templateArgs=["typename T"],
21029 body=bodyWithThisWithoutRv,
21030 canRunScript=method.canRunScript,
21032 ClassMethod(
21033 method.name,
21034 method.returnType,
21035 argsWithoutThisAndRv,
21036 bodyInHeader=True,
21037 body=bodyWithoutThisAndRv,
21038 canRunScript=method.canRunScript,
21040 method,
21043 def deps(self):
21044 return self._deps
21047 class CGCallbackFunction(CGCallback):
21048 def __init__(self, callback, descriptorProvider):
21049 self.callback = callback
21050 if callback.isConstructor():
21051 methods = [ConstructCallback(callback, descriptorProvider)]
21052 else:
21053 methods = [CallCallback(callback, descriptorProvider)]
21054 CGCallback.__init__(
21055 self, callback, descriptorProvider, "CallbackFunction", methods
21058 def getConstructors(self):
21059 return CGCallback.getConstructors(self) + [
21060 ClassConstructor(
21061 [Argument("CallbackFunction*", "aOther")],
21062 bodyInHeader=True,
21063 visibility="public",
21064 explicit=True,
21065 baseConstructors=["CallbackFunction(aOther)"],
21070 class CGFastCallback(CGClass):
21071 def __init__(self, idlObject):
21072 self._deps = idlObject.getDeps()
21073 baseName = idlObject.identifier.name
21074 constructor = ClassConstructor(
21076 Argument("JSObject*", "aCallback"),
21077 Argument("JSObject*", "aCallbackGlobal"),
21079 bodyInHeader=True,
21080 visibility="public",
21081 explicit=True,
21082 baseConstructors=[
21083 "%s(aCallback, aCallbackGlobal, FastCallbackConstructor())" % baseName,
21085 body="",
21088 traceMethod = ClassMethod(
21089 "Trace",
21090 "void",
21091 [Argument("JSTracer*", "aTracer")],
21092 inline=True,
21093 bodyInHeader=True,
21094 visibility="public",
21095 body="%s::Trace(aTracer);\n" % baseName,
21097 holdMethod = ClassMethod(
21098 "FinishSlowJSInitIfMoreThanOneOwner",
21099 "void",
21100 [Argument("JSContext*", "aCx")],
21101 inline=True,
21102 bodyInHeader=True,
21103 visibility="public",
21104 body=("%s::FinishSlowJSInitIfMoreThanOneOwner(aCx);\n" % baseName),
21107 CGClass.__init__(
21108 self,
21109 "Fast%s" % baseName,
21110 bases=[ClassBase(baseName)],
21111 constructors=[constructor],
21112 methods=[traceMethod, holdMethod],
21115 def deps(self):
21116 return self._deps
21119 class CGCallbackInterface(CGCallback):
21120 def __init__(self, descriptor, spiderMonkeyInterfacesAreStructs=False):
21121 iface = descriptor.interface
21122 attrs = [
21124 for m in iface.members
21125 if (
21126 m.isAttr()
21127 and not m.isStatic()
21128 and (not m.isMaplikeOrSetlikeAttr() or not iface.isJSImplemented())
21131 getters = [
21132 CallbackGetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
21133 for a in attrs
21135 setters = [
21136 CallbackSetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
21137 for a in attrs
21138 if not a.readonly
21140 methods = [
21142 for m in iface.members
21143 if (
21144 m.isMethod()
21145 and not m.isStatic()
21146 and not m.isIdentifierLess()
21147 and (
21148 not m.isMaplikeOrSetlikeOrIterableMethod()
21149 or not iface.isJSImplemented()
21153 methods = [
21154 CallbackOperation(m, sig, descriptor, spiderMonkeyInterfacesAreStructs)
21155 for m in methods
21156 for sig in m.signatures()
21159 needInitId = False
21160 if iface.isJSImplemented() and iface.ctor():
21161 sigs = descriptor.interface.ctor().signatures()
21162 if len(sigs) != 1:
21163 raise TypeError("We only handle one constructor. See bug 869268.")
21164 methods.append(CGJSImplInitOperation(sigs[0], descriptor))
21165 needInitId = True
21167 idlist = [
21168 descriptor.binaryNameFor(m.identifier.name, m.isStatic())
21169 for m in iface.members
21170 if m.isAttr() or m.isMethod()
21172 if needInitId:
21173 idlist.append("__init")
21175 if iface.isJSImplemented() and iface.getExtendedAttribute(
21176 "WantsEventListenerHooks"
21178 methods.append(CGJSImplEventHookOperation(descriptor, "eventListenerAdded"))
21179 methods.append(
21180 CGJSImplEventHookOperation(descriptor, "eventListenerRemoved")
21182 idlist.append("eventListenerAdded")
21183 idlist.append("eventListenerRemoved")
21185 if len(idlist) != 0:
21186 methods.append(initIdsClassMethod(idlist, iface.identifier.name + "Atoms"))
21187 CGCallback.__init__(
21188 self,
21189 iface,
21190 descriptor,
21191 "CallbackInterface",
21192 methods,
21193 getters=getters,
21194 setters=setters,
21198 class FakeMember:
21199 def __init__(self, name=None):
21200 if name is not None:
21201 self.identifier = FakeIdentifier(name)
21203 def isStatic(self):
21204 return False
21206 def isAttr(self):
21207 return False
21209 def isMethod(self):
21210 return False
21212 def getExtendedAttribute(self, name):
21213 # Claim to be a [NewObject] so we can avoid the "return a raw pointer"
21214 # comments CGNativeMember codegen would otherwise stick in.
21215 if name == "NewObject":
21216 return True
21217 return None
21220 class CallbackMember(CGNativeMember):
21221 # XXXbz It's OK to use CallbackKnownNotGray for wrapScope because
21222 # CallSetup already handled the unmark-gray bits for us. we don't have
21223 # anything better to use for 'obj', really...
21224 def __init__(
21225 self,
21226 sig,
21227 name,
21228 descriptorProvider,
21229 needThisHandling,
21230 rethrowContentException=False,
21231 spiderMonkeyInterfacesAreStructs=False,
21232 wrapScope=None,
21233 canRunScript=False,
21234 passJSBitsAsNeeded=False,
21237 needThisHandling is True if we need to be able to accept a specified
21238 thisObj, False otherwise.
21240 assert not rethrowContentException or not needThisHandling
21242 self.retvalType = sig[0]
21243 self.originalSig = sig
21244 args = sig[1]
21245 self.argCount = len(args)
21246 if self.argCount > 0:
21247 # Check for variadic arguments
21248 lastArg = args[self.argCount - 1]
21249 if lastArg.variadic:
21250 self.argCountStr = "(%d - 1) + %s.Length()" % (
21251 self.argCount,
21252 lastArg.identifier.name,
21254 else:
21255 self.argCountStr = "%d" % self.argCount
21256 self.needThisHandling = needThisHandling
21257 # If needThisHandling, we generate ourselves as private and the caller
21258 # will handle generating public versions that handle the "this" stuff.
21259 visibility = "private" if needThisHandling else "public"
21260 self.rethrowContentException = rethrowContentException
21262 self.wrapScope = wrapScope
21263 # We don't care, for callback codegen, whether our original member was
21264 # a method or attribute or whatnot. Just always pass FakeMember()
21265 # here.
21266 CGNativeMember.__init__(
21267 self,
21268 descriptorProvider,
21269 FakeMember(),
21270 name,
21271 (self.retvalType, args),
21272 extendedAttrs=["needsErrorResult"],
21273 passJSBitsAsNeeded=passJSBitsAsNeeded,
21274 visibility=visibility,
21275 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
21276 canRunScript=canRunScript,
21278 # We have to do all the generation of our body now, because
21279 # the caller relies on us throwing if we can't manage it.
21280 self.body = self.getImpl()
21282 def getImpl(self):
21283 setupCall = self.getCallSetup()
21284 declRval = self.getRvalDecl()
21285 if self.argCount > 0:
21286 argvDecl = fill(
21288 JS::RootedVector<JS::Value> argv(cx);
21289 if (!argv.resize(${argCount})) {
21290 $*{failureCode}
21291 return${errorReturn};
21293 """,
21294 argCount=self.argCountStr,
21295 failureCode=self.getArgvDeclFailureCode(),
21296 errorReturn=self.getDefaultRetval(),
21298 else:
21299 # Avoid weird 0-sized arrays
21300 argvDecl = ""
21301 convertArgs = self.getArgConversions()
21302 doCall = self.getCall()
21303 returnResult = self.getResultConversion()
21305 body = declRval + argvDecl + convertArgs + doCall
21306 if self.needsScopeBody():
21307 body = "{\n" + indent(body) + "}\n"
21308 return setupCall + body + returnResult
21310 def needsScopeBody(self):
21311 return False
21313 def getArgvDeclFailureCode(self):
21314 return dedent(
21316 // That threw an exception on the JSContext, and our CallSetup will do
21317 // the right thing with that.
21321 def getExceptionCode(self, forResult):
21322 return fill(
21324 aRv.Throw(NS_ERROR_UNEXPECTED);
21325 return${defaultRetval};
21326 """,
21327 defaultRetval=self.getDefaultRetval(),
21330 def getResultConversion(
21331 self, val="rval", failureCode=None, isDefinitelyObject=False, exceptionCode=None
21333 replacements = {
21334 "val": val,
21335 "holderName": "rvalHolder",
21336 "declName": "rvalDecl",
21337 # We actually want to pass in a null scope object here, because
21338 # wrapping things into our current compartment (that of mCallback)
21339 # is what we want.
21340 "obj": "nullptr",
21341 "passedToJSImpl": "false",
21344 if isJSImplementedDescriptor(self.descriptorProvider):
21345 isCallbackReturnValue = "JSImpl"
21346 else:
21347 isCallbackReturnValue = "Callback"
21348 sourceDescription = "return value of %s" % self.getPrettyName()
21349 convertType = instantiateJSToNativeConversion(
21350 getJSToNativeConversionInfo(
21351 self.retvalType,
21352 self.descriptorProvider,
21353 failureCode=failureCode,
21354 isDefinitelyObject=isDefinitelyObject,
21355 exceptionCode=exceptionCode or self.getExceptionCode(forResult=True),
21356 isCallbackReturnValue=isCallbackReturnValue,
21357 # Allow returning a callback type that
21358 # allows non-callable objects.
21359 allowTreatNonCallableAsNull=True,
21360 sourceDescription=sourceDescription,
21362 replacements,
21364 assignRetval = string.Template(
21365 self.getRetvalInfo(self.retvalType, False)[2]
21366 ).substitute(replacements)
21367 type = convertType.define()
21368 return type + assignRetval
21370 def getArgConversions(self):
21371 # Just reget the arglist from self.originalSig, because our superclasses
21372 # just have way to many members they like to clobber, so I can't find a
21373 # safe member name to store it in.
21374 argConversions = [
21375 self.getArgConversion(i, arg) for i, arg in enumerate(self.originalSig[1])
21377 if not argConversions:
21378 return "\n"
21380 # Do them back to front, so our argc modifications will work
21381 # correctly, because we examine trailing arguments first.
21382 argConversions.reverse()
21383 # Wrap each one in a scope so that any locals it has don't leak out, and
21384 # also so that we can just "break;" for our successCode.
21385 argConversions = [
21386 CGWrapper(CGIndenter(CGGeneric(c)), pre="do {\n", post="} while (false);\n")
21387 for c in argConversions
21389 if self.argCount > 0:
21390 argConversions.insert(0, self.getArgcDecl())
21391 # And slap them together.
21392 return CGList(argConversions, "\n").define() + "\n"
21394 def getArgConversion(self, i, arg):
21395 argval = arg.identifier.name
21397 if arg.variadic:
21398 argval = argval + "[idx]"
21399 jsvalIndex = "%d + idx" % i
21400 else:
21401 jsvalIndex = "%d" % i
21402 if arg.canHaveMissingValue():
21403 argval += ".Value()"
21405 prepend = ""
21407 wrapScope = self.wrapScope
21408 if arg.type.isUnion() and wrapScope is None:
21409 prepend += (
21410 "JS::Rooted<JSObject*> callbackObj(cx, CallbackKnownNotGray());\n"
21412 wrapScope = "callbackObj"
21414 conversion = prepend + wrapForType(
21415 arg.type,
21416 self.descriptorProvider,
21418 "result": argval,
21419 "successCode": "continue;\n" if arg.variadic else "break;\n",
21420 "jsvalRef": "argv[%s]" % jsvalIndex,
21421 "jsvalHandle": "argv[%s]" % jsvalIndex,
21422 "obj": wrapScope,
21423 "returnsNewObject": False,
21424 "exceptionCode": self.getExceptionCode(forResult=False),
21425 "spiderMonkeyInterfacesAreStructs": self.spiderMonkeyInterfacesAreStructs,
21429 if arg.variadic:
21430 conversion = fill(
21432 for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) {
21433 $*{conversion}
21435 break;
21436 """,
21437 arg=arg.identifier.name,
21438 conversion=conversion,
21440 elif arg.canHaveMissingValue():
21441 conversion = fill(
21443 if (${argName}.WasPassed()) {
21444 $*{conversion}
21445 } else if (argc == ${iPlus1}) {
21446 // This is our current trailing argument; reduce argc
21447 --argc;
21448 } else {
21449 argv[${i}].setUndefined();
21451 """,
21452 argName=arg.identifier.name,
21453 conversion=conversion,
21454 iPlus1=i + 1,
21455 i=i,
21457 return conversion
21459 def getDefaultRetval(self):
21460 default = self.getRetvalInfo(self.retvalType, False)[1]
21461 if len(default) != 0:
21462 default = " " + default
21463 return default
21465 def getArgs(self, returnType, argList):
21466 args = CGNativeMember.getArgs(self, returnType, argList)
21467 if not self.needThisHandling:
21468 # Since we don't need this handling, we're the actual method that
21469 # will be called, so we need an aRethrowExceptions argument.
21470 if not self.rethrowContentException:
21471 args.append(Argument("const char*", "aExecutionReason", "nullptr"))
21472 args.append(
21473 Argument(
21474 "ExceptionHandling", "aExceptionHandling", "eReportExceptions"
21477 args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
21478 return args
21479 # We want to allow the caller to pass in a "this" value, as
21480 # well as a BindingCallContext.
21481 return [
21482 Argument("BindingCallContext&", "cx"),
21483 Argument("JS::Handle<JS::Value>", "aThisVal"),
21484 ] + args
21486 def getCallSetup(self):
21487 if self.needThisHandling:
21488 # It's been done for us already
21489 return ""
21490 callSetup = "CallSetup s(this, aRv"
21491 if self.rethrowContentException:
21492 # getArgs doesn't add the aExceptionHandling argument but does add
21493 # aRealm for us.
21494 callSetup += (
21495 ', "%s", eRethrowContentExceptions, aRealm, /* aIsJSImplementedWebIDL = */ '
21496 % self.getPrettyName()
21498 callSetup += toStringBool(
21499 isJSImplementedDescriptor(self.descriptorProvider)
21501 else:
21502 callSetup += ', "%s", aExceptionHandling, aRealm' % self.getPrettyName()
21503 callSetup += ");\n"
21504 return fill(
21506 $*{callSetup}
21507 if (aRv.Failed()) {
21508 return${errorReturn};
21510 MOZ_ASSERT(s.GetContext());
21511 BindingCallContext& cx = s.GetCallContext();
21513 """,
21514 callSetup=callSetup,
21515 errorReturn=self.getDefaultRetval(),
21518 def getArgcDecl(self):
21519 return CGGeneric("unsigned argc = %s;\n" % self.argCountStr)
21521 @staticmethod
21522 def ensureASCIIName(idlObject):
21523 type = "attribute" if idlObject.isAttr() else "operation"
21524 if re.match("[^\x20-\x7E]", idlObject.identifier.name):
21525 raise SyntaxError(
21526 'Callback %s name "%s" contains non-ASCII '
21527 "characters. We can't handle that. %s"
21528 % (type, idlObject.identifier.name, idlObject.location)
21530 if re.match('"', idlObject.identifier.name):
21531 raise SyntaxError(
21532 "Callback %s name '%s' contains "
21533 "double-quote character. We can't handle "
21534 "that. %s" % (type, idlObject.identifier.name, idlObject.location)
21538 class ConstructCallback(CallbackMember):
21539 def __init__(self, callback, descriptorProvider):
21540 self.callback = callback
21541 CallbackMember.__init__(
21542 self,
21543 callback.signatures()[0],
21544 "Construct",
21545 descriptorProvider,
21546 needThisHandling=False,
21547 canRunScript=True,
21550 def getRvalDecl(self):
21551 # Box constructedObj for getJSToNativeConversionInfo().
21552 return "JS::Rooted<JS::Value> rval(cx);\n"
21554 def getCall(self):
21555 if self.argCount > 0:
21556 args = "JS::HandleValueArray::subarray(argv, 0, argc)"
21557 else:
21558 args = "JS::HandleValueArray::empty()"
21560 return fill(
21562 JS::Rooted<JS::Value> constructor(cx, JS::ObjectValue(*mCallback));
21563 JS::Rooted<JSObject*> constructedObj(cx);
21564 if (!JS::Construct(cx, constructor,
21565 ${args}, &constructedObj)) {
21566 aRv.NoteJSContextException(cx);
21567 return${errorReturn};
21569 rval.setObject(*constructedObj);
21570 """,
21571 args=args,
21572 errorReturn=self.getDefaultRetval(),
21575 def getResultConversion(self):
21576 return CallbackMember.getResultConversion(self, isDefinitelyObject=True)
21578 def getPrettyName(self):
21579 return self.callback.identifier.name
21582 class CallbackMethod(CallbackMember):
21583 def __init__(
21584 self,
21585 sig,
21586 name,
21587 descriptorProvider,
21588 needThisHandling,
21589 rethrowContentException=False,
21590 spiderMonkeyInterfacesAreStructs=False,
21591 canRunScript=False,
21593 CallbackMember.__init__(
21594 self,
21595 sig,
21596 name,
21597 descriptorProvider,
21598 needThisHandling,
21599 rethrowContentException,
21600 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
21601 canRunScript=canRunScript,
21604 def getRvalDecl(self):
21605 return "JS::Rooted<JS::Value> rval(cx);\n"
21607 def getNoteCallFailed(self):
21608 return fill(
21610 aRv.NoteJSContextException(cx);
21611 return${errorReturn};
21612 """,
21613 errorReturn=self.getDefaultRetval(),
21616 def getCall(self):
21617 if self.argCount > 0:
21618 args = "JS::HandleValueArray::subarray(argv, 0, argc)"
21619 else:
21620 args = "JS::HandleValueArray::empty()"
21622 return fill(
21624 $*{declCallable}
21625 $*{declThis}
21626 if (${callGuard}!JS::Call(cx, ${thisVal}, callable,
21627 ${args}, &rval)) {
21628 $*{noteError}
21630 """,
21631 declCallable=self.getCallableDecl(),
21632 declThis=self.getThisDecl(),
21633 callGuard=self.getCallGuard(),
21634 thisVal=self.getThisVal(),
21635 args=args,
21636 noteError=self.getNoteCallFailed(),
21640 class CallCallback(CallbackMethod):
21641 def __init__(self, callback, descriptorProvider):
21642 self.callback = callback
21643 CallbackMethod.__init__(
21644 self,
21645 callback.signatures()[0],
21646 "Call",
21647 descriptorProvider,
21648 needThisHandling=True,
21649 canRunScript=not callback.isRunScriptBoundary(),
21652 def getNoteCallFailed(self):
21653 if self.retvalType.isPromise():
21654 return dedent(
21656 // Convert exception to a rejected promise.
21657 // See https://heycam.github.io/webidl/#call-a-user-objects-operation
21658 // step 12 and step 15.5.
21659 return CreateRejectedPromiseFromThrownException(cx, aRv);
21662 return CallbackMethod.getNoteCallFailed(self)
21664 def getExceptionCode(self, forResult):
21665 # If the result value is a promise, and conversion
21666 # to the promise throws an exception we shouldn't
21667 # try to convert that exception to a promise again.
21668 if self.retvalType.isPromise() and not forResult:
21669 return dedent(
21671 // Convert exception to a rejected promise.
21672 // See https://heycam.github.io/webidl/#call-a-user-objects-operation
21673 // step 10 and step 15.5.
21674 return CreateRejectedPromiseFromThrownException(cx, aRv);
21677 return CallbackMethod.getExceptionCode(self, forResult)
21679 def getThisDecl(self):
21680 return ""
21682 def getThisVal(self):
21683 return "aThisVal"
21685 def getCallableDecl(self):
21686 return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n"
21688 def getPrettyName(self):
21689 return self.callback.identifier.name
21691 def getCallGuard(self):
21692 if self.callback._treatNonObjectAsNull:
21693 return "JS::IsCallable(mCallback) && "
21694 return ""
21697 class CallbackOperationBase(CallbackMethod):
21699 Common class for implementing various callback operations.
21702 def __init__(
21703 self,
21704 signature,
21705 jsName,
21706 nativeName,
21707 descriptor,
21708 singleOperation,
21709 rethrowContentException=False,
21710 spiderMonkeyInterfacesAreStructs=False,
21712 self.singleOperation = singleOperation
21713 self.methodName = descriptor.binaryNameFor(jsName, False)
21714 CallbackMethod.__init__(
21715 self,
21716 signature,
21717 nativeName,
21718 descriptor,
21719 singleOperation,
21720 rethrowContentException,
21721 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
21724 def getThisDecl(self):
21725 if not self.singleOperation:
21726 return "JS::Rooted<JS::Value> thisValue(cx, JS::ObjectValue(*mCallback));\n"
21727 # This relies on getCallableDecl declaring a boolean
21728 # isCallable in the case when we're a single-operation
21729 # interface.
21730 return dedent(
21732 JS::Rooted<JS::Value> thisValue(cx, isCallable ? aThisVal.get()
21733 : JS::ObjectValue(*mCallback));
21737 def getThisVal(self):
21738 return "thisValue"
21740 def getCallableDecl(self):
21741 getCallableFromProp = fill(
21743 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
21744 if ((reinterpret_cast<jsid*>(atomsCache)->isVoid() &&
21745 !InitIds(cx, atomsCache)) ||
21746 !GetCallableProperty(cx, atomsCache->${methodAtomName}, &callable)) {
21747 aRv.Throw(NS_ERROR_UNEXPECTED);
21748 return${errorReturn};
21750 """,
21751 methodAtomName=CGDictionary.makeIdName(self.methodName),
21752 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
21753 errorReturn=self.getDefaultRetval(),
21755 if not self.singleOperation:
21756 return "JS::Rooted<JS::Value> callable(cx);\n" + getCallableFromProp
21757 return fill(
21759 bool isCallable = JS::IsCallable(mCallback);
21760 JS::Rooted<JS::Value> callable(cx);
21761 if (isCallable) {
21762 callable = JS::ObjectValue(*mCallback);
21763 } else {
21764 $*{getCallableFromProp}
21766 """,
21767 getCallableFromProp=getCallableFromProp,
21770 def getCallGuard(self):
21771 return ""
21774 class CallbackOperation(CallbackOperationBase):
21776 Codegen actual WebIDL operations on callback interfaces.
21779 def __init__(self, method, signature, descriptor, spiderMonkeyInterfacesAreStructs):
21780 self.ensureASCIIName(method)
21781 self.method = method
21782 jsName = method.identifier.name
21783 CallbackOperationBase.__init__(
21784 self,
21785 signature,
21786 jsName,
21787 MakeNativeName(descriptor.binaryNameFor(jsName, False)),
21788 descriptor,
21789 descriptor.interface.isSingleOperationInterface(),
21790 rethrowContentException=descriptor.interface.isJSImplemented(),
21791 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
21794 def getPrettyName(self):
21795 return "%s.%s" % (
21796 self.descriptorProvider.interface.identifier.name,
21797 self.method.identifier.name,
21801 class CallbackAccessor(CallbackMember):
21803 Shared superclass for CallbackGetter and CallbackSetter.
21806 def __init__(self, attr, sig, name, descriptor, spiderMonkeyInterfacesAreStructs):
21807 self.ensureASCIIName(attr)
21808 self.attrName = attr.identifier.name
21809 CallbackMember.__init__(
21810 self,
21811 sig,
21812 name,
21813 descriptor,
21814 needThisHandling=False,
21815 rethrowContentException=descriptor.interface.isJSImplemented(),
21816 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
21819 def getPrettyName(self):
21820 return "%s.%s" % (
21821 self.descriptorProvider.interface.identifier.name,
21822 self.attrName,
21826 class CallbackGetter(CallbackAccessor):
21827 def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
21828 CallbackAccessor.__init__(
21829 self,
21830 attr,
21831 (attr.type, []),
21832 callbackGetterName(attr, descriptor),
21833 descriptor,
21834 spiderMonkeyInterfacesAreStructs,
21837 def getRvalDecl(self):
21838 return "JS::Rooted<JS::Value> rval(cx);\n"
21840 def getCall(self):
21841 return fill(
21843 JS::Rooted<JSObject *> callback(cx, mCallback);
21844 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
21845 if ((reinterpret_cast<jsid*>(atomsCache)->isVoid()
21846 && !InitIds(cx, atomsCache)) ||
21847 !JS_GetPropertyById(cx, callback, atomsCache->${attrAtomName}, &rval)) {
21848 aRv.Throw(NS_ERROR_UNEXPECTED);
21849 return${errorReturn};
21851 """,
21852 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
21853 attrAtomName=CGDictionary.makeIdName(
21854 self.descriptorProvider.binaryNameFor(self.attrName, False)
21856 errorReturn=self.getDefaultRetval(),
21860 class CallbackSetter(CallbackAccessor):
21861 def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
21862 CallbackAccessor.__init__(
21863 self,
21864 attr,
21866 BuiltinTypes[IDLBuiltinType.Types.undefined],
21867 [FakeArgument(attr.type)],
21869 callbackSetterName(attr, descriptor),
21870 descriptor,
21871 spiderMonkeyInterfacesAreStructs,
21874 def getRvalDecl(self):
21875 # We don't need an rval
21876 return ""
21878 def getCall(self):
21879 return fill(
21881 MOZ_ASSERT(argv.length() == 1);
21882 JS::Rooted<JSObject*> callback(cx, CallbackKnownNotGray());
21883 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
21884 if ((reinterpret_cast<jsid*>(atomsCache)->isVoid() &&
21885 !InitIds(cx, atomsCache)) ||
21886 !JS_SetPropertyById(cx, callback, atomsCache->${attrAtomName}, argv[0])) {
21887 aRv.Throw(NS_ERROR_UNEXPECTED);
21888 return${errorReturn};
21890 """,
21891 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
21892 attrAtomName=CGDictionary.makeIdName(
21893 self.descriptorProvider.binaryNameFor(self.attrName, False)
21895 errorReturn=self.getDefaultRetval(),
21898 def getArgcDecl(self):
21899 return None
21902 class CGJSImplInitOperation(CallbackOperationBase):
21904 Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL.
21907 def __init__(self, sig, descriptor):
21908 assert sig in descriptor.interface.ctor().signatures()
21909 CallbackOperationBase.__init__(
21910 self,
21911 (BuiltinTypes[IDLBuiltinType.Types.undefined], sig[1]),
21912 "__init",
21913 "__Init",
21914 descriptor,
21915 singleOperation=False,
21916 rethrowContentException=True,
21917 spiderMonkeyInterfacesAreStructs=True,
21920 def getPrettyName(self):
21921 return "__init"
21924 class CGJSImplEventHookOperation(CallbackOperationBase):
21926 Codegen the hooks on a JS impl for adding/removing event listeners.
21929 def __init__(self, descriptor, name):
21930 self.name = name
21932 CallbackOperationBase.__init__(
21933 self,
21935 BuiltinTypes[IDLBuiltinType.Types.undefined],
21936 [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.domstring], "aType")],
21938 name,
21939 MakeNativeName(name),
21940 descriptor,
21941 singleOperation=False,
21942 rethrowContentException=False,
21943 spiderMonkeyInterfacesAreStructs=True,
21946 def getPrettyName(self):
21947 return self.name
21950 def getMaplikeOrSetlikeErrorReturn(helperImpl):
21952 Generate return values based on whether a maplike or setlike generated
21953 method is an interface method (which returns bool) or a helper function
21954 (which uses ErrorResult).
21956 if helperImpl:
21957 return dedent(
21959 aRv.Throw(NS_ERROR_UNEXPECTED);
21960 return%s;
21962 % helperImpl.getDefaultRetval()
21964 return "return false;\n"
21967 def getMaplikeOrSetlikeBackingObject(descriptor, maplikeOrSetlike, helperImpl=None):
21969 Generate code to get/create a JS backing object for a maplike/setlike
21970 declaration from the declaration slot.
21972 func_prefix = maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title()
21973 ret = fill(
21975 JS::Rooted<JSObject*> backingObj(cx);
21976 bool created = false;
21977 if (!Get${func_prefix}BackingObject(cx, obj, ${slot}, &backingObj, &created)) {
21978 $*{errorReturn}
21980 if (created) {
21981 PreserveWrapper<${selfType}>(self);
21983 """,
21984 slot=memberReservedSlot(maplikeOrSetlike, descriptor),
21985 func_prefix=func_prefix,
21986 errorReturn=getMaplikeOrSetlikeErrorReturn(helperImpl),
21987 selfType=descriptor.nativeType,
21989 return ret
21992 def getMaplikeOrSetlikeSizeGetterBody(descriptor, attr):
21994 Creates the body for the size getter method of maplike/setlike interfaces.
21996 # We should only have one declaration attribute currently
21997 assert attr.identifier.name == "size"
21998 assert attr.isMaplikeOrSetlikeAttr()
21999 return fill(
22001 $*{getBackingObj}
22002 uint32_t result = JS::${funcPrefix}Size(cx, backingObj);
22003 MOZ_ASSERT(!JS_IsExceptionPending(cx));
22004 args.rval().setNumber(result);
22005 return true;
22006 """,
22007 getBackingObj=getMaplikeOrSetlikeBackingObject(
22008 descriptor, attr.maplikeOrSetlike
22010 funcPrefix=attr.maplikeOrSetlike.prefix,
22014 class CGMaplikeOrSetlikeMethodGenerator(CGThing):
22016 Creates methods for maplike/setlike interfaces. It is expected that all
22017 methods will be have a maplike/setlike object attached. Unwrapping/wrapping
22018 will be taken care of by the usual method generation machinery in
22019 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
22020 using CGCallGenerator.
22023 def __init__(
22024 self,
22025 descriptor,
22026 maplikeOrSetlike,
22027 methodName,
22028 needsValueTypeReturn=False,
22029 helperImpl=None,
22031 CGThing.__init__(self)
22032 # True if this will be the body of a C++ helper function.
22033 self.helperImpl = helperImpl
22034 self.descriptor = descriptor
22035 self.maplikeOrSetlike = maplikeOrSetlike
22036 self.cgRoot = CGList([])
22037 impl_method_name = methodName
22038 if impl_method_name[0] == "_":
22039 # double underscore means this is a js-implemented chrome only rw
22040 # function. Truncate the double underscore so calling the right
22041 # underlying JSAPI function still works.
22042 impl_method_name = impl_method_name[2:]
22043 self.cgRoot.append(
22044 CGGeneric(
22045 getMaplikeOrSetlikeBackingObject(
22046 self.descriptor, self.maplikeOrSetlike, self.helperImpl
22050 self.returnStmt = getMaplikeOrSetlikeErrorReturn(self.helperImpl)
22052 # Generates required code for the method. Method descriptions included
22053 # in definitions below. Throw if we don't have a method to fill in what
22054 # we're looking for.
22055 try:
22056 methodGenerator = getattr(self, impl_method_name)
22057 except AttributeError:
22058 raise TypeError(
22059 "Missing %s method definition '%s'"
22060 % (self.maplikeOrSetlike.maplikeOrSetlikeType, methodName)
22062 # Method generator returns tuple, containing:
22064 # - a list of CGThings representing setup code for preparing to call
22065 # the JS API function
22066 # - a list of arguments needed for the JS API function we're calling
22067 # - list of code CGThings needed for return value conversion.
22068 (setupCode, arguments, setResult) = methodGenerator()
22070 # Create the actual method call, and then wrap it with the code to
22071 # return the value if needed.
22072 funcName = self.maplikeOrSetlike.prefix + MakeNativeName(impl_method_name)
22073 # Append the list of setup code CGThings
22074 self.cgRoot.append(CGList(setupCode))
22075 # Create the JS API call
22076 code = dedent(
22078 if (!JS::${funcName}(${args})) {
22079 $*{errorReturn}
22084 if needsValueTypeReturn:
22085 assert self.helperImpl and impl_method_name == "get"
22086 code += fill(
22088 if (result.isUndefined()) {
22089 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
22090 return${retval};
22092 """,
22093 retval=self.helperImpl.getDefaultRetval(),
22096 self.cgRoot.append(
22097 CGWrapper(
22098 CGGeneric(
22099 fill(
22100 code,
22101 funcName=funcName,
22102 args=", ".join(["cx", "backingObj"] + arguments),
22103 errorReturn=self.returnStmt,
22108 # Append result conversion
22109 self.cgRoot.append(CGList(setResult))
22111 def mergeTuples(self, a, b):
22113 Expecting to take 2 tuples were all elements are lists, append the lists in
22114 the second tuple to the lists in the first.
22116 return tuple([x + y for x, y in zip(a, b)])
22118 def appendArgConversion(self, name):
22120 Generate code to convert arguments to JS::Values, so they can be
22121 passed into JSAPI functions.
22123 return CGGeneric(
22124 fill(
22126 JS::Rooted<JS::Value> ${name}Val(cx);
22127 if (!ToJSValue(cx, ${name}, &${name}Val)) {
22128 $*{errorReturn}
22130 """,
22131 name=name,
22132 errorReturn=self.returnStmt,
22136 def appendKeyArgConversion(self):
22138 Generates the key argument for methods. Helper functions will use
22139 a RootedVector<JS::Value>, while interface methods have separate JS::Values.
22141 if self.helperImpl:
22142 return ([], ["argv[0]"], [])
22143 return ([self.appendArgConversion("arg0")], ["arg0Val"], [])
22145 def appendKeyAndValueArgConversion(self):
22147 Generates arguments for methods that require a key and value. Helper
22148 functions will use a RootedVector<JS::Value>, while interface methods have
22149 separate JS::Values.
22151 r = self.appendKeyArgConversion()
22152 if self.helperImpl:
22153 return self.mergeTuples(r, ([], ["argv[1]"], []))
22154 return self.mergeTuples(
22155 r, ([self.appendArgConversion("arg1")], ["arg1Val"], [])
22158 def appendIteratorResult(self):
22160 Generate code to output JSObject* return values, needed for functions that
22161 return iterators. Iterators cannot currently be wrapped via Xrays. If
22162 something that would return an iterator is called via Xray, fail early.
22164 # TODO: Bug 1173651 - Remove check once bug 1023984 is fixed.
22165 code = CGGeneric(
22166 dedent(
22168 // TODO (Bug 1173651): Xrays currently cannot wrap iterators. Change
22169 // after bug 1023984 is fixed.
22170 if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
22171 JS_ReportErrorASCII(cx, "Xray wrapping of iterators not supported.");
22172 return false;
22174 JS::Rooted<JSObject*> result(cx);
22175 JS::Rooted<JS::Value> v(cx);
22179 arguments = "&v"
22180 setResult = CGGeneric(
22181 dedent(
22183 result = &v.toObject();
22187 return ([code], [arguments], [setResult])
22189 def appendSelfResult(self):
22191 Generate code to return the interface object itself.
22193 code = CGGeneric(
22194 dedent(
22196 JS::Rooted<JSObject*> result(cx);
22200 setResult = CGGeneric(
22201 dedent(
22203 result = obj;
22207 return ([code], [], [setResult])
22209 def appendBoolResult(self):
22210 if self.helperImpl:
22211 return ([CGGeneric("bool retVal;\n")], ["&retVal"], [])
22212 return ([CGGeneric("bool result;\n")], ["&result"], [])
22214 def forEach(self):
22216 void forEach(callback c, any thisval);
22218 ForEach takes a callback, and a possible value to use as 'this'. The
22219 callback needs to take value, key, and the interface object
22220 implementing maplike/setlike. In order to make sure that the third arg
22221 is our interface object instead of the map/set backing object, we
22222 create a js function with the callback and original object in its
22223 storage slots, then use a helper function in BindingUtils to make sure
22224 the callback is called correctly.
22226 assert not self.helperImpl
22227 code = [
22228 CGGeneric(
22229 dedent(
22231 // Create a wrapper function.
22232 JSFunction* func = js::NewFunctionWithReserved(cx, ForEachHandler, 3, 0, nullptr);
22233 if (!func) {
22234 return false;
22236 JS::Rooted<JSObject*> funcObj(cx, JS_GetFunctionObject(func));
22237 JS::Rooted<JS::Value> funcVal(cx, JS::ObjectValue(*funcObj));
22238 js::SetFunctionNativeReserved(funcObj, FOREACH_CALLBACK_SLOT,
22239 JS::ObjectValue(*arg0));
22240 js::SetFunctionNativeReserved(funcObj, FOREACH_MAPLIKEORSETLIKEOBJ_SLOT,
22241 JS::ObjectValue(*obj));
22246 arguments = ["funcVal", "arg1"]
22247 return (code, arguments, [])
22249 def set(self):
22251 object set(key, value);
22253 Maplike only function, takes key and sets value to it, returns
22254 interface object unless being called from a C++ helper.
22256 assert self.maplikeOrSetlike.isMaplike()
22257 r = self.appendKeyAndValueArgConversion()
22258 if self.helperImpl:
22259 return r
22260 return self.mergeTuples(r, self.appendSelfResult())
22262 def add(self):
22264 object add(value);
22266 Setlike only function, adds value to set, returns interface object
22267 unless being called from a C++ helper
22269 assert self.maplikeOrSetlike.isSetlike()
22270 r = self.appendKeyArgConversion()
22271 if self.helperImpl:
22272 return r
22273 return self.mergeTuples(r, self.appendSelfResult())
22275 def get(self):
22277 type? get(key);
22279 Retrieves a value from a backing object based on the key. Returns value
22280 if key is in backing object, undefined otherwise.
22282 assert self.maplikeOrSetlike.isMaplike()
22283 r = self.appendKeyArgConversion()
22285 code = []
22286 # We don't need to create the result variable because it'll be created elsewhere
22287 # for JSObject Get method
22288 if not self.helperImpl or not self.helperImpl.needsScopeBody():
22289 code = [
22290 CGGeneric(
22291 dedent(
22293 JS::Rooted<JS::Value> result(cx);
22299 arguments = ["&result"]
22300 return self.mergeTuples(r, (code, arguments, []))
22302 def has(self):
22304 bool has(key);
22306 Check if an entry exists in the backing object. Returns true if value
22307 exists in backing object, false otherwise.
22309 return self.mergeTuples(self.appendKeyArgConversion(), self.appendBoolResult())
22311 def keys(self):
22313 object keys();
22315 Returns new object iterator with all keys from backing object.
22317 return self.appendIteratorResult()
22319 def values(self):
22321 object values();
22323 Returns new object iterator with all values from backing object.
22325 return self.appendIteratorResult()
22327 def entries(self):
22329 object entries();
22331 Returns new object iterator with all keys and values from backing
22332 object. Keys will be null for set.
22334 return self.appendIteratorResult()
22336 def clear(self):
22338 void clear();
22340 Removes all entries from map/set.
22342 return ([], [], [])
22344 def delete(self):
22346 bool delete(key);
22348 Deletes an entry from the backing object. Returns true if value existed
22349 in backing object, false otherwise.
22351 return self.mergeTuples(self.appendKeyArgConversion(), self.appendBoolResult())
22353 def define(self):
22354 return self.cgRoot.define()
22357 class CGHelperFunctionGenerator(CallbackMember):
22359 Generates code to allow C++ to perform operations. Gets a context from the
22360 binding wrapper, turns arguments into JS::Values (via
22361 CallbackMember/CGNativeMember argument conversion), then uses
22362 getCall to generate the body for getting result, and maybe convert the
22363 result into return type (via CallbackMember/CGNativeMember result
22364 conversion)
22367 class HelperFunction(CGAbstractMethod):
22369 Generates context retrieval code and rooted JSObject for interface for
22370 method generator to use
22373 def __init__(self, descriptor, name, args, code, returnType):
22374 self.code = code
22375 CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
22377 def definition_body(self):
22378 return self.code
22380 def __init__(
22381 self,
22382 descriptor,
22383 name,
22384 args,
22385 returnType=BuiltinTypes[IDLBuiltinType.Types.undefined],
22386 needsResultConversion=True,
22388 assert returnType.isType()
22389 self.needsResultConversion = needsResultConversion
22391 # Run CallbackMember init function to generate argument conversion code.
22392 # wrapScope is set to 'obj' when generating maplike or setlike helper
22393 # functions, as we don't have access to the CallbackPreserveColor
22394 # method.
22395 CallbackMember.__init__(
22396 self,
22397 [returnType, args],
22398 name,
22399 descriptor,
22400 False,
22401 wrapScope="obj",
22402 passJSBitsAsNeeded=typeNeedsCx(returnType),
22405 # Wrap CallbackMember body code into a CGAbstractMethod to make
22406 # generation easier.
22407 self.implMethod = CGHelperFunctionGenerator.HelperFunction(
22408 descriptor, name, self.args, self.body, self.returnType
22411 def getCallSetup(self):
22412 # If passJSBitsAsNeeded is true, it means the caller will provide a
22413 # JSContext, so we don't need to create JSContext and enter
22414 # UnprivilegedJunkScopeOrWorkerGlobal here.
22415 code = "MOZ_ASSERT(self);\n"
22416 if not self.passJSBitsAsNeeded:
22417 code += dedent(
22419 AutoJSAPI jsapi;
22420 jsapi.Init();
22421 JSContext* cx = jsapi.cx();
22422 // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here because
22423 // all we want is to wrap into _some_ scope and then unwrap to find
22424 // the reflector, and wrapping has no side-effects.
22425 JSObject* scope = UnprivilegedJunkScopeOrWorkerGlobal(fallible);
22426 if (!scope) {
22427 aRv.Throw(NS_ERROR_UNEXPECTED);
22428 return%s;
22430 JSAutoRealm tempRealm(cx, scope);
22432 % self.getDefaultRetval()
22435 code += dedent(
22437 JS::Rooted<JS::Value> v(cx);
22438 if(!ToJSValue(cx, self, &v)) {
22439 aRv.Throw(NS_ERROR_UNEXPECTED);
22440 return%s;
22442 // This is a reflector, but due to trying to name things
22443 // similarly across method generators, it's called obj here.
22444 JS::Rooted<JSObject*> obj(cx);
22445 obj = js::UncheckedUnwrap(&v.toObject(), /* stopAtWindowProxy = */ false);
22447 % self.getDefaultRetval()
22450 # We'd like wrap the inner code in a scope such that the code can use the
22451 # same realm. So here we are creating the result variable outside of the
22452 # scope.
22453 if self.needsScopeBody():
22454 code += "JS::Rooted<JS::Value> result(cx);\n"
22456 return code
22458 def getArgs(self, returnType, argList):
22459 # We don't need the context or the value. We'll generate those instead.
22460 args = CGNativeMember.getArgs(self, returnType, argList)
22461 # Prepend a pointer to the binding object onto the arguments
22462 return [Argument(self.descriptorProvider.nativeType + "*", "self")] + args
22464 def needsScopeBody(self):
22465 return self.passJSBitsAsNeeded
22467 def getArgvDeclFailureCode(self):
22468 return "aRv.Throw(NS_ERROR_UNEXPECTED);\n"
22470 def getResultConversion(self):
22471 if self.needsResultConversion:
22472 code = ""
22473 if self.needsScopeBody():
22474 code = dedent(
22476 if (!JS_WrapValue(cx, &result)) {
22477 aRv.NoteJSContextException(cx);
22478 return;
22483 failureCode = dedent("aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n")
22485 exceptionCode = None
22486 if self.retvalType.isPrimitive():
22487 exceptionCode = dedent(
22488 "aRv.NoteJSContextException(cx);\nreturn%s;\n"
22489 % self.getDefaultRetval()
22492 return code + CallbackMember.getResultConversion(
22493 self,
22494 "result",
22495 failureCode=failureCode,
22496 isDefinitelyObject=True,
22497 exceptionCode=exceptionCode,
22500 assignRetval = string.Template(
22501 self.getRetvalInfo(self.retvalType, False)[2]
22502 ).substitute(
22504 "declName": "retVal",
22507 return assignRetval
22509 def getRvalDecl(self):
22510 # hack to make sure we put JSAutoRealm inside the body scope
22511 return "JSAutoRealm reflectorRealm(cx, obj);\n"
22513 def getArgcDecl(self):
22514 # Don't need argc for anything.
22515 return None
22517 def getCall(self):
22518 assert False # Override me!
22520 def getPrettyName(self):
22521 return self.name
22523 def declare(self):
22524 return self.implMethod.declare()
22526 def define(self):
22527 return self.implMethod.define()
22530 class CGMaplikeOrSetlikeHelperFunctionGenerator(CGHelperFunctionGenerator):
22532 Generates code to allow C++ to perform operations on backing objects. Gets
22533 a context from the binding wrapper, turns arguments into JS::Values (via
22534 CallbackMember/CGNativeMember argument conversion), then uses
22535 CGMaplikeOrSetlikeMethodGenerator to generate the body.
22538 def __init__(
22539 self,
22540 descriptor,
22541 maplikeOrSetlike,
22542 name,
22543 needsKeyArg=False,
22544 needsValueArg=False,
22545 needsValueTypeReturn=False,
22546 needsBoolReturn=False,
22547 needsResultConversion=True,
22549 self.maplikeOrSetlike = maplikeOrSetlike
22550 self.needsValueTypeReturn = needsValueTypeReturn
22552 args = []
22553 if needsKeyArg:
22554 args.append(FakeArgument(maplikeOrSetlike.keyType, "aKey"))
22555 if needsValueArg:
22556 assert needsKeyArg
22557 assert not needsValueTypeReturn
22558 args.append(FakeArgument(maplikeOrSetlike.valueType, "aValue"))
22560 returnType = BuiltinTypes[IDLBuiltinType.Types.undefined]
22561 if needsBoolReturn:
22562 returnType = BuiltinTypes[IDLBuiltinType.Types.boolean]
22563 elif needsValueTypeReturn:
22564 returnType = maplikeOrSetlike.valueType
22566 CGHelperFunctionGenerator.__init__(
22567 self,
22568 descriptor,
22569 name,
22570 args,
22571 returnType,
22572 needsResultConversion,
22575 def getCall(self):
22576 return CGMaplikeOrSetlikeMethodGenerator(
22577 self.descriptorProvider,
22578 self.maplikeOrSetlike,
22579 self.name.lower(),
22580 self.needsValueTypeReturn,
22581 helperImpl=self,
22582 ).define()
22585 class CGMaplikeOrSetlikeHelperGenerator(CGNamespace):
22587 Declares and defines convenience methods for accessing backing objects on
22588 setlike/maplike interface. Generates function signatures, un/packs
22589 backing objects from slot, etc.
22592 def __init__(self, descriptor, maplikeOrSetlike):
22593 self.descriptor = descriptor
22594 # Since iterables are folded in with maplike/setlike, make sure we've
22595 # got the right type here.
22596 assert maplikeOrSetlike.isMaplike() or maplikeOrSetlike.isSetlike()
22597 self.maplikeOrSetlike = maplikeOrSetlike
22598 self.namespace = "%sHelpers" % (
22599 self.maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title()
22601 self.helpers = [
22602 CGMaplikeOrSetlikeHelperFunctionGenerator(
22603 descriptor, maplikeOrSetlike, "Clear"
22605 CGMaplikeOrSetlikeHelperFunctionGenerator(
22606 descriptor,
22607 maplikeOrSetlike,
22608 "Delete",
22609 needsKeyArg=True,
22610 needsBoolReturn=True,
22611 needsResultConversion=False,
22613 CGMaplikeOrSetlikeHelperFunctionGenerator(
22614 descriptor,
22615 maplikeOrSetlike,
22616 "Has",
22617 needsKeyArg=True,
22618 needsBoolReturn=True,
22619 needsResultConversion=False,
22622 if self.maplikeOrSetlike.isMaplike():
22623 self.helpers.append(
22624 CGMaplikeOrSetlikeHelperFunctionGenerator(
22625 descriptor,
22626 maplikeOrSetlike,
22627 "Set",
22628 needsKeyArg=True,
22629 needsValueArg=True,
22632 self.helpers.append(
22633 CGMaplikeOrSetlikeHelperFunctionGenerator(
22634 descriptor,
22635 maplikeOrSetlike,
22636 "Get",
22637 needsKeyArg=True,
22638 needsValueTypeReturn=True,
22641 else:
22642 assert self.maplikeOrSetlike.isSetlike()
22643 self.helpers.append(
22644 CGMaplikeOrSetlikeHelperFunctionGenerator(
22645 descriptor, maplikeOrSetlike, "Add", needsKeyArg=True
22648 CGNamespace.__init__(self, self.namespace, CGList(self.helpers))
22651 class CGIterableMethodGenerator(CGGeneric):
22653 Creates methods for iterable interfaces. Unwrapping/wrapping
22654 will be taken care of by the usual method generation machinery in
22655 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
22656 using CGCallGenerator.
22659 def __init__(self, descriptor, methodName, args):
22660 if methodName == "forEach":
22661 assert len(args) == 2
22663 CGGeneric.__init__(
22664 self,
22665 fill(
22667 if (!JS::IsCallable(arg0)) {
22668 cx.ThrowErrorMessage<MSG_NOT_CALLABLE>("Argument 1");
22669 return false;
22671 JS::RootedValueArray<3> callArgs(cx);
22672 callArgs[2].setObject(*obj);
22673 JS::Rooted<JS::Value> ignoredReturnVal(cx);
22674 auto GetKeyAtIndex = &${selfType}::GetKeyAtIndex;
22675 auto GetValueAtIndex = &${selfType}::GetValueAtIndex;
22676 for (size_t i = 0; i < self->GetIterableLength(); ++i) {
22677 if (!CallIterableGetter(cx, GetValueAtIndex, self, i,
22678 callArgs[0])) {
22679 return false;
22681 if (!CallIterableGetter(cx, GetKeyAtIndex, self, i,
22682 callArgs[1])) {
22683 return false;
22685 if (!JS::Call(cx, arg1, arg0, JS::HandleValueArray(callArgs),
22686 &ignoredReturnVal)) {
22687 return false;
22690 """,
22691 ifaceName=descriptor.interface.identifier.name,
22692 selfType=descriptor.nativeType,
22695 return
22697 if descriptor.interface.isIterable():
22698 assert descriptor.interface.maplikeOrSetlikeOrIterable.isPairIterator()
22699 assert len(args) == 0
22701 wrap = f"{descriptor.interface.identifier.name}Iterator_Binding::Wrap"
22702 iterClass = f"mozilla::dom::binding_detail::WrappableIterableIterator<{descriptor.nativeType}, &{wrap}>"
22703 else:
22704 needReturnMethod = toStringBool(
22705 descriptor.interface.maplikeOrSetlikeOrIterable.getExtendedAttribute(
22706 "GenerateReturnMethod"
22708 is not None
22710 wrap = f"{descriptor.interface.identifier.name}AsyncIterator_Binding::Wrap"
22711 iterClass = f"mozilla::dom::binding_detail::WrappableAsyncIterableIterator<{descriptor.nativeType}, {needReturnMethod}, &{wrap}>"
22713 createIterator = fill(
22715 typedef ${iterClass} itrType;
22716 RefPtr<itrType> result(new itrType(self,
22717 itrType::IteratorType::${itrMethod}));
22718 """,
22719 iterClass=iterClass,
22720 itrMethod=methodName.title(),
22723 if descriptor.interface.isAsyncIterable():
22724 args.append("initError")
22725 createIterator = fill(
22727 $*{createIterator}
22729 ErrorResult initError;
22730 self->InitAsyncIteratorData(result->Data(), itrType::IteratorType::${itrMethod}, ${args});
22731 if (initError.MaybeSetPendingException(cx, "Asynchronous iterator initialization steps for ${ifaceName} failed")) {
22732 return false;
22735 """,
22736 createIterator=createIterator,
22737 itrMethod=methodName.title(),
22738 args=", ".join(args),
22739 ifaceName=descriptor.interface.identifier.name,
22742 CGGeneric.__init__(self, createIterator)
22745 def getObservableArrayBackingObject(descriptor, attr, errorReturn="return false;\n"):
22747 Generate code to get/create a JS backing list for an observableArray attribute
22748 from the declaration slot.
22750 assert attr.isAttr()
22751 assert attr.type.isObservableArray()
22753 # GetObservableArrayBackingObject may return a wrapped object for Xrays, so
22754 # when we create it we need to unwrap it to store the interface in the
22755 # reserved slot.
22756 return fill(
22758 JS::Rooted<JSObject*> backingObj(cx);
22759 bool created = false;
22760 if (!GetObservableArrayBackingObject(cx, obj, ${slot},
22761 &backingObj, &created, ${namespace}::ObservableArrayProxyHandler::getInstance(),
22762 self)) {
22763 $*{errorReturn}
22765 if (created) {
22766 PreserveWrapper(self);
22768 """,
22769 namespace=toBindingNamespace(MakeNativeName(attr.identifier.name)),
22770 slot=memberReservedSlot(attr, descriptor),
22771 errorReturn=errorReturn,
22772 selfType=descriptor.nativeType,
22776 def getObservableArrayGetterBody(descriptor, attr):
22778 Creates the body for the getter method of an observableArray attribute.
22780 assert attr.type.isObservableArray()
22781 return fill(
22783 $*{getBackingObj}
22784 MOZ_ASSERT(!JS_IsExceptionPending(cx));
22785 args.rval().setObject(*backingObj);
22786 return true;
22787 """,
22788 getBackingObj=getObservableArrayBackingObject(descriptor, attr),
22792 class CGObservableArrayProxyHandler_callback(ClassMethod):
22794 Base class for declaring and defining the hook methods for ObservableArrayProxyHandler
22795 subclasses to get the interface native object from backing object and calls
22796 its On{Set|Delete}* callback.
22798 * 'callbackType': "Set" or "Delete".
22799 * 'invalidTypeFatal' (optional): If True, we don't expect the type
22800 conversion would fail, so generate the
22801 assertion code if type conversion fails.
22804 def __init__(
22805 self, descriptor, attr, name, args, callbackType, invalidTypeFatal=False
22807 assert attr.isAttr()
22808 assert attr.type.isObservableArray()
22809 assert callbackType in ["Set", "Delete"]
22810 self.descriptor = descriptor
22811 self.attr = attr
22812 self.innertype = attr.type.inner
22813 self.callbackType = callbackType
22814 self.invalidTypeFatal = invalidTypeFatal
22815 ClassMethod.__init__(
22816 self,
22817 name,
22818 "bool",
22819 args,
22820 visibility="protected",
22821 virtual=True,
22822 override=True,
22823 const=True,
22826 def preConversion(self):
22828 The code to run before the conversion steps.
22830 return ""
22832 def preCallback(self):
22834 The code to run before calling the callback.
22836 return ""
22838 def postCallback(self):
22840 The code to run after calling the callback, all subclasses should override
22841 this to generate the return values.
22843 assert False # Override me!
22845 def getBody(self):
22846 exceptionCode = (
22847 fill(
22849 MOZ_ASSERT_UNREACHABLE("The item in ObservableArray backing list is not ${innertype}?");
22850 return false;
22851 """,
22852 innertype=self.innertype,
22854 if self.invalidTypeFatal
22855 else None
22857 convertType = instantiateJSToNativeConversion(
22858 getJSToNativeConversionInfo(
22859 self.innertype,
22860 self.descriptor,
22861 sourceDescription="Element in ObservableArray backing list",
22862 exceptionCode=exceptionCode,
22865 "declName": "decl",
22866 "holderName": "holder",
22867 "val": "aValue",
22870 callbackArgs = ["decl", "aIndex", "rv"]
22871 if typeNeedsCx(self.innertype):
22872 callbackArgs.insert(0, "cx")
22873 return fill(
22875 MOZ_ASSERT(IsObservableArrayProxy(aProxy));
22876 $*{preConversion}
22878 BindingCallContext cx(aCx, "ObservableArray ${name}");
22879 $*{convertType}
22881 $*{preCallback}
22882 JS::Value val = js::GetProxyReservedSlot(aProxy, OBSERVABLE_ARRAY_DOM_INTERFACE_SLOT);
22883 auto* interface = static_cast<${ifaceType}*>(val.toPrivate());
22884 MOZ_ASSERT(interface);
22886 ErrorResult rv;
22887 MOZ_KnownLive(interface)->${methodName}(${callbackArgs});
22888 $*{postCallback}
22889 """,
22890 preConversion=self.preConversion(),
22891 name=self.name,
22892 convertType=convertType.define(),
22893 preCallback=self.preCallback(),
22894 ifaceType=self.descriptor.nativeType,
22895 methodName="On%s%s"
22896 % (self.callbackType, MakeNativeName(self.attr.identifier.name)),
22897 callbackArgs=", ".join(callbackArgs),
22898 postCallback=self.postCallback(),
22902 class CGObservableArrayProxyHandler_OnDeleteItem(
22903 CGObservableArrayProxyHandler_callback
22906 Declares and defines the hook methods for ObservableArrayProxyHandler
22907 subclasses to get the interface native object from backing object and calls
22908 its OnDelete* callback.
22911 def __init__(self, descriptor, attr):
22912 args = [
22913 Argument("JSContext*", "aCx"),
22914 Argument("JS::Handle<JSObject*>", "aProxy"),
22915 Argument("JS::Handle<JS::Value>", "aValue"),
22916 Argument("uint32_t", "aIndex"),
22918 CGObservableArrayProxyHandler_callback.__init__(
22919 self,
22920 descriptor,
22921 attr,
22922 "OnDeleteItem",
22923 args,
22924 "Delete",
22925 True,
22928 def postCallback(self):
22929 return dedent(
22931 return !rv.MaybeSetPendingException(cx);
22936 class CGObservableArrayProxyHandler_SetIndexedValue(
22937 CGObservableArrayProxyHandler_callback
22940 Declares and defines the hook methods for ObservableArrayProxyHandler
22941 subclasses to run the setting the indexed value steps.
22944 def __init__(self, descriptor, attr):
22945 args = [
22946 Argument("JSContext*", "aCx"),
22947 Argument("JS::Handle<JSObject*>", "aProxy"),
22948 Argument("JS::Handle<JSObject*>", "aBackingList"),
22949 Argument("uint32_t", "aIndex"),
22950 Argument("JS::Handle<JS::Value>", "aValue"),
22951 Argument("JS::ObjectOpResult&", "aResult"),
22953 CGObservableArrayProxyHandler_callback.__init__(
22954 self,
22955 descriptor,
22956 attr,
22957 "SetIndexedValue",
22958 args,
22959 "Set",
22962 def preConversion(self):
22963 return dedent(
22965 uint32_t oldLen;
22966 if (!JS::GetArrayLength(aCx, aBackingList, &oldLen)) {
22967 return false;
22970 if (aIndex > oldLen) {
22971 return aResult.failBadIndex();
22976 def preCallback(self):
22977 return dedent(
22979 if (aIndex < oldLen) {
22980 JS::Rooted<JS::Value> value(aCx);
22981 if (!JS_GetElement(aCx, aBackingList, aIndex, &value)) {
22982 return false;
22985 if (!OnDeleteItem(aCx, aProxy, value, aIndex)) {
22986 return false;
22993 def postCallback(self):
22994 return dedent(
22996 if (rv.MaybeSetPendingException(cx)) {
22997 return false;
23000 if (!JS_SetElement(aCx, aBackingList, aIndex, aValue)) {
23001 return false;
23004 return aResult.succeed();
23009 class CGObservableArrayProxyHandler(CGThing):
23011 A class for declaring a ObservableArrayProxyHandler.
23014 def __init__(self, descriptor, attr):
23015 assert attr.isAttr()
23016 assert attr.type.isObservableArray()
23017 methods = [
23018 # The item stored in backing object should always be converted successfully.
23019 CGObservableArrayProxyHandler_OnDeleteItem(descriptor, attr),
23020 CGObservableArrayProxyHandler_SetIndexedValue(descriptor, attr),
23021 CGJSProxyHandler_getInstance("ObservableArrayProxyHandler"),
23023 parentClass = "mozilla::dom::ObservableArrayProxyHandler"
23024 self.proxyHandler = CGClass(
23025 "ObservableArrayProxyHandler",
23026 bases=[ClassBase(parentClass)],
23027 constructors=[],
23028 methods=methods,
23031 def declare(self):
23032 # Our class declaration should happen when we're defining
23033 return ""
23035 def define(self):
23036 return self.proxyHandler.declare() + "\n" + self.proxyHandler.define()
23039 class CGObservableArrayProxyHandlerGenerator(CGNamespace):
23041 Declares and defines convenience methods for accessing backing list objects
23042 for observable array attribute. Generates function signatures, un/packs
23043 backing list objects from slot, etc.
23046 def __init__(self, descriptor, attr):
23047 assert attr.isAttr()
23048 assert attr.type.isObservableArray()
23049 namespace = toBindingNamespace(MakeNativeName(attr.identifier.name))
23050 proxyHandler = CGObservableArrayProxyHandler(descriptor, attr)
23051 CGNamespace.__init__(self, namespace, proxyHandler)
23054 class CGObservableArraySetterGenerator(CGGeneric):
23056 Creates setter for an observableArray attributes.
23059 def __init__(self, descriptor, attr):
23060 assert attr.isAttr()
23061 assert attr.type.isObservableArray()
23062 getBackingObject = getObservableArrayBackingObject(descriptor, attr)
23063 setElement = dedent(
23065 if (!JS_SetElement(cx, backingObj, i, val)) {
23066 return false;
23070 conversion = wrapForType(
23071 attr.type.inner,
23072 descriptor,
23074 "result": "arg0.ElementAt(i)",
23075 "successCode": setElement,
23076 "jsvalRef": "val",
23077 "jsvalHandle": "&val",
23080 CGGeneric.__init__(
23081 self,
23082 fill(
23084 if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
23085 JS_ReportErrorASCII(cx, "Accessing from Xray wrapper is not supported.");
23086 return false;
23089 ${getBackingObject}
23090 const ObservableArrayProxyHandler* handler = GetObservableArrayProxyHandler(backingObj);
23091 if (!handler->SetLength(cx, backingObj, 0)) {
23092 return false;
23095 JS::Rooted<JS::Value> val(cx);
23096 for (size_t i = 0; i < arg0.Length(); i++) {
23097 $*{conversion}
23099 """,
23100 conversion=conversion,
23101 getBackingObject=getBackingObject,
23106 class CGObservableArrayHelperFunctionGenerator(CGHelperFunctionGenerator):
23108 Generates code to allow C++ to perform operations on backing objects. Gets
23109 a context from the binding wrapper, turns arguments into JS::Values (via
23110 CallbackMember/CGNativeMember argument conversion), then uses
23111 MethodBodyGenerator to generate the body.
23114 class MethodBodyGenerator(CGThing):
23116 Creates methods body for observable array attribute. It is expected that all
23117 methods will be have a maplike/setlike object attached. Unwrapping/wrapping
23118 will be taken care of by the usual method generation machinery in
23119 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
23120 using CGCallGenerator.
23123 def __init__(
23124 self,
23125 descriptor,
23126 attr,
23127 methodName,
23128 helperGenerator,
23129 needsIndexArg,
23131 assert attr.isAttr()
23132 assert attr.type.isObservableArray()
23134 CGThing.__init__(self)
23135 self.helperGenerator = helperGenerator
23136 self.cgRoot = CGList([])
23138 self.cgRoot.append(
23139 CGGeneric(
23140 getObservableArrayBackingObject(
23141 descriptor,
23142 attr,
23143 dedent(
23145 aRv.Throw(NS_ERROR_UNEXPECTED);
23146 return%s;
23148 % helperGenerator.getDefaultRetval()
23154 # Generates required code for the method. Method descriptions included
23155 # in definitions below. Throw if we don't have a method to fill in what
23156 # we're looking for.
23157 try:
23158 methodGenerator = getattr(self, methodName)
23159 except AttributeError:
23160 raise TypeError(
23161 "Missing observable array method definition '%s'" % methodName
23163 # Method generator returns tuple, containing:
23165 # - a list of CGThings representing setup code for preparing to call
23166 # the JS API function
23167 # - JS API function name
23168 # - a list of arguments needed for the JS API function we're calling
23169 # - a list of CGThings representing code needed before return.
23170 (setupCode, funcName, arguments, returnCode) = methodGenerator()
23172 # Append the list of setup code CGThings
23173 self.cgRoot.append(CGList(setupCode))
23174 # Create the JS API call
23175 if needsIndexArg:
23176 arguments.insert(0, "aIndex")
23177 self.cgRoot.append(
23178 CGWrapper(
23179 CGGeneric(
23180 fill(
23182 aRv.MightThrowJSException();
23183 if (!${funcName}(${args})) {
23184 aRv.StealExceptionFromJSContext(cx);
23185 return${retval};
23187 """,
23188 funcName=funcName,
23189 args=", ".join(["cx", "backingObj"] + arguments),
23190 retval=helperGenerator.getDefaultRetval(),
23195 # Append code before return
23196 self.cgRoot.append(CGList(returnCode))
23198 def elementat(self):
23199 setupCode = []
23200 if not self.helperGenerator.needsScopeBody():
23201 setupCode.append(CGGeneric("JS::Rooted<JS::Value> result(cx);\n"))
23202 returnCode = [
23203 CGGeneric(
23204 fill(
23206 if (result.isUndefined()) {
23207 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
23208 return${retval};
23210 """,
23211 retval=self.helperGenerator.getDefaultRetval(),
23215 return (setupCode, "JS_GetElement", ["&result"], returnCode)
23217 def replaceelementat(self):
23218 setupCode = [
23219 CGGeneric(
23220 fill(
23222 uint32_t length;
23223 aRv.MightThrowJSException();
23224 if (!JS::GetArrayLength(cx, backingObj, &length)) {
23225 aRv.StealExceptionFromJSContext(cx);
23226 return${retval};
23228 if (aIndex > length) {
23229 aRv.ThrowRangeError("Invalid index");
23230 return${retval};
23232 """,
23233 retval=self.helperGenerator.getDefaultRetval(),
23237 return (setupCode, "JS_SetElement", ["argv[0]"], [])
23239 def appendelement(self):
23240 setupCode = [
23241 CGGeneric(
23242 fill(
23244 uint32_t length;
23245 aRv.MightThrowJSException();
23246 if (!JS::GetArrayLength(cx, backingObj, &length)) {
23247 aRv.StealExceptionFromJSContext(cx);
23248 return${retval};
23250 """,
23251 retval=self.helperGenerator.getDefaultRetval(),
23255 return (setupCode, "JS_SetElement", ["length", "argv[0]"], [])
23257 def removelastelement(self):
23258 setupCode = [
23259 CGGeneric(
23260 fill(
23262 uint32_t length;
23263 aRv.MightThrowJSException();
23264 if (!JS::GetArrayLength(cx, backingObj, &length)) {
23265 aRv.StealExceptionFromJSContext(cx);
23266 return${retval};
23268 if (length == 0) {
23269 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
23270 return${retval};
23272 """,
23273 retval=self.helperGenerator.getDefaultRetval(),
23277 return (setupCode, "JS::SetArrayLength", ["length - 1"], [])
23279 def length(self):
23280 return (
23281 [CGGeneric("uint32_t retVal;\n")],
23282 "JS::GetArrayLength",
23283 ["&retVal"],
23287 def define(self):
23288 return self.cgRoot.define()
23290 def __init__(
23291 self,
23292 descriptor,
23293 attr,
23294 name,
23295 returnType=BuiltinTypes[IDLBuiltinType.Types.undefined],
23296 needsResultConversion=True,
23297 needsIndexArg=False,
23298 needsValueArg=False,
23300 assert attr.isAttr()
23301 assert attr.type.isObservableArray()
23302 self.attr = attr
23303 self.needsIndexArg = needsIndexArg
23305 args = []
23306 if needsValueArg:
23307 args.append(FakeArgument(attr.type.inner, "aValue"))
23309 CGHelperFunctionGenerator.__init__(
23310 self,
23311 descriptor,
23312 name,
23313 args,
23314 returnType,
23315 needsResultConversion,
23318 def getArgs(self, returnType, argList):
23319 if self.needsIndexArg:
23320 argList = [
23321 FakeArgument(BuiltinTypes[IDLBuiltinType.Types.unsigned_long], "aIndex")
23322 ] + argList
23323 return CGHelperFunctionGenerator.getArgs(self, returnType, argList)
23325 def getCall(self):
23326 return CGObservableArrayHelperFunctionGenerator.MethodBodyGenerator(
23327 self.descriptorProvider,
23328 self.attr,
23329 self.name.lower(),
23330 self,
23331 self.needsIndexArg,
23332 ).define()
23335 class CGObservableArrayHelperGenerator(CGNamespace):
23337 Declares and defines convenience methods for accessing backing object for
23338 observable array type. Generates function signatures, un/packs
23339 backing objects from slot, etc.
23342 def __init__(self, descriptor, attr):
23343 assert attr.isAttr()
23344 assert attr.type.isObservableArray()
23346 namespace = "%sHelpers" % MakeNativeName(attr.identifier.name)
23347 helpers = [
23348 CGObservableArrayHelperFunctionGenerator(
23349 descriptor,
23350 attr,
23351 "ElementAt",
23352 returnType=attr.type.inner,
23353 needsIndexArg=True,
23355 CGObservableArrayHelperFunctionGenerator(
23356 descriptor,
23357 attr,
23358 "ReplaceElementAt",
23359 needsIndexArg=True,
23360 needsValueArg=True,
23362 CGObservableArrayHelperFunctionGenerator(
23363 descriptor,
23364 attr,
23365 "AppendElement",
23366 needsValueArg=True,
23368 CGObservableArrayHelperFunctionGenerator(
23369 descriptor,
23370 attr,
23371 "RemoveLastElement",
23373 CGObservableArrayHelperFunctionGenerator(
23374 descriptor,
23375 attr,
23376 "Length",
23377 returnType=BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
23378 needsResultConversion=False,
23381 CGNamespace.__init__(self, namespace, CGList(helpers, "\n"))
23384 class GlobalGenRoots:
23386 Roots for global codegen.
23388 To generate code, call the method associated with the target, and then
23389 call the appropriate define/declare method.
23392 @staticmethod
23393 def GeneratedAtomList(config):
23394 # Atom enum
23395 dictionaries = config.dictionaries
23397 structs = []
23399 def memberToAtomCacheMember(binaryNameFor, m):
23400 binaryMemberName = binaryNameFor(m)
23401 return ClassMember(
23402 CGDictionary.makeIdName(binaryMemberName),
23403 "PinnedStringId",
23404 visibility="public",
23407 def buildAtomCacheStructure(idlobj, binaryNameFor, members):
23408 classMembers = [memberToAtomCacheMember(binaryNameFor, m) for m in members]
23409 structName = idlobj.identifier.name + "Atoms"
23410 return (
23411 structName,
23412 CGWrapper(
23413 CGClass(
23414 structName, bases=None, isStruct=True, members=classMembers
23416 post="\n",
23420 for dict in dictionaries:
23421 if len(dict.members) == 0:
23422 continue
23424 structs.append(
23425 buildAtomCacheStructure(dict, lambda m: m.identifier.name, dict.members)
23428 for d in config.getDescriptors(isJSImplemented=True) + config.getDescriptors(
23429 isCallback=True
23431 members = [m for m in d.interface.members if m.isAttr() or m.isMethod()]
23432 if d.interface.isJSImplemented() and d.interface.ctor():
23433 # We'll have an __init() method.
23434 members.append(FakeMember("__init"))
23435 if d.interface.isJSImplemented() and d.interface.getExtendedAttribute(
23436 "WantsEventListenerHooks"
23438 members.append(FakeMember("eventListenerAdded"))
23439 members.append(FakeMember("eventListenerRemoved"))
23440 if len(members) == 0:
23441 continue
23443 structs.append(
23444 buildAtomCacheStructure(
23445 d.interface,
23446 lambda m: d.binaryNameFor(m.identifier.name, m.isStatic()),
23447 members,
23451 structs.sort()
23452 generatedStructs = [struct for structName, struct in structs]
23453 structNames = [structName for structName, struct in structs]
23455 mainStruct = CGWrapper(
23456 CGClass(
23457 "PerThreadAtomCache",
23458 bases=[ClassBase(structName) for structName in structNames],
23459 isStruct=True,
23461 post="\n",
23464 structs = CGList(generatedStructs + [mainStruct])
23466 # Wrap all of that in our namespaces.
23467 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(structs, pre="\n"))
23468 curr = CGWrapper(curr, post="\n")
23470 # Add include statement for PinnedStringId.
23471 declareIncludes = ["mozilla/dom/PinnedStringId.h"]
23472 curr = CGHeaders([], [], [], [], declareIncludes, [], "GeneratedAtomList", curr)
23474 # Add include guards.
23475 curr = CGIncludeGuard("GeneratedAtomList", curr)
23477 # Add the auto-generated comment.
23478 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
23480 # Done.
23481 return curr
23483 @staticmethod
23484 def GeneratedEventList(config):
23485 eventList = CGList([])
23486 for generatedEvent in config.generatedEvents:
23487 eventList.append(
23488 CGGeneric(declare=("GENERATED_EVENT(%s)\n" % generatedEvent))
23490 return eventList
23492 @staticmethod
23493 def PrototypeList(config):
23494 # Prototype ID enum.
23495 descriptorsWithPrototype = config.getDescriptors(
23496 hasInterfacePrototypeObject=True
23498 protos = [d.name for d in descriptorsWithPrototype]
23499 idEnum = CGNamespacedEnum("id", "ID", ["_ID_Start"] + protos, [0, "_ID_Start"])
23500 idEnum = CGList([idEnum])
23502 def fieldSizeAssert(amount, jitInfoField, message):
23503 maxFieldValue = (
23504 "(uint64_t(1) << (sizeof(std::declval<JSJitInfo>().%s) * 8))"
23505 % jitInfoField
23507 return CGGeneric(
23508 define='static_assert(%s < %s, "%s");\n\n'
23509 % (amount, maxFieldValue, message)
23512 idEnum.append(
23513 fieldSizeAssert("id::_ID_Count", "protoID", "Too many prototypes!")
23516 # Wrap all of that in our namespaces.
23517 idEnum = CGNamespace.build(
23518 ["mozilla", "dom", "prototypes"], CGWrapper(idEnum, pre="\n")
23520 idEnum = CGWrapper(idEnum, post="\n")
23522 curr = CGList(
23524 CGGeneric(define="#include <stdint.h>\n"),
23525 CGGeneric(define="#include <type_traits>\n\n"),
23526 CGGeneric(define='#include "js/experimental/JitInfo.h"\n\n'),
23527 CGGeneric(define='#include "mozilla/dom/BindingNames.h"\n\n'),
23528 CGGeneric(define='#include "mozilla/dom/PrototypeList.h"\n\n'),
23529 idEnum,
23533 # Let things know the maximum length of the prototype chain.
23534 maxMacroName = "MAX_PROTOTYPE_CHAIN_LENGTH"
23535 maxMacro = CGGeneric(
23536 declare="#define " + maxMacroName + " " + str(config.maxProtoChainLength)
23538 curr.append(CGWrapper(maxMacro, post="\n\n"))
23539 curr.append(
23540 fieldSizeAssert(
23541 maxMacroName, "depth", "Some inheritance chain is too long!"
23545 # Constructor ID enum.
23546 constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)]
23547 idEnum = CGNamespacedEnum(
23548 "id",
23549 "ID",
23550 ["_ID_Start"] + constructors,
23551 ["prototypes::id::_ID_Count", "_ID_Start"],
23554 # Wrap all of that in our namespaces.
23555 idEnum = CGNamespace.build(
23556 ["mozilla", "dom", "constructors"], CGWrapper(idEnum, pre="\n")
23558 idEnum = CGWrapper(idEnum, post="\n")
23560 curr.append(idEnum)
23562 # Named properties object enum.
23563 namedPropertiesObjects = [
23564 d.name for d in config.getDescriptors(hasNamedPropertiesObject=True)
23566 idEnum = CGNamespacedEnum(
23567 "id",
23568 "ID",
23569 ["_ID_Start"] + namedPropertiesObjects,
23570 ["constructors::id::_ID_Count", "_ID_Start"],
23573 # Wrap all of that in our namespaces.
23574 idEnum = CGNamespace.build(
23575 ["mozilla", "dom", "namedpropertiesobjects"], CGWrapper(idEnum, pre="\n")
23577 idEnum = CGWrapper(idEnum, post="\n")
23579 curr.append(idEnum)
23581 traitsDecls = [
23582 CGGeneric(
23583 declare=dedent(
23585 template <prototypes::ID PrototypeID>
23586 struct PrototypeTraits;
23591 traitsDecls.extend(CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype)
23593 ifaceNamesWithProto = [
23594 d.interface.getClassName() for d in descriptorsWithPrototype
23596 traitsDecls.append(
23597 CGStringTable("NamesOfInterfacesWithProtos", ifaceNamesWithProto)
23600 traitsDecl = CGNamespace.build(["mozilla", "dom"], CGList(traitsDecls))
23602 curr.append(traitsDecl)
23604 # Add include guards.
23605 curr = CGIncludeGuard("PrototypeList", curr)
23607 # Add the auto-generated comment.
23608 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
23610 # Done.
23611 return curr
23613 @staticmethod
23614 def BindingNames(config):
23615 declare = fill(
23617 enum class BindingNamesOffset : uint16_t {
23618 $*{enumValues}
23621 namespace binding_detail {
23622 extern const char sBindingNames[];
23623 } // namespace binding_detail
23625 MOZ_ALWAYS_INLINE const char* BindingName(BindingNamesOffset aOffset) {
23626 return binding_detail::sBindingNames + static_cast<size_t>(aOffset);
23628 """,
23629 enumValues="".join(
23630 "%s = %i,\n" % (BindingNamesOffsetEnum(n), o)
23631 for (n, o) in config.namesStringOffsets
23634 define = fill(
23636 namespace binding_detail {
23638 const char sBindingNames[] = {
23639 $*{namesString}
23642 } // namespace binding_detail
23644 // Making this enum bigger than a uint16_t has consequences on the size
23645 // of some structs (eg. WebIDLNameTableEntry) and tables. We should try
23646 // to avoid that.
23647 static_assert(EnumTypeFitsWithin<BindingNamesOffset, uint16_t>::value,
23648 "Size increase");
23649 """,
23650 namesString=' "\\0"\n'.join(
23651 '/* %5i */ "%s"' % (o, n) for (n, o) in config.namesStringOffsets
23653 + "\n",
23656 curr = CGGeneric(declare=declare, define=define)
23657 curr = CGWrapper(curr, pre="\n", post="\n")
23659 curr = CGNamespace.build(["mozilla", "dom"], curr)
23660 curr = CGWrapper(curr, post="\n")
23662 curr = CGHeaders(
23667 ["<stddef.h>", "<stdint.h>", "mozilla/Attributes.h"],
23668 ["mozilla/dom/BindingNames.h", "mozilla/EnumTypeTraits.h"],
23669 "BindingNames",
23670 curr,
23673 # Add include guards.
23674 curr = CGIncludeGuard("BindingNames", curr)
23676 # Done.
23677 return curr
23679 @staticmethod
23680 def RegisterBindings(config):
23681 curr = CGNamespace.build(
23682 ["mozilla", "dom"], CGGlobalNames(config.windowGlobalNames)
23684 curr = CGWrapper(curr, post="\n")
23686 # Add the includes
23687 defineIncludes = [
23688 CGHeaders.getDeclarationFilename(desc.interface)
23689 for desc in config.getDescriptors(
23690 hasInterfaceObject=True, isExposedInWindow=True, register=True
23693 defineIncludes.append("mozilla/dom/BindingNames.h")
23694 defineIncludes.append("mozilla/dom/WebIDLGlobalNameHash.h")
23695 defineIncludes.append("mozilla/dom/PrototypeList.h")
23696 defineIncludes.append("mozilla/PerfectHash.h")
23697 defineIncludes.append("js/String.h")
23698 curr = CGHeaders([], [], [], [], [], defineIncludes, "RegisterBindings", curr)
23700 # Add include guards.
23701 curr = CGIncludeGuard("RegisterBindings", curr)
23703 # Done.
23704 return curr
23706 @staticmethod
23707 def RegisterWorkerBindings(config):
23708 curr = CGRegisterWorkerBindings(config)
23710 # Wrap all of that in our namespaces.
23711 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
23712 curr = CGWrapper(curr, post="\n")
23714 # Add the includes
23715 defineIncludes = [
23716 CGHeaders.getDeclarationFilename(desc.interface)
23717 for desc in config.getDescriptors(
23718 hasInterfaceObject=True, register=True, isExposedInAnyWorker=True
23722 curr = CGHeaders(
23723 [], [], [], [], [], defineIncludes, "RegisterWorkerBindings", curr
23726 # Add include guards.
23727 curr = CGIncludeGuard("RegisterWorkerBindings", curr)
23729 # Done.
23730 return curr
23732 @staticmethod
23733 def RegisterWorkerDebuggerBindings(config):
23734 curr = CGRegisterWorkerDebuggerBindings(config)
23736 # Wrap all of that in our namespaces.
23737 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
23738 curr = CGWrapper(curr, post="\n")
23740 # Add the includes
23741 defineIncludes = [
23742 CGHeaders.getDeclarationFilename(desc.interface)
23743 for desc in config.getDescriptors(
23744 hasInterfaceObject=True, register=True, isExposedInWorkerDebugger=True
23748 curr = CGHeaders(
23749 [], [], [], [], [], defineIncludes, "RegisterWorkerDebuggerBindings", curr
23752 # Add include guards.
23753 curr = CGIncludeGuard("RegisterWorkerDebuggerBindings", curr)
23755 # Done.
23756 return curr
23758 @staticmethod
23759 def RegisterWorkletBindings(config):
23760 curr = CGRegisterWorkletBindings(config)
23762 # Wrap all of that in our namespaces.
23763 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
23764 curr = CGWrapper(curr, post="\n")
23766 # Add the includes
23767 defineIncludes = [
23768 CGHeaders.getDeclarationFilename(desc.interface)
23769 for desc in config.getDescriptors(
23770 hasInterfaceObject=True, register=True, isExposedInAnyWorklet=True
23774 curr = CGHeaders(
23775 [], [], [], [], [], defineIncludes, "RegisterWorkletBindings", curr
23778 # Add include guards.
23779 curr = CGIncludeGuard("RegisterWorkletBindings", curr)
23781 # Done.
23782 return curr
23784 @staticmethod
23785 def RegisterShadowRealmBindings(config):
23786 curr = CGRegisterShadowRealmBindings(config)
23788 # Wrap all of that in our namespaces.
23789 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
23790 curr = CGWrapper(curr, post="\n")
23792 # Add the includes
23793 defineIncludes = [
23794 CGHeaders.getDeclarationFilename(desc.interface)
23795 for desc in config.getDescriptors(
23796 hasInterfaceObject=True, register=True, isExposedInShadowRealms=True
23800 curr = CGHeaders(
23801 [], [], [], [], [], defineIncludes, "RegisterShadowRealmBindings", curr
23804 # Add include guards.
23805 curr = CGIncludeGuard("RegisterShadowRealmBindings", curr)
23807 # Done.
23808 return curr
23810 @staticmethod
23811 def UnionTypes(config):
23812 unionTypes = UnionsForFile(config, None)
23814 includes,
23815 implincludes,
23816 declarations,
23817 traverseMethods,
23818 unlinkMethods,
23819 unionStructs,
23820 ) = UnionTypes(unionTypes, config)
23822 unionStructs = dependencySortDictionariesAndUnionsAndCallbacks(unionStructs)
23824 unions = CGList(
23825 traverseMethods
23826 + unlinkMethods
23827 + [CGUnionStruct(t, config) for t in unionStructs]
23828 + [CGUnionStruct(t, config, True) for t in unionStructs],
23829 "\n",
23832 includes.add("mozilla/OwningNonNull.h")
23833 includes.add("mozilla/dom/UnionMember.h")
23834 includes.add("mozilla/dom/BindingDeclarations.h")
23835 # BindingUtils.h is only needed for SetToObject.
23836 # If it stops being inlined or stops calling CallerSubsumes
23837 # both this bit and the bit in CGBindingRoot can be removed.
23838 includes.add("mozilla/dom/BindingUtils.h")
23840 # Wrap all of that in our namespaces.
23841 curr = CGNamespace.build(["mozilla", "dom"], unions)
23843 curr = CGWrapper(curr, post="\n")
23845 builder = ForwardDeclarationBuilder()
23846 for className, isStruct in declarations:
23847 builder.add(className, isStruct=isStruct)
23849 curr = CGList([builder.build(), curr], "\n")
23851 curr = CGHeaders([], [], [], [], includes, implincludes, "UnionTypes", curr)
23853 # Add include guards.
23854 curr = CGIncludeGuard("UnionTypes", curr)
23856 # Done.
23857 return curr
23859 @staticmethod
23860 def WebIDLPrefs(config):
23861 prefs = set()
23862 headers = set(["mozilla/dom/WebIDLPrefs.h"])
23863 for d in config.getDescriptors(hasInterfaceOrInterfacePrototypeObject=True):
23864 for m in d.interface.members:
23865 pref = PropertyDefiner.getStringAttr(m, "Pref")
23866 if pref:
23867 headers.add(prefHeader(pref))
23868 prefs.add((pref, prefIdentifier(pref)))
23869 prefs = sorted(prefs)
23870 declare = fill(
23872 enum class WebIDLPrefIndex : uint8_t {
23873 NoPref,
23874 $*{prefs}
23876 typedef bool (*WebIDLPrefFunc)();
23877 extern const WebIDLPrefFunc sWebIDLPrefs[${len}];
23878 """,
23879 prefs=",\n".join(map(lambda p: "// " + p[0] + "\n" + p[1], prefs)) + "\n",
23880 len=len(prefs) + 1,
23882 define = fill(
23884 const WebIDLPrefFunc sWebIDLPrefs[] = {
23885 nullptr,
23886 $*{prefs}
23888 """,
23889 prefs=",\n".join(
23890 map(lambda p: "// " + p[0] + "\nStaticPrefs::" + p[1], prefs)
23892 + "\n",
23894 prefFunctions = CGGeneric(declare=declare, define=define)
23896 # Wrap all of that in our namespaces.
23897 curr = CGNamespace.build(["mozilla", "dom"], prefFunctions)
23899 curr = CGWrapper(curr, post="\n")
23901 curr = CGHeaders([], [], [], [], [], headers, "WebIDLPrefs", curr)
23903 # Add include guards.
23904 curr = CGIncludeGuard("WebIDLPrefs", curr)
23906 # Done.
23907 return curr
23909 @staticmethod
23910 def WebIDLSerializable(config):
23911 # We need a declaration of StructuredCloneTags in the header.
23912 declareIncludes = set(
23914 "mozilla/dom/DOMJSClass.h",
23915 "mozilla/dom/StructuredCloneTags.h",
23916 "js/TypeDecls.h",
23919 defineIncludes = set(
23920 ["mozilla/dom/WebIDLSerializable.h", "mozilla/PerfectHash.h"]
23922 names = list()
23923 for d in config.getDescriptors(isSerializable=True):
23924 names.append(d.name)
23925 defineIncludes.add(CGHeaders.getDeclarationFilename(d.interface))
23927 if len(names) == 0:
23928 # We can't really create a PerfectHash out of this, but also there's
23929 # not much point to this file if we have no [Serializable] objects.
23930 # Just spit out an empty file.
23931 return CGIncludeGuard("WebIDLSerializable", CGGeneric(""))
23933 # If we had a lot of serializable things, it might be worth it to use a
23934 # PerfectHash here, or an array ordered by sctag value and binary
23935 # search. But setting those up would require knowing in this python
23936 # code the values of the various SCTAG_DOM_*. We could hardcode them
23937 # here and add static asserts that the values are right, or switch to
23938 # code-generating StructuredCloneTags.h or something. But in practice,
23939 # there's a pretty small number of serializable interfaces, and just
23940 # doing a linear walk is fine. It's not obviously worse than the
23941 # if-cascade we used to have. Let's just make sure we notice if we do
23942 # end up with a lot of serializable things here.
23944 # Also, in practice it looks like compilers compile this linear walk to
23945 # an out-of-bounds check followed by a direct index into an array, by
23946 # basically making a second copy of this array ordered by tag, with the
23947 # holes filled in. Again, worth checking whether this still happens if
23948 # we have too many serializable things.
23949 if len(names) > 20:
23950 raise TypeError(
23951 "We now have %s serializable interfaces. "
23952 "Double-check that the compiler is still "
23953 "generating a jump table." % len(names)
23956 entries = list()
23957 # Make sure we have stable ordering.
23958 for name in sorted(names):
23959 exposedGlobals = computeGlobalNamesFromExposureSet(d.interface.exposureSet)
23960 # Strip off trailing newline to make our formatting look right.
23961 entries.append(
23962 fill(
23965 /* mTag */ ${tag},
23966 /* mDeserialize */ ${name}_Binding::Deserialize,
23967 /* mExposedGlobals */ ${exposedGlobals},
23969 """,
23970 tag=StructuredCloneTag(name),
23971 name=name,
23972 exposedGlobals=exposedGlobals,
23973 )[:-1]
23976 declare = dedent(
23978 Maybe<std::pair<uint16_t, WebIDLDeserializer>> LookupDeserializer(StructuredCloneTags aTag);
23981 define = fill(
23983 struct WebIDLSerializableEntry {
23984 StructuredCloneTags mTag;
23985 WebIDLDeserializer mDeserialize;
23986 uint16_t mExposedGlobals;
23989 static const WebIDLSerializableEntry sEntries[] = {
23990 $*{entries}
23993 Maybe<std::pair<uint16_t, WebIDLDeserializer>> LookupDeserializer(StructuredCloneTags aTag) {
23994 for (auto& entry : sEntries) {
23995 if (entry.mTag == aTag) {
23996 return Some(std::pair(entry.mExposedGlobals, entry.mDeserialize));
23999 return Nothing();
24001 """,
24002 entries=",\n".join(entries) + "\n",
24005 code = CGGeneric(declare=declare, define=define)
24007 # Wrap all of that in our namespaces.
24008 curr = CGNamespace.build(["mozilla", "dom"], code)
24010 curr = CGWrapper(curr, post="\n")
24012 curr = CGHeaders(
24013 [], [], [], [], declareIncludes, defineIncludes, "WebIDLSerializable", curr
24016 # Add include guards.
24017 curr = CGIncludeGuard("WebIDLSerializable", curr)
24019 # Done.
24020 return curr
24023 # Code generator for simple events
24024 class CGEventGetter(CGNativeMember):
24025 def __init__(self, descriptor, attr):
24026 ea = descriptor.getExtendedAttributes(attr, getter=True)
24027 CGNativeMember.__init__(
24028 self,
24029 descriptor,
24030 attr,
24031 CGSpecializedGetterCommon.makeNativeName(descriptor, attr),
24032 (attr.type, []),
24034 resultNotAddRefed=not attr.type.isSequence(),
24036 self.body = self.getMethodBody()
24038 def getArgs(self, returnType, argList):
24039 if "needsErrorResult" in self.extendedAttrs:
24040 raise TypeError("Event code generator does not support [Throws]!")
24041 if "canOOM" in self.extendedAttrs:
24042 raise TypeError("Event code generator does not support [CanOOM]!")
24043 if not self.member.isAttr():
24044 raise TypeError("Event code generator does not support methods")
24045 if self.member.isStatic():
24046 raise TypeError("Event code generators does not support static attributes")
24047 return CGNativeMember.getArgs(self, returnType, argList)
24049 def getMethodBody(self):
24050 type = self.member.type
24051 memberName = CGDictionary.makeMemberName(self.member.identifier.name)
24052 if (
24053 (type.isPrimitive() and type.tag() in builtinNames)
24054 or type.isEnum()
24055 or type.isPromise()
24056 or type.isGeckoInterface()
24058 return "return " + memberName + ";\n"
24059 if type.isJSString():
24060 # https://bugzilla.mozilla.org/show_bug.cgi?id=1580167
24061 raise TypeError("JSString not supported as member of a generated event")
24062 if (
24063 type.isDOMString()
24064 or type.isByteString()
24065 or type.isUSVString()
24066 or type.isUTF8String()
24068 return "aRetVal = " + memberName + ";\n"
24069 if type.isSpiderMonkeyInterface() or type.isObject():
24070 return fill(
24072 if (${memberName}) {
24073 JS::ExposeObjectToActiveJS(${memberName});
24075 aRetVal.set(${memberName});
24076 return;
24077 """,
24078 memberName=memberName,
24080 if type.isAny():
24081 return fill(
24083 ${selfName}(aRetVal);
24084 """,
24085 selfName=self.name,
24087 if type.isUnion():
24088 return "aRetVal = " + memberName + ";\n"
24089 if type.isSequence():
24090 if type.nullable():
24091 return (
24092 "if ("
24093 + memberName
24094 + ".IsNull()) { aRetVal.SetNull(); } else { aRetVal.SetValue("
24095 + memberName
24096 + ".Value().Clone()); }\n"
24098 else:
24099 return "aRetVal = " + memberName + ".Clone();\n"
24100 if type.isDictionary():
24101 return "aRetVal = " + memberName + ";\n"
24102 raise TypeError("Event code generator does not support this type!")
24104 def declare(self, cgClass):
24105 if (
24106 getattr(self.member, "originatingInterface", cgClass.descriptor.interface)
24107 != cgClass.descriptor.interface
24109 return ""
24110 return CGNativeMember.declare(self, cgClass)
24112 def define(self, cgClass):
24113 if (
24114 getattr(self.member, "originatingInterface", cgClass.descriptor.interface)
24115 != cgClass.descriptor.interface
24117 return ""
24118 return CGNativeMember.define(self, cgClass)
24121 class CGEventSetter(CGNativeMember):
24122 def __init__(self):
24123 raise TypeError("Event code generator does not support setters!")
24126 class CGEventMethod(CGNativeMember):
24127 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
24128 self.isInit = False
24130 CGNativeMember.__init__(
24131 self,
24132 descriptor,
24133 method,
24134 CGSpecializedMethod.makeNativeName(descriptor, method),
24135 signature,
24136 descriptor.getExtendedAttributes(method),
24137 breakAfter=breakAfter,
24138 variadicIsSequence=True,
24140 self.originalArgs = list(self.args)
24142 iface = descriptor.interface
24143 allowed = isConstructor
24144 if not allowed and iface.getExtendedAttribute("LegacyEventInit"):
24145 # Allow it, only if it fits the initFooEvent profile exactly
24146 # We could check the arg types but it's not worth the effort.
24147 if (
24148 method.identifier.name == "init" + iface.identifier.name
24149 and signature[1][0].type.isDOMString()
24150 and signature[1][1].type.isBoolean()
24151 and signature[1][2].type.isBoolean()
24153 # -3 on the left to ignore the type, bubbles, and cancelable parameters
24154 # -1 on the right to ignore the .trusted property which bleeds through
24155 # here because it is [Unforgeable].
24156 len(signature[1]) - 3
24157 == len([x for x in iface.members if x.isAttr()]) - 1
24159 allowed = True
24160 self.isInit = True
24162 if not allowed:
24163 raise TypeError("Event code generator does not support methods!")
24165 def getArgs(self, returnType, argList):
24166 args = [self.getArg(arg) for arg in argList]
24167 return args
24169 def getArg(self, arg):
24170 decl, ref = self.getArgType(
24171 arg.type, arg.canHaveMissingValue(), "Variadic" if arg.variadic else False
24173 if ref:
24174 decl = CGWrapper(decl, pre="const ", post="&")
24176 name = arg.identifier.name
24177 name = "a" + name[0].upper() + name[1:]
24178 return Argument(decl.define(), name)
24180 def declare(self, cgClass):
24181 if self.isInit:
24182 constructorForNativeCaller = ""
24183 else:
24184 self.args = list(self.originalArgs)
24185 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
24186 constructorForNativeCaller = CGNativeMember.declare(self, cgClass)
24188 self.args = list(self.originalArgs)
24189 if needCx(None, self.arguments(), [], considerTypes=True, static=True):
24190 self.args.insert(0, Argument("JSContext*", "aCx"))
24191 if not self.isInit:
24192 self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
24194 return constructorForNativeCaller + CGNativeMember.declare(self, cgClass)
24196 def defineInit(self, cgClass):
24197 iface = self.descriptorProvider.interface
24198 members = ""
24199 while iface.identifier.name != "Event":
24200 i = 3 # Skip the boilerplate args: type, bubble,s cancelable.
24201 for m in iface.members:
24202 if m.isAttr():
24203 # We need to initialize all the member variables that do
24204 # not come from Event.
24205 if (
24206 getattr(m, "originatingInterface", iface).identifier.name
24207 == "Event"
24209 continue
24210 name = CGDictionary.makeMemberName(m.identifier.name)
24211 members += "%s = %s;\n" % (name, self.args[i].name)
24212 i += 1
24213 iface = iface.parent
24215 self.body = fill(
24217 InitEvent(${typeArg}, ${bubblesArg}, ${cancelableArg});
24218 ${members}
24219 """,
24220 typeArg=self.args[0].name,
24221 bubblesArg=self.args[1].name,
24222 cancelableArg=self.args[2].name,
24223 members=members,
24226 return CGNativeMember.define(self, cgClass)
24228 def define(self, cgClass):
24229 self.args = list(self.originalArgs)
24230 if self.isInit:
24231 return self.defineInit(cgClass)
24232 members = ""
24233 holdJS = ""
24234 iface = self.descriptorProvider.interface
24235 while iface.identifier.name != "Event":
24236 for m in self.descriptorProvider.getDescriptor(
24237 iface.identifier.name
24238 ).interface.members:
24239 if m.isAttr():
24240 # We initialize all the other member variables in the
24241 # Constructor except those ones coming from the Event.
24242 if (
24243 getattr(
24244 m, "originatingInterface", cgClass.descriptor.interface
24245 ).identifier.name
24246 == "Event"
24248 continue
24249 name = CGDictionary.makeMemberName(m.identifier.name)
24250 if m.type.isSequence():
24251 # For sequences we may not be able to do a simple
24252 # assignment because the underlying types may not match.
24253 # For example, the argument can be a
24254 # Sequence<OwningNonNull<SomeInterface>> while our
24255 # member is an nsTArray<RefPtr<SomeInterface>>. So
24256 # use AppendElements, which is actually a template on
24257 # the incoming type on nsTArray and does the right thing
24258 # for this case.
24259 target = name
24260 source = "%s.%s" % (self.args[1].name, name)
24261 sequenceCopy = "e->%s.AppendElements(%s);\n"
24262 if m.type.nullable():
24263 sequenceCopy = CGIfWrapper(
24264 CGGeneric(sequenceCopy), "!%s.IsNull()" % source
24265 ).define()
24266 target += ".SetValue()"
24267 source += ".Value()"
24268 members += sequenceCopy % (target, source)
24269 elif m.type.isSpiderMonkeyInterface():
24270 srcname = "%s.%s" % (self.args[1].name, name)
24271 if m.type.nullable():
24272 members += fill(
24274 if (${srcname}.IsNull()) {
24275 e->${varname} = nullptr;
24276 } else {
24277 e->${varname} = ${srcname}.Value().Obj();
24279 """,
24280 varname=name,
24281 srcname=srcname,
24283 else:
24284 members += fill(
24286 e->${varname}.set(${srcname}.Obj());
24287 """,
24288 varname=name,
24289 srcname=srcname,
24291 else:
24292 members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name)
24293 if (
24294 m.type.isAny()
24295 or m.type.isObject()
24296 or m.type.isSpiderMonkeyInterface()
24298 holdJS = "mozilla::HoldJSObjects(e.get());\n"
24299 iface = iface.parent
24301 self.body = fill(
24303 RefPtr<${nativeType}> e = new ${nativeType}(aOwner);
24304 bool trusted = e->Init(aOwner);
24305 e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable);
24306 $*{members}
24307 e->SetTrusted(trusted);
24308 e->SetComposed(${eventInit}.mComposed);
24309 $*{holdJS}
24310 return e.forget();
24311 """,
24312 nativeType=self.descriptorProvider.nativeType.split("::")[-1],
24313 eventType=self.args[0].name,
24314 eventInit=self.args[1].name,
24315 members=members,
24316 holdJS=holdJS,
24319 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
24320 constructorForNativeCaller = CGNativeMember.define(self, cgClass) + "\n"
24321 self.args = list(self.originalArgs)
24322 self.body = fill(
24324 nsCOMPtr<mozilla::dom::EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
24325 return Constructor(owner, ${arg0}, ${arg1});
24326 """,
24327 arg0=self.args[0].name,
24328 arg1=self.args[1].name,
24330 if needCx(None, self.arguments(), [], considerTypes=True, static=True):
24331 self.args.insert(0, Argument("JSContext*", "aCx"))
24332 self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
24333 return constructorForNativeCaller + CGNativeMember.define(self, cgClass)
24336 class CGEventClass(CGBindingImplClass):
24338 Codegen for the actual Event class implementation for this descriptor
24341 def __init__(self, descriptor):
24342 CGBindingImplClass.__init__(
24343 self,
24344 descriptor,
24345 CGEventMethod,
24346 CGEventGetter,
24347 CGEventSetter,
24348 False,
24349 "WrapObjectInternal",
24351 members = []
24352 extraMethods = []
24353 self.membersNeedingCC = []
24354 self.membersNeedingTrace = []
24356 for m in descriptor.interface.members:
24357 if (
24358 getattr(m, "originatingInterface", descriptor.interface)
24359 != descriptor.interface
24361 continue
24363 if m.isAttr():
24364 if m.type.isAny():
24365 self.membersNeedingTrace.append(m)
24366 # Add a getter that doesn't need a JSContext. Note that we
24367 # don't need to do this if our originating interface is not
24368 # the descriptor's interface, because in that case we
24369 # wouldn't generate the getter that _does_ need a JSContext
24370 # either.
24371 extraMethods.append(
24372 ClassMethod(
24373 CGSpecializedGetterCommon.makeNativeName(descriptor, m),
24374 "void",
24375 [Argument("JS::MutableHandle<JS::Value>", "aRetVal")],
24376 const=True,
24377 body=fill(
24379 JS::ExposeValueToActiveJS(${memberName});
24380 aRetVal.set(${memberName});
24381 """,
24382 memberName=CGDictionary.makeMemberName(
24383 m.identifier.name
24388 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
24389 self.membersNeedingTrace.append(m)
24390 elif typeNeedsRooting(m.type):
24391 raise TypeError(
24392 "Need to implement tracing for event member of type %s" % m.type
24394 elif idlTypeNeedsCycleCollection(m.type):
24395 self.membersNeedingCC.append(m)
24397 nativeType = self.getNativeTypeForIDLType(m.type).define()
24398 members.append(
24399 ClassMember(
24400 CGDictionary.makeMemberName(m.identifier.name),
24401 nativeType,
24402 visibility="private",
24403 body="body",
24407 parent = self.descriptor.interface.parent
24408 self.parentType = self.descriptor.getDescriptor(
24409 parent.identifier.name
24410 ).nativeType.split("::")[-1]
24411 self.nativeType = self.descriptor.nativeType.split("::")[-1]
24413 if self.needCC():
24414 isupportsDecl = fill(
24416 NS_DECL_ISUPPORTS_INHERITED
24417 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(${nativeType}, ${parentType})
24418 """,
24419 nativeType=self.nativeType,
24420 parentType=self.parentType,
24422 else:
24423 isupportsDecl = fill(
24425 NS_INLINE_DECL_REFCOUNTING_INHERITED(${nativeType}, ${parentType})
24426 """,
24427 nativeType=self.nativeType,
24428 parentType=self.parentType,
24431 baseDeclarations = fill(
24433 public:
24434 $*{isupportsDecl}
24436 protected:
24437 virtual ~${nativeType}();
24438 explicit ${nativeType}(mozilla::dom::EventTarget* aOwner);
24440 """,
24441 isupportsDecl=isupportsDecl,
24442 nativeType=self.nativeType,
24443 parentType=self.parentType,
24446 className = self.nativeType
24447 asConcreteTypeMethod = ClassMethod(
24448 "As%s" % className,
24449 "%s*" % className,
24451 virtual=True,
24452 body="return this;\n",
24453 breakAfterReturnDecl=" ",
24454 override=True,
24456 extraMethods.append(asConcreteTypeMethod)
24458 CGClass.__init__(
24459 self,
24460 className,
24461 bases=[ClassBase(self.parentType)],
24462 methods=extraMethods + self.methodDecls,
24463 members=members,
24464 extradeclarations=baseDeclarations,
24467 def getWrapObjectBody(self):
24468 return (
24469 "return %s_Binding::Wrap(aCx, this, aGivenProto);\n" % self.descriptor.name
24472 def needCC(self):
24473 return len(self.membersNeedingCC) != 0 or len(self.membersNeedingTrace) != 0
24475 def implTraverse(self):
24476 retVal = ""
24477 for m in self.membersNeedingCC:
24478 retVal += (
24479 " NS_IMPL_CYCLE_COLLECTION_TRAVERSE(%s)\n"
24480 % CGDictionary.makeMemberName(m.identifier.name)
24482 return retVal
24484 def implUnlink(self):
24485 retVal = ""
24486 for m in self.membersNeedingCC:
24487 retVal += (
24488 " NS_IMPL_CYCLE_COLLECTION_UNLINK(%s)\n"
24489 % CGDictionary.makeMemberName(m.identifier.name)
24491 for m in self.membersNeedingTrace:
24492 name = CGDictionary.makeMemberName(m.identifier.name)
24493 if m.type.isAny():
24494 retVal += " tmp->" + name + ".setUndefined();\n"
24495 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
24496 retVal += " tmp->" + name + " = nullptr;\n"
24497 else:
24498 raise TypeError("Unknown traceable member type %s" % m.type)
24499 return retVal
24501 def implTrace(self):
24502 retVal = ""
24503 for m in self.membersNeedingTrace:
24504 retVal += (
24505 " NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(%s)\n"
24506 % CGDictionary.makeMemberName(m.identifier.name)
24508 return retVal
24510 def define(self):
24511 for m in self.membersNeedingTrace:
24512 if not (
24513 m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface()
24515 raise TypeError("Unknown traceable member type %s" % m.type)
24517 if len(self.membersNeedingTrace) > 0:
24518 dropJS = "mozilla::DropJSObjects(this);\n"
24519 else:
24520 dropJS = ""
24521 # Just override CGClass and do our own thing
24522 ctorParams = (
24523 "aOwner, nullptr, nullptr" if self.parentType == "Event" else "aOwner"
24526 if self.needCC():
24527 classImpl = fill(
24530 NS_IMPL_CYCLE_COLLECTION_CLASS(${nativeType})
24532 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
24533 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
24535 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(${nativeType}, ${parentType})
24536 $*{traverse}
24537 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
24539 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(${nativeType}, ${parentType})
24540 $*{trace}
24541 NS_IMPL_CYCLE_COLLECTION_TRACE_END
24543 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(${nativeType}, ${parentType})
24544 $*{unlink}
24545 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
24547 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
24548 NS_INTERFACE_MAP_END_INHERITING(${parentType})
24549 """,
24550 nativeType=self.nativeType,
24551 parentType=self.parentType,
24552 traverse=self.implTraverse(),
24553 unlink=self.implUnlink(),
24554 trace=self.implTrace(),
24556 else:
24557 classImpl = ""
24559 classImpl += fill(
24562 ${nativeType}::${nativeType}(mozilla::dom::EventTarget* aOwner)
24563 : ${parentType}(${ctorParams})
24567 ${nativeType}::~${nativeType}()
24569 $*{dropJS}
24572 """,
24573 nativeType=self.nativeType,
24574 ctorParams=ctorParams,
24575 parentType=self.parentType,
24576 dropJS=dropJS,
24579 return classImpl + CGBindingImplClass.define(self)
24581 def getNativeTypeForIDLType(self, type):
24582 if type.isPrimitive() and type.tag() in builtinNames:
24583 nativeType = CGGeneric(builtinNames[type.tag()])
24584 if type.nullable():
24585 nativeType = CGTemplatedType("Nullable", nativeType)
24586 elif type.isEnum():
24587 nativeType = CGGeneric(type.unroll().inner.identifier.name)
24588 if type.nullable():
24589 nativeType = CGTemplatedType("Nullable", nativeType)
24590 elif type.isJSString():
24591 nativeType = CGGeneric("JS::Heap<JSString*>")
24592 elif type.isDOMString() or type.isUSVString():
24593 nativeType = CGGeneric("nsString")
24594 elif type.isByteString() or type.isUTF8String():
24595 nativeType = CGGeneric("nsCString")
24596 elif type.isPromise():
24597 nativeType = CGGeneric("RefPtr<Promise>")
24598 elif type.isDictionary():
24599 if typeNeedsRooting(type):
24600 raise TypeError(
24601 "We don't support event members that are dictionary types "
24602 "that need rooting (%s)" % type
24604 nativeType = CGGeneric(CGDictionary.makeDictionaryName(type.unroll().inner))
24605 if type.nullable():
24606 nativeType = CGTemplatedType("Nullable", nativeType)
24607 elif type.isGeckoInterface():
24608 iface = type.unroll().inner
24609 nativeType = self.descriptor.getDescriptor(iface.identifier.name).nativeType
24610 # Now trim off unnecessary namespaces
24611 nativeType = nativeType.split("::")
24612 if nativeType[0] == "mozilla":
24613 nativeType.pop(0)
24614 if nativeType[0] == "dom":
24615 nativeType.pop(0)
24616 nativeType = CGWrapper(
24617 CGGeneric("::".join(nativeType)), pre="RefPtr<", post=">"
24619 elif type.isAny():
24620 nativeType = CGGeneric("JS::Heap<JS::Value>")
24621 elif type.isObject() or type.isSpiderMonkeyInterface():
24622 nativeType = CGGeneric("JS::Heap<JSObject*>")
24623 elif type.isUnion():
24624 nativeType = CGGeneric(CGUnionStruct.unionTypeDecl(type, True))
24625 elif type.isSequence():
24626 if type.nullable():
24627 innerType = type.inner.inner
24628 else:
24629 innerType = type.inner
24630 if (
24631 not innerType.isPrimitive()
24632 and not innerType.isEnum()
24633 and not innerType.isDOMString()
24634 and not innerType.isByteString()
24635 and not innerType.isUTF8String()
24636 and not innerType.isPromise()
24637 and not innerType.isGeckoInterface()
24639 raise TypeError(
24640 "Don't know how to properly manage GC/CC for "
24641 "event member of type %s" % type
24643 nativeType = CGTemplatedType(
24644 "nsTArray", self.getNativeTypeForIDLType(innerType)
24646 if type.nullable():
24647 nativeType = CGTemplatedType("Nullable", nativeType)
24648 else:
24649 raise TypeError("Don't know how to declare event member of type %s" % type)
24650 return nativeType
24653 class CGEventRoot(CGThing):
24654 def __init__(self, config, interfaceName):
24655 descriptor = config.getDescriptor(interfaceName)
24657 self.root = CGWrapper(CGEventClass(descriptor), pre="\n", post="\n")
24659 self.root = CGNamespace.build(["mozilla", "dom"], self.root)
24661 self.root = CGList(
24662 [CGClassForwardDeclare("JSContext", isStruct=True), self.root]
24665 parent = descriptor.interface.parent.identifier.name
24667 # Throw in our #includes
24668 self.root = CGHeaders(
24669 [descriptor],
24674 config.getDescriptor(parent).headerFile,
24675 "mozilla/Attributes.h",
24676 "mozilla/dom/%sBinding.h" % interfaceName,
24677 "mozilla/dom/BindingUtils.h",
24680 "%s.h" % interfaceName,
24681 "js/GCAPI.h",
24682 "mozilla/HoldDropJSObjects.h",
24683 "mozilla/dom/Nullable.h",
24686 self.root,
24687 config,
24690 # And now some include guards
24691 self.root = CGIncludeGuard(interfaceName, self.root)
24693 self.root = CGWrapper(
24694 self.root,
24695 pre=(
24696 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT
24697 % os.path.basename(descriptor.interface.filename)
24701 self.root = CGWrapper(
24702 self.root,
24703 pre=dedent(
24705 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
24706 /* vim:set ts=2 sw=2 sts=2 et cindent: */
24707 /* This Source Code Form is subject to the terms of the Mozilla Public
24708 * License, v. 2.0. If a copy of the MPL was not distributed with this
24709 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
24715 def declare(self):
24716 return self.root.declare()
24718 def define(self):
24719 return self.root.define()