Bug 1700051: part 36) Reduce accessibility of `SoftText::mBegin` to `private`. r...
[gecko.git] / dom / bindings / Codegen.py
blob15a1cf901b58ab579795f6a7388cb9531bfb74a5
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 os
8 import re
9 import string
10 import math
11 import textwrap
12 import functools
14 from perfecthash import PerfectHash
15 import six
17 from WebIDL import (
18 BuiltinTypes,
19 IDLBuiltinType,
20 IDLDefaultDictionaryValue,
21 IDLNullValue,
22 IDLSequenceType,
23 IDLType,
24 IDLAttribute,
25 IDLInterfaceMember,
26 IDLUndefinedValue,
27 IDLEmptySequenceValue,
28 IDLDictionary,
30 from Configuration import (
31 NoSuchDescriptorError,
32 getTypesFromDescriptor,
33 getTypesFromDictionary,
34 getTypesFromCallback,
35 getAllTypes,
36 Descriptor,
37 MemberIsLegacyUnforgeable,
38 iteratorNativeType,
41 AUTOGENERATED_WARNING_COMMENT = (
42 "/* THIS FILE IS AUTOGENERATED BY Codegen.py - DO NOT EDIT */\n\n"
44 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT = (
45 "/* THIS FILE IS AUTOGENERATED FROM %s BY Codegen.py - DO NOT EDIT */\n\n"
47 ADDPROPERTY_HOOK_NAME = "_addProperty"
48 GETWRAPPERCACHE_HOOK_NAME = "_getWrapperCache"
49 FINALIZE_HOOK_NAME = "_finalize"
50 OBJECT_MOVED_HOOK_NAME = "_objectMoved"
51 CONSTRUCT_HOOK_NAME = "_constructor"
52 LEGACYCALLER_HOOK_NAME = "_legacycaller"
53 RESOLVE_HOOK_NAME = "_resolve"
54 MAY_RESOLVE_HOOK_NAME = "_mayResolve"
55 NEW_ENUMERATE_HOOK_NAME = "_newEnumerate"
56 ENUM_ENTRY_VARIABLE_NAME = "strings"
57 INSTANCE_RESERVED_SLOTS = 1
59 # This size is arbitrary. It is a power of 2 to make using it as a modulo
60 # operand cheap, and is usually around 1/3-1/5th of the set size (sometimes
61 # smaller for very large sets).
62 GLOBAL_NAMES_PHF_SIZE = 256
65 def memberReservedSlot(member, descriptor):
66 return (
67 "(DOM_INSTANCE_RESERVED_SLOTS + %d)"
68 % member.slotIndices[descriptor.interface.identifier.name]
72 def memberXrayExpandoReservedSlot(member, descriptor):
73 return (
74 "(xpc::JSSLOT_EXPANDO_COUNT + %d)"
75 % member.slotIndices[descriptor.interface.identifier.name]
79 def mayUseXrayExpandoSlots(descriptor, attr):
80 assert not attr.getExtendedAttribute("NewObject")
81 # For attributes whose type is a Gecko interface we always use
82 # slots on the reflector for caching. Also, for interfaces that
83 # don't want Xrays we obviously never use the Xray expando slot.
84 return descriptor.wantsXrays and not attr.type.isGeckoInterface()
87 def toStringBool(arg):
88 """
89 Converts IDL/Python Boolean (True/False) to C++ Boolean (true/false)
90 """
91 return str(not not arg).lower()
94 def toBindingNamespace(arg):
95 return arg + "_Binding"
98 def isTypeCopyConstructible(type):
99 # Nullable and sequence stuff doesn't affect copy-constructibility
100 type = type.unroll()
101 return (
102 type.isPrimitive()
103 or type.isString()
104 or type.isEnum()
105 or (type.isUnion() and CGUnionStruct.isUnionCopyConstructible(type))
106 or (
107 type.isDictionary()
108 and CGDictionary.isDictionaryCopyConstructible(type.inner)
111 # Interface types are only copy-constructible if they're Gecko
112 # interfaces. SpiderMonkey interfaces are not copy-constructible
113 # because of rooting issues.
114 (type.isInterface() and type.isGeckoInterface())
118 class CycleCollectionUnsupported(TypeError):
119 def __init__(self, message):
120 TypeError.__init__(self, message)
123 def idlTypeNeedsCycleCollection(type):
124 type = type.unroll() # Takes care of sequences and nullables
125 if (
126 (type.isPrimitive() and type.tag() in builtinNames)
127 or type.isEnum()
128 or type.isString()
129 or type.isAny()
130 or type.isObject()
131 or type.isSpiderMonkeyInterface()
133 return False
134 elif type.isCallback() or type.isPromise() or type.isGeckoInterface():
135 return True
136 elif type.isUnion():
137 return any(idlTypeNeedsCycleCollection(t) for t in type.flatMemberTypes)
138 elif type.isRecord():
139 if idlTypeNeedsCycleCollection(type.inner):
140 raise CycleCollectionUnsupported(
141 "Cycle collection for type %s is not supported" % type
143 return False
144 elif type.isDictionary():
145 return CGDictionary.dictionaryNeedsCycleCollection(type.inner)
146 else:
147 raise CycleCollectionUnsupported(
148 "Don't know whether to cycle-collect type %s" % type
152 def idlTypeNeedsCallContext(type, descriptor=None, allowTreatNonCallableAsNull=False):
154 Returns whether the given type needs error reporting via a
155 BindingCallContext for JS-to-C++ conversions. This will happen when the
156 conversion can throw an exception due to logic in the IDL spec or
157 Gecko-specific security checks. In particular, a type needs a
158 BindingCallContext if and only if the JS-to-C++ conversion for that type can
159 end up calling ThrowErrorMessage.
161 For some types this depends on the descriptor (e.g. because we do certain
162 checks only for some kinds of interfaces).
164 The allowTreatNonCallableAsNull optimization is there so we can avoid
165 generating an unnecessary BindingCallContext for all the event handler
166 attribute setters.
169 while True:
170 if type.isSequence():
171 # Sequences can always throw "not an object"
172 return True
173 if type.nullable():
174 # treatNonObjectAsNull() and treatNonCallableAsNull() are
175 # only sane things to test on nullable types, so do that now.
176 if (
177 allowTreatNonCallableAsNull
178 and type.isCallback()
179 and (type.treatNonObjectAsNull() or type.treatNonCallableAsNull())
181 # This can't throw. so never needs a method description.
182 return False
183 type = type.inner
184 else:
185 break
187 # The float check needs to come before the isPrimitive() check,
188 # because floats are primitives too.
189 if type.isFloat():
190 # Floats can throw if restricted.
191 return not type.isUnrestricted()
192 if type.isPrimitive() and type.tag() in builtinNames:
193 # Numbers can throw if enforcing range.
194 return type.hasEnforceRange()
195 if type.isEnum():
196 # Can throw on invalid value.
197 return True
198 if type.isString():
199 # Can throw if it's a ByteString
200 return type.isByteString()
201 if type.isAny():
202 # JS-implemented interfaces do extra security checks so need a
203 # method description here. If we have no descriptor, this
204 # might be JS-implemented thing, so it will do the security
205 # check and we need the method description.
206 return not descriptor or descriptor.interface.isJSImplemented()
207 if type.isPromise():
208 # JS-to-Promise conversion won't cause us to throw any
209 # specific exceptions, so does not need a method description.
210 return False
211 if (
212 type.isObject()
213 or type.isInterface()
214 or type.isCallback()
215 or type.isDictionary()
216 or type.isRecord()
218 # These can all throw if a primitive is passed in, at the very least.
219 # There are some rare cases when we know we have an object, but those
220 # are not worth the complexity of optimizing for.
222 # Note that we checked the [LegacyTreatNonObjectAsNull] case already when
223 # unwrapping nullables.
224 return True
225 if type.isUnion():
226 # Can throw if a type not in the union is passed in.
227 return True
228 if type.isVoid():
229 # Clearly doesn't need a method description; we can only get here from
230 # CGHeaders trying to decide whether to include the method description
231 # header.
232 return False
233 raise TypeError("Don't know whether type '%s' needs a method description" % type)
236 # TryPreserveWrapper uses the addProperty hook to preserve the wrapper of
237 # non-nsISupports cycle collected objects, so if wantsAddProperty is changed
238 # to not cover that case then TryPreserveWrapper will need to be changed.
239 def wantsAddProperty(desc):
240 return desc.concrete and desc.wrapperCache and not desc.isGlobal()
243 def wantsGetWrapperCache(desc):
244 return (
245 desc.concrete and desc.wrapperCache and not desc.isGlobal() and not desc.proxy
249 # We'll want to insert the indent at the beginnings of lines, but we
250 # don't want to indent empty lines. So only indent lines that have a
251 # non-newline character on them.
252 lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE)
255 def indent(s, indentLevel=2):
257 Indent C++ code.
259 Weird secret feature: this doesn't indent lines that start with # (such as
260 #include lines or #ifdef/#endif).
262 if s == "":
263 return s
264 return re.sub(lineStartDetector, indentLevel * " ", s)
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 string table for the given strings with a function accessor:
418 const char *accessorName(unsigned int index) {
419 static const char table[] = "...";
420 static const uint16_t indices = { ... };
421 return &table[indices[index]];
424 This is more efficient than the more natural:
426 const char *table[] = {
430 The uint16_t indices are smaller than the pointer equivalents, and the
431 string table 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 "extern const char *%s(unsigned int aIndex);\n" % self.accessorName
445 def define(self):
446 table = ' "\\0" '.join('"%s"' % s for s in self.strings)
447 indices = []
448 currentIndex = 0
449 for s in self.strings:
450 indices.append(currentIndex)
451 currentIndex += len(s) + 1 # for the null terminator
452 return fill(
454 ${static}const char *${name}(unsigned int aIndex)
456 static const char table[] = ${table};
457 static const uint16_t indices[] = { ${indices} };
458 static_assert(${currentIndex} <= UINT16_MAX, "string table overflow!");
459 return &table[indices[aIndex]];
461 """,
462 static="static " if self.static else "",
463 name=self.accessorName,
464 table=table,
465 indices=", ".join("%d" % index for index in indices),
466 currentIndex=currentIndex,
470 class CGNativePropertyHooks(CGThing):
472 Generate a NativePropertyHooks for a given descriptor
475 def __init__(self, descriptor, properties):
476 CGThing.__init__(self)
477 self.descriptor = descriptor
478 self.properties = properties
480 def declare(self):
481 if not self.descriptor.wantsXrays:
482 return ""
483 return dedent(
485 // We declare this as an array so that retrieving a pointer to this
486 // binding's property hooks only requires compile/link-time resolvable
487 // address arithmetic. Declaring it as a pointer instead would require
488 // doing a run-time load to fetch a pointer to this binding's property
489 // hooks. And then structures which embedded a pointer to this structure
490 // would require a run-time load for proper initialization, which would
491 // then induce static constructors. Lots of static constructors.
492 extern const NativePropertyHooks sNativePropertyHooks[];
496 def define(self):
497 if not self.descriptor.wantsXrays:
498 return ""
499 deleteNamedProperty = "nullptr"
500 if (
501 self.descriptor.concrete
502 and self.descriptor.proxy
503 and not self.descriptor.isMaybeCrossOriginObject()
505 resolveOwnProperty = "ResolveOwnProperty"
506 enumerateOwnProperties = "EnumerateOwnProperties"
507 if self.descriptor.needsXrayNamedDeleterHook():
508 deleteNamedProperty = "DeleteNamedProperty"
509 elif self.descriptor.needsXrayResolveHooks():
510 resolveOwnProperty = "ResolveOwnPropertyViaResolve"
511 enumerateOwnProperties = "EnumerateOwnPropertiesViaGetOwnPropertyNames"
512 else:
513 resolveOwnProperty = "nullptr"
514 enumerateOwnProperties = "nullptr"
515 if self.properties.hasNonChromeOnly():
516 regular = "sNativeProperties.Upcast()"
517 else:
518 regular = "nullptr"
519 if self.properties.hasChromeOnly():
520 chrome = "sChromeOnlyNativeProperties.Upcast()"
521 else:
522 chrome = "nullptr"
523 constructorID = "constructors::id::"
524 if self.descriptor.interface.hasInterfaceObject():
525 constructorID += self.descriptor.name
526 else:
527 constructorID += "_ID_Count"
528 prototypeID = "prototypes::id::"
529 if self.descriptor.interface.hasInterfacePrototypeObject():
530 prototypeID += self.descriptor.name
531 else:
532 prototypeID += "_ID_Count"
533 parentProtoName = self.descriptor.parentPrototypeName
534 parentHooks = (
535 toBindingNamespace(parentProtoName) + "::sNativePropertyHooks"
536 if parentProtoName
537 else "nullptr"
540 if self.descriptor.wantsXrayExpandoClass:
541 expandoClass = "&sXrayExpandoObjectClass"
542 else:
543 expandoClass = "&DefaultXrayExpandoObjectClass"
545 return fill(
547 const NativePropertyHooks sNativePropertyHooks[] = { {
548 ${resolveOwnProperty},
549 ${enumerateOwnProperties},
550 ${deleteNamedProperty},
551 { ${regular}, ${chrome} },
552 ${prototypeID},
553 ${constructorID},
554 ${parentHooks},
555 ${expandoClass}
556 } };
557 """,
558 resolveOwnProperty=resolveOwnProperty,
559 enumerateOwnProperties=enumerateOwnProperties,
560 deleteNamedProperty=deleteNamedProperty,
561 regular=regular,
562 chrome=chrome,
563 prototypeID=prototypeID,
564 constructorID=constructorID,
565 parentHooks=parentHooks,
566 expandoClass=expandoClass,
570 def NativePropertyHooks(descriptor):
571 return (
572 "&sEmptyNativePropertyHooks"
573 if not descriptor.wantsXrays
574 else "sNativePropertyHooks"
578 def DOMClass(descriptor):
579 protoList = ["prototypes::id::" + proto for proto in descriptor.prototypeNameChain]
580 # Pad out the list to the right length with _ID_Count so we
581 # guarantee that all the lists are the same length. _ID_Count
582 # is never the ID of any prototype, so it's safe to use as
583 # padding.
584 protoList.extend(
585 ["prototypes::id::_ID_Count"]
586 * (descriptor.config.maxProtoChainLength - len(protoList))
589 if descriptor.interface.isSerializable():
590 serializer = "Serialize"
591 else:
592 serializer = "nullptr"
594 if wantsGetWrapperCache(descriptor):
595 wrapperCacheGetter = GETWRAPPERCACHE_HOOK_NAME
596 else:
597 wrapperCacheGetter = "nullptr"
599 return fill(
601 { ${protoChain} },
602 std::is_base_of_v<nsISupports, ${nativeType}>,
603 ${hooks},
604 FindAssociatedGlobalForNative<${nativeType}>::Get,
605 GetProtoObjectHandle,
606 GetCCParticipant<${nativeType}>::Get(),
607 ${serializer},
608 ${wrapperCacheGetter}
609 """,
610 protoChain=", ".join(protoList),
611 nativeType=descriptor.nativeType,
612 hooks=NativePropertyHooks(descriptor),
613 serializer=serializer,
614 wrapperCacheGetter=wrapperCacheGetter,
618 def InstanceReservedSlots(descriptor):
619 slots = INSTANCE_RESERVED_SLOTS + descriptor.interface.totalMembersInSlots
620 if descriptor.isMaybeCrossOriginObject():
621 # We need a slot for the cross-origin holder too.
622 if descriptor.interface.hasChildInterfaces():
623 raise TypeError(
624 "We don't support non-leaf cross-origin interfaces "
625 "like %s" % descriptor.interface.identifier.name
627 slots += 1
628 return slots
631 class CGDOMJSClass(CGThing):
633 Generate a DOMJSClass for a given descriptor
636 def __init__(self, descriptor):
637 CGThing.__init__(self)
638 self.descriptor = descriptor
640 def declare(self):
641 return ""
643 def define(self):
644 callHook = (
645 LEGACYCALLER_HOOK_NAME
646 if self.descriptor.operations["LegacyCaller"]
647 else "nullptr"
649 objectMovedHook = (
650 OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else "nullptr"
652 slotCount = InstanceReservedSlots(self.descriptor)
653 classFlags = "JSCLASS_IS_DOMJSCLASS | JSCLASS_FOREGROUND_FINALIZE | "
654 if self.descriptor.isGlobal():
655 classFlags += (
656 "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS)"
658 traceHook = "JS_GlobalObjectTraceHook"
659 reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
660 else:
661 classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
662 traceHook = "nullptr"
663 reservedSlots = slotCount
664 if self.descriptor.interface.hasProbablyShortLivingWrapper():
665 if not self.descriptor.wrapperCache:
666 raise TypeError(
667 "Need a wrapper cache to support nursery "
668 "allocation of DOM objects"
670 classFlags += " | JSCLASS_SKIP_NURSERY_FINALIZE"
672 if self.descriptor.interface.getExtendedAttribute("NeedResolve"):
673 resolveHook = RESOLVE_HOOK_NAME
674 mayResolveHook = MAY_RESOLVE_HOOK_NAME
675 newEnumerateHook = NEW_ENUMERATE_HOOK_NAME
676 elif self.descriptor.isGlobal():
677 resolveHook = "mozilla::dom::ResolveGlobal"
678 mayResolveHook = "mozilla::dom::MayResolveGlobal"
679 newEnumerateHook = "mozilla::dom::EnumerateGlobal"
680 else:
681 resolveHook = "nullptr"
682 mayResolveHook = "nullptr"
683 newEnumerateHook = "nullptr"
685 return fill(
687 static const JSClassOps sClassOps = {
688 ${addProperty}, /* addProperty */
689 nullptr, /* delProperty */
690 nullptr, /* enumerate */
691 ${newEnumerate}, /* newEnumerate */
692 ${resolve}, /* resolve */
693 ${mayResolve}, /* mayResolve */
694 ${finalize}, /* finalize */
695 ${call}, /* call */
696 nullptr, /* hasInstance */
697 nullptr, /* construct */
698 ${trace}, /* trace */
701 static const js::ClassExtension sClassExtension = {
702 ${objectMoved} /* objectMovedOp */
705 static const DOMJSClass sClass = {
706 { "${name}",
707 ${flags},
708 &sClassOps,
709 JS_NULL_CLASS_SPEC,
710 &sClassExtension,
711 JS_NULL_OBJECT_OPS
713 $*{descriptor}
715 static_assert(${instanceReservedSlots} == DOM_INSTANCE_RESERVED_SLOTS,
716 "Must have the right minimal number of reserved slots.");
717 static_assert(${reservedSlots} >= ${slotCount},
718 "Must have enough reserved slots.");
719 """,
720 name=self.descriptor.interface.getClassName(),
721 flags=classFlags,
722 addProperty=ADDPROPERTY_HOOK_NAME
723 if wantsAddProperty(self.descriptor)
724 else "nullptr",
725 newEnumerate=newEnumerateHook,
726 resolve=resolveHook,
727 mayResolve=mayResolveHook,
728 finalize=FINALIZE_HOOK_NAME,
729 call=callHook,
730 trace=traceHook,
731 objectMoved=objectMovedHook,
732 descriptor=DOMClass(self.descriptor),
733 instanceReservedSlots=INSTANCE_RESERVED_SLOTS,
734 reservedSlots=reservedSlots,
735 slotCount=slotCount,
739 class CGDOMProxyJSClass(CGThing):
741 Generate a DOMJSClass for a given proxy descriptor
744 def __init__(self, descriptor):
745 CGThing.__init__(self)
746 self.descriptor = descriptor
748 def declare(self):
749 return ""
751 def define(self):
752 slotCount = InstanceReservedSlots(self.descriptor)
753 # We need one reserved slot (DOM_OBJECT_SLOT).
754 flags = ["JSCLASS_IS_DOMJSCLASS", "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount]
755 # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because
756 # we don't want people ever adding that to any interface other than
757 # HTMLAllCollection. So just hardcode it here.
758 if self.descriptor.interface.identifier.name == "HTMLAllCollection":
759 flags.append("JSCLASS_EMULATES_UNDEFINED")
760 return fill(
762 static const DOMJSClass sClass = {
763 PROXY_CLASS_DEF("${name}",
764 ${flags}),
765 $*{descriptor}
767 """,
768 name=self.descriptor.interface.identifier.name,
769 flags=" | ".join(flags),
770 descriptor=DOMClass(self.descriptor),
774 class CGXrayExpandoJSClass(CGThing):
776 Generate a JSClass for an Xray expando object. This is only
777 needed if we have members in slots (for [Cached] or [StoreInSlot]
778 stuff).
781 def __init__(self, descriptor):
782 assert descriptor.interface.totalMembersInSlots != 0
783 assert descriptor.wantsXrays
784 assert descriptor.wantsXrayExpandoClass
785 CGThing.__init__(self)
786 self.descriptor = descriptor
788 def declare(self):
789 return ""
791 def define(self):
792 return fill(
794 // This may allocate too many slots, because we only really need
795 // slots for our non-interface-typed members that we cache. But
796 // allocating slots only for those would make the slot index
797 // computations much more complicated, so let's do this the simple
798 // way for now.
799 DEFINE_XRAY_EXPANDO_CLASS(static, sXrayExpandoObjectClass, ${memberSlots});
800 """,
801 memberSlots=self.descriptor.interface.totalMembersInSlots,
805 def PrototypeIDAndDepth(descriptor):
806 prototypeID = "prototypes::id::"
807 if descriptor.interface.hasInterfacePrototypeObject():
808 prototypeID += descriptor.interface.identifier.name
809 depth = "PrototypeTraits<%s>::Depth" % prototypeID
810 else:
811 prototypeID += "_ID_Count"
812 depth = "0"
813 return (prototypeID, depth)
816 def InterfacePrototypeObjectProtoGetter(descriptor):
818 Returns a tuple with two elements:
820 1) The name of the function to call to get the prototype to use for the
821 interface prototype object as a JSObject*.
823 2) The name of the function to call to get the prototype to use for the
824 interface prototype object as a JS::Handle<JSObject*> or None if no
825 such function exists.
827 parentProtoName = descriptor.parentPrototypeName
828 if descriptor.hasNamedPropertiesObject:
829 protoGetter = "GetNamedPropertiesObject"
830 protoHandleGetter = None
831 elif parentProtoName is None:
832 if descriptor.interface.getExtendedAttribute("ExceptionClass"):
833 protoGetter = "JS::GetRealmErrorPrototype"
834 elif descriptor.interface.isIteratorInterface():
835 protoGetter = "JS::GetRealmIteratorPrototype"
836 else:
837 protoGetter = "JS::GetRealmObjectPrototype"
838 protoHandleGetter = None
839 else:
840 prefix = toBindingNamespace(parentProtoName)
841 protoGetter = prefix + "::GetProtoObject"
842 protoHandleGetter = prefix + "::GetProtoObjectHandle"
844 return (protoGetter, protoHandleGetter)
847 class CGPrototypeJSClass(CGThing):
848 def __init__(self, descriptor, properties):
849 CGThing.__init__(self)
850 self.descriptor = descriptor
851 self.properties = properties
853 def declare(self):
854 # We're purely for internal consumption
855 return ""
857 def define(self):
858 prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
859 slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE"
860 # Globals handle unforgeables directly in Wrap() instead of
861 # via a holder.
862 if (
863 self.descriptor.hasLegacyUnforgeableMembers
864 and not self.descriptor.isGlobal()
866 slotCount += (
867 " + 1 /* slot for the JSObject holding the unforgeable properties */"
869 (protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor)
870 type = (
871 "eGlobalInterfacePrototype"
872 if self.descriptor.isGlobal()
873 else "eInterfacePrototype"
875 return fill(
877 static const DOMIfaceAndProtoJSClass sPrototypeClass = {
879 "${name}Prototype",
880 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
881 JS_NULL_CLASS_OPS,
882 JS_NULL_CLASS_SPEC,
883 JS_NULL_CLASS_EXT,
884 JS_NULL_OBJECT_OPS
886 ${type},
887 false,
888 ${prototypeID},
889 ${depth},
890 ${hooks},
891 nullptr,
892 ${protoGetter}
894 """,
895 name=self.descriptor.interface.getClassName(),
896 slotCount=slotCount,
897 type=type,
898 hooks=NativePropertyHooks(self.descriptor),
899 prototypeID=prototypeID,
900 depth=depth,
901 protoGetter=protoGetter,
905 def InterfaceObjectProtoGetter(descriptor, forXrays=False):
907 Returns a tuple with two elements:
909 1) The name of the function to call to get the prototype to use for the
910 interface object as a JSObject*.
912 2) The name of the function to call to get the prototype to use for the
913 interface prototype as a JS::Handle<JSObject*> or None if no such
914 function exists.
916 parentInterface = descriptor.interface.parent
917 if parentInterface:
918 assert not descriptor.interface.isNamespace()
919 parentIfaceName = parentInterface.identifier.name
920 parentDesc = descriptor.getDescriptor(parentIfaceName)
921 prefix = toBindingNamespace(parentDesc.name)
922 protoGetter = prefix + "::GetConstructorObject"
923 protoHandleGetter = prefix + "::GetConstructorObjectHandle"
924 elif descriptor.interface.isNamespace():
925 if forXrays or not descriptor.interface.getExtendedAttribute("ProtoObjectHack"):
926 protoGetter = "JS::GetRealmObjectPrototype"
927 else:
928 protoGetter = "GetHackedNamespaceProtoObject"
929 protoHandleGetter = None
930 else:
931 protoGetter = "JS::GetRealmFunctionPrototype"
932 protoHandleGetter = None
933 return (protoGetter, protoHandleGetter)
936 class CGInterfaceObjectJSClass(CGThing):
937 def __init__(self, descriptor, properties):
938 CGThing.__init__(self)
939 self.descriptor = descriptor
940 self.properties = properties
942 def declare(self):
943 # We're purely for internal consumption
944 return ""
946 def define(self):
947 if self.descriptor.interface.ctor():
948 assert not self.descriptor.interface.isNamespace()
949 ctorname = CONSTRUCT_HOOK_NAME
950 elif self.descriptor.interface.isNamespace():
951 ctorname = "nullptr"
952 else:
953 ctorname = "ThrowingConstructor"
954 needsHasInstance = self.descriptor.interface.hasInterfacePrototypeObject()
956 prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
957 slotCount = "DOM_INTERFACE_SLOTS_BASE"
958 if len(self.descriptor.interface.legacyFactoryFunctions) > 0:
959 slotCount += " + %i /* slots for the legacy factory functions */" % len(
960 self.descriptor.interface.legacyFactoryFunctions
962 (protoGetter, _) = InterfaceObjectProtoGetter(self.descriptor, forXrays=True)
964 if ctorname == "ThrowingConstructor":
965 ret = ""
966 classOpsPtr = "&sBoringInterfaceObjectClassClassOps"
967 elif ctorname == "nullptr":
968 ret = ""
969 classOpsPtr = "JS_NULL_CLASS_OPS"
970 else:
971 ret = fill(
973 static const JSClassOps sInterfaceObjectClassOps = {
974 nullptr, /* addProperty */
975 nullptr, /* delProperty */
976 nullptr, /* enumerate */
977 nullptr, /* newEnumerate */
978 nullptr, /* resolve */
979 nullptr, /* mayResolve */
980 nullptr, /* finalize */
981 ${ctorname}, /* call */
982 nullptr, /* hasInstance */
983 ${ctorname}, /* construct */
984 nullptr, /* trace */
987 """,
988 ctorname=ctorname,
990 classOpsPtr = "&sInterfaceObjectClassOps"
992 if self.descriptor.interface.isNamespace():
993 classString = self.descriptor.interface.getExtendedAttribute("ClassString")
994 if classString is None:
995 classString = "Object"
996 else:
997 classString = classString[0]
998 funToString = "nullptr"
999 objectOps = "JS_NULL_OBJECT_OPS"
1000 else:
1001 classString = "Function"
1002 funToString = (
1003 '"function %s() {\\n [native code]\\n}"'
1004 % self.descriptor.interface.identifier.name
1006 # We need non-default ObjectOps so we can actually make
1007 # use of our funToString.
1008 objectOps = "&sInterfaceObjectClassObjectOps"
1010 ret = ret + fill(
1012 static const DOMIfaceAndProtoJSClass sInterfaceObjectClass = {
1014 "${classString}",
1015 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
1016 ${classOpsPtr},
1017 JS_NULL_CLASS_SPEC,
1018 JS_NULL_CLASS_EXT,
1019 ${objectOps}
1021 eInterface,
1022 ${needsHasInstance},
1023 ${prototypeID},
1024 ${depth},
1025 ${hooks},
1026 ${funToString},
1027 ${protoGetter}
1029 """,
1030 classString=classString,
1031 slotCount=slotCount,
1032 classOpsPtr=classOpsPtr,
1033 hooks=NativePropertyHooks(self.descriptor),
1034 objectOps=objectOps,
1035 needsHasInstance=toStringBool(needsHasInstance),
1036 prototypeID=prototypeID,
1037 depth=depth,
1038 funToString=funToString,
1039 protoGetter=protoGetter,
1041 return ret
1044 class CGList(CGThing):
1046 Generate code for a list of GCThings. Just concatenates them together, with
1047 an optional joiner string. "\n" is a common joiner.
1050 def __init__(self, children, joiner=""):
1051 CGThing.__init__(self)
1052 # Make a copy of the kids into a list, because if someone passes in a
1053 # generator we won't be able to both declare and define ourselves, or
1054 # define ourselves more than once!
1055 self.children = list(children)
1056 self.joiner = joiner
1058 def append(self, child):
1059 self.children.append(child)
1061 def prepend(self, child):
1062 self.children.insert(0, child)
1064 def extend(self, kids):
1065 self.children.extend(kids)
1067 def join(self, iterable):
1068 return self.joiner.join(s for s in iterable if len(s) > 0)
1070 def declare(self):
1071 return self.join(
1072 child.declare() for child in self.children if child is not None
1075 def define(self):
1076 return self.join(child.define() for child in self.children if child is not None)
1078 def deps(self):
1079 deps = set()
1080 for child in self.children:
1081 if child is None:
1082 continue
1083 deps = deps.union(child.deps())
1084 return deps
1086 def __len__(self):
1087 return len(self.children)
1090 class CGGeneric(CGThing):
1092 A class that spits out a fixed string into the codegen. Can spit out a
1093 separate string for the declaration too.
1096 def __init__(self, define="", declare=""):
1097 self.declareText = declare
1098 self.defineText = define
1100 def declare(self):
1101 return self.declareText
1103 def define(self):
1104 return self.defineText
1106 def deps(self):
1107 return set()
1110 class CGIndenter(CGThing):
1112 A class that takes another CGThing and generates code that indents that
1113 CGThing by some number of spaces. The default indent is two spaces.
1116 def __init__(self, child, indentLevel=2, declareOnly=False):
1117 assert isinstance(child, CGThing)
1118 CGThing.__init__(self)
1119 self.child = child
1120 self.indentLevel = indentLevel
1121 self.declareOnly = declareOnly
1123 def declare(self):
1124 return indent(self.child.declare(), self.indentLevel)
1126 def define(self):
1127 defn = self.child.define()
1128 if self.declareOnly:
1129 return defn
1130 else:
1131 return indent(defn, self.indentLevel)
1134 class CGWrapper(CGThing):
1136 Generic CGThing that wraps other CGThings with pre and post text.
1139 def __init__(
1140 self,
1141 child,
1142 pre="",
1143 post="",
1144 declarePre=None,
1145 declarePost=None,
1146 definePre=None,
1147 definePost=None,
1148 declareOnly=False,
1149 defineOnly=False,
1150 reindent=False,
1152 CGThing.__init__(self)
1153 self.child = child
1154 self.declarePre = declarePre or pre
1155 self.declarePost = declarePost or post
1156 self.definePre = definePre or pre
1157 self.definePost = definePost or post
1158 self.declareOnly = declareOnly
1159 self.defineOnly = defineOnly
1160 self.reindent = reindent
1162 def declare(self):
1163 if self.defineOnly:
1164 return ""
1165 decl = self.child.declare()
1166 if self.reindent:
1167 decl = self.reindentString(decl, self.declarePre)
1168 return self.declarePre + decl + self.declarePost
1170 def define(self):
1171 if self.declareOnly:
1172 return ""
1173 defn = self.child.define()
1174 if self.reindent:
1175 defn = self.reindentString(defn, self.definePre)
1176 return self.definePre + defn + self.definePost
1178 @staticmethod
1179 def reindentString(stringToIndent, widthString):
1180 # We don't use lineStartDetector because we don't want to
1181 # insert whitespace at the beginning of our _first_ line.
1182 # Use the length of the last line of width string, in case
1183 # it is a multiline string.
1184 lastLineWidth = len(widthString.splitlines()[-1])
1185 return stripTrailingWhitespace(
1186 stringToIndent.replace("\n", "\n" + (" " * lastLineWidth))
1189 def deps(self):
1190 return self.child.deps()
1193 class CGIfWrapper(CGList):
1194 def __init__(self, child, condition):
1195 CGList.__init__(
1196 self,
1198 CGWrapper(
1199 CGGeneric(condition), pre="if (", post=") {\n", reindent=True
1201 CGIndenter(child),
1202 CGGeneric("}\n"),
1207 class CGIfElseWrapper(CGList):
1208 def __init__(self, condition, ifTrue, ifFalse):
1209 CGList.__init__(
1210 self,
1212 CGWrapper(
1213 CGGeneric(condition), pre="if (", post=") {\n", reindent=True
1215 CGIndenter(ifTrue),
1216 CGGeneric("} else {\n"),
1217 CGIndenter(ifFalse),
1218 CGGeneric("}\n"),
1223 class CGElseChain(CGThing):
1225 Concatenate if statements in an if-else-if-else chain.
1228 def __init__(self, children):
1229 self.children = [c for c in children if c is not None]
1231 def declare(self):
1232 assert False
1234 def define(self):
1235 if not self.children:
1236 return ""
1237 s = self.children[0].define()
1238 assert s.endswith("\n")
1239 for child in self.children[1:]:
1240 code = child.define()
1241 assert code.startswith("if") or code.startswith("{")
1242 assert code.endswith("\n")
1243 s = s.rstrip() + " else " + code
1244 return s
1247 class CGTemplatedType(CGWrapper):
1248 def __init__(self, templateName, child, isConst=False, isReference=False):
1249 if isinstance(child, list):
1250 child = CGList(child, ", ")
1251 const = "const " if isConst else ""
1252 pre = "%s%s<" % (const, templateName)
1253 ref = "&" if isReference else ""
1254 post = ">%s" % ref
1255 CGWrapper.__init__(self, child, pre=pre, post=post)
1258 class CGNamespace(CGWrapper):
1259 def __init__(self, namespace, child, declareOnly=False):
1260 pre = "namespace %s {\n" % namespace
1261 post = "} // namespace %s\n" % namespace
1262 CGWrapper.__init__(self, child, pre=pre, post=post, declareOnly=declareOnly)
1264 @staticmethod
1265 def build(namespaces, child, declareOnly=False):
1267 Static helper method to build multiple wrapped namespaces.
1269 if not namespaces:
1270 return CGWrapper(child, declareOnly=declareOnly)
1271 inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly)
1272 return CGNamespace(namespaces[0], inner, declareOnly=declareOnly)
1275 class CGIncludeGuard(CGWrapper):
1277 Generates include guards for a header.
1280 def __init__(self, prefix, child):
1281 """|prefix| is the filename without the extension."""
1282 define = "mozilla_dom_%s_h" % prefix
1283 CGWrapper.__init__(
1284 self,
1285 child,
1286 declarePre="#ifndef %s\n#define %s\n\n" % (define, define),
1287 declarePost="\n#endif // %s\n" % define,
1291 class CGHeaders(CGWrapper):
1293 Generates the appropriate include statements.
1296 def __init__(
1297 self,
1298 descriptors,
1299 dictionaries,
1300 callbacks,
1301 callbackDescriptors,
1302 declareIncludes,
1303 defineIncludes,
1304 prefix,
1305 child,
1306 config=None,
1307 jsImplementedDescriptors=[],
1310 Builds a set of includes to cover |descriptors|.
1312 Also includes the files in |declareIncludes| in the header
1313 file and the files in |defineIncludes| in the .cpp.
1315 |prefix| contains the basename of the file that we generate include
1316 statements for.
1319 # Determine the filenames for which we need headers.
1320 interfaceDeps = [d.interface for d in descriptors]
1321 ancestors = []
1322 for iface in interfaceDeps:
1323 if iface.parent:
1324 # We're going to need our parent's prototype, to use as the
1325 # prototype of our prototype object.
1326 ancestors.append(iface.parent)
1327 # And if we have an interface object, we'll need the nearest
1328 # ancestor with an interface object too, so we can use its
1329 # interface object as the proto of our interface object.
1330 if iface.hasInterfaceObject():
1331 parent = iface.parent
1332 while parent and not parent.hasInterfaceObject():
1333 parent = parent.parent
1334 if parent:
1335 ancestors.append(parent)
1336 interfaceDeps.extend(ancestors)
1338 # Include parent interface headers needed for default toJSON code.
1339 jsonInterfaceParents = []
1340 for desc in descriptors:
1341 if not desc.hasDefaultToJSON:
1342 continue
1343 parent = desc.interface.parent
1344 while parent:
1345 parentDesc = desc.getDescriptor(parent.identifier.name)
1346 if parentDesc.hasDefaultToJSON:
1347 jsonInterfaceParents.append(parentDesc.interface)
1348 parent = parent.parent
1349 interfaceDeps.extend(jsonInterfaceParents)
1351 bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps)
1353 # Grab all the implementation declaration files we need.
1354 implementationIncludes = set(
1355 d.headerFile for d in descriptors if d.needsHeaderInclude()
1358 # Now find all the things we'll need as arguments because we
1359 # need to wrap or unwrap them.
1360 bindingHeaders = set()
1361 declareIncludes = set(declareIncludes)
1363 def addHeadersForType(typeAndPossibleDictionary):
1365 Add the relevant headers for this type. We use dictionary, if
1366 passed, to decide what to do with interface types.
1368 t, dictionary = typeAndPossibleDictionary
1369 # Dictionaries have members that need to be actually
1370 # declared, not just forward-declared.
1371 if dictionary:
1372 headerSet = declareIncludes
1373 else:
1374 headerSet = bindingHeaders
1375 # Strip off outer layers and add headers they might require. (This
1376 # is conservative: only nullable non-pointer types need Nullable.h;
1377 # only sequences outside unions need ForOfIterator.h; only functions
1378 # that return, and attributes that are, sequences in interfaces need
1379 # Array.h, &c.)
1380 unrolled = t
1381 while True:
1382 if idlTypeNeedsCallContext(unrolled):
1383 bindingHeaders.add("mozilla/dom/BindingCallContext.h")
1384 if unrolled.nullable():
1385 headerSet.add("mozilla/dom/Nullable.h")
1386 elif unrolled.isSequence():
1387 bindingHeaders.add("js/Array.h")
1388 bindingHeaders.add("js/ForOfIterator.h")
1389 else:
1390 break
1391 unrolled = unrolled.inner
1392 if unrolled.isUnion():
1393 headerSet.add(self.getUnionDeclarationFilename(config, unrolled))
1394 bindingHeaders.add("mozilla/dom/UnionConversions.h")
1395 for t in unrolled.flatMemberTypes:
1396 addHeadersForType((t, None))
1397 elif unrolled.isPromise():
1398 # See comment in the isInterface() case for why we add
1399 # Promise.h to headerSet, not bindingHeaders.
1400 headerSet.add("mozilla/dom/Promise.h")
1401 # We need ToJSValue to do the Promise to JS conversion.
1402 bindingHeaders.add("mozilla/dom/ToJSValue.h")
1403 elif unrolled.isInterface():
1404 if unrolled.isSpiderMonkeyInterface():
1405 bindingHeaders.add("jsfriendapi.h")
1406 if jsImplementedDescriptors:
1407 # Since we can't forward-declare typed array types
1408 # (because they're typedefs), we have to go ahead and
1409 # just include their header if we need to have functions
1410 # taking references to them declared in that header.
1411 headerSet = declareIncludes
1412 if unrolled.isReadableStream():
1413 headerSet.add("mozilla/dom/ReadableStream.h")
1414 else:
1415 headerSet.add("mozilla/dom/TypedArray.h")
1416 else:
1417 try:
1418 typeDesc = config.getDescriptor(unrolled.inner.identifier.name)
1419 except NoSuchDescriptorError:
1420 return
1421 # Dictionaries with interface members rely on the
1422 # actual class definition of that interface member
1423 # being visible in the binding header, because they
1424 # store them in RefPtr and have inline
1425 # constructors/destructors.
1427 # XXXbz maybe dictionaries with interface members
1428 # should just have out-of-line constructors and
1429 # destructors?
1430 headerSet.add(typeDesc.headerFile)
1431 elif unrolled.isDictionary():
1432 headerSet.add(self.getDeclarationFilename(unrolled.inner))
1433 # And if it needs rooting, we need RootedDictionary too
1434 if typeNeedsRooting(unrolled):
1435 headerSet.add("mozilla/dom/RootedDictionary.h")
1436 elif unrolled.isCallback():
1437 headerSet.add(self.getDeclarationFilename(unrolled.callback))
1438 elif unrolled.isFloat() and not unrolled.isUnrestricted():
1439 # Restricted floats are tested for finiteness
1440 bindingHeaders.add("mozilla/FloatingPoint.h")
1441 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
1442 elif unrolled.isEnum():
1443 filename = self.getDeclarationFilename(unrolled.inner)
1444 declareIncludes.add(filename)
1445 elif unrolled.isPrimitive():
1446 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
1447 elif unrolled.isRecord():
1448 if dictionary or jsImplementedDescriptors:
1449 declareIncludes.add("mozilla/dom/Record.h")
1450 else:
1451 bindingHeaders.add("mozilla/dom/Record.h")
1452 # Also add headers for the type the record is
1453 # parametrized over, if needed.
1454 addHeadersForType((t.inner, dictionary))
1456 for t in getAllTypes(
1457 descriptors + callbackDescriptors, dictionaries, callbacks
1459 addHeadersForType(t)
1461 def addHeaderForFunc(func, desc):
1462 if func is None:
1463 return
1464 # Include the right class header, which we can only do
1465 # if this is a class member function.
1466 if desc is not None and not desc.headerIsDefault:
1467 # An explicit header file was provided, assume that we know
1468 # what we're doing.
1469 return
1471 if "::" in func:
1472 # Strip out the function name and convert "::" to "/"
1473 bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")
1475 # Now for non-callback descriptors make sure we include any
1476 # headers needed by Func declarations and other things like that.
1477 for desc in descriptors:
1478 # If this is an iterator interface generated for a separate
1479 # iterable interface, skip generating type includes, as we have
1480 # what we need in IterableIterator.h
1481 if desc.interface.isIteratorInterface():
1482 continue
1484 for m in desc.interface.members:
1485 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), desc)
1486 staticTypeOverride = PropertyDefiner.getStringAttr(
1487 m, "StaticClassOverride"
1489 if staticTypeOverride:
1490 bindingHeaders.add("/".join(staticTypeOverride.split("::")) + ".h")
1491 # getExtendedAttribute() returns a list, extract the entry.
1492 funcList = desc.interface.getExtendedAttribute("Func")
1493 if funcList is not None:
1494 addHeaderForFunc(funcList[0], desc)
1496 if desc.interface.maplikeOrSetlikeOrIterable:
1497 # We need ToJSValue.h for maplike/setlike type conversions
1498 bindingHeaders.add("mozilla/dom/ToJSValue.h")
1499 # Add headers for the key and value types of the
1500 # maplike/setlike/iterable, since they'll be needed for
1501 # convenience functions
1502 if desc.interface.maplikeOrSetlikeOrIterable.hasKeyType():
1503 addHeadersForType(
1504 (desc.interface.maplikeOrSetlikeOrIterable.keyType, None)
1506 if desc.interface.maplikeOrSetlikeOrIterable.hasValueType():
1507 addHeadersForType(
1508 (desc.interface.maplikeOrSetlikeOrIterable.valueType, None)
1511 for d in dictionaries:
1512 if d.parent:
1513 declareIncludes.add(self.getDeclarationFilename(d.parent))
1514 bindingHeaders.add(self.getDeclarationFilename(d))
1515 for m in d.members:
1516 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"), None)
1517 # No need to worry about Func on members of ancestors, because that
1518 # will happen automatically in whatever files those ancestors live
1519 # in.
1521 for c in callbacks:
1522 bindingHeaders.add(self.getDeclarationFilename(c))
1524 for c in callbackDescriptors:
1525 bindingHeaders.add(self.getDeclarationFilename(c.interface))
1527 if len(callbacks) != 0:
1528 # We need CallbackFunction to serve as our parent class
1529 declareIncludes.add("mozilla/dom/CallbackFunction.h")
1530 # And we need ToJSValue.h so we can wrap "this" objects
1531 declareIncludes.add("mozilla/dom/ToJSValue.h")
1533 if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0:
1534 # We need CallbackInterface to serve as our parent class
1535 declareIncludes.add("mozilla/dom/CallbackInterface.h")
1536 # And we need ToJSValue.h so we can wrap "this" objects
1537 declareIncludes.add("mozilla/dom/ToJSValue.h")
1539 # Also need to include the headers for ancestors of
1540 # JS-implemented interfaces.
1541 for jsImplemented in jsImplementedDescriptors:
1542 jsParent = jsImplemented.interface.parent
1543 if jsParent:
1544 parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name)
1545 declareIncludes.add(parentDesc.jsImplParentHeader)
1547 # Now make sure we're not trying to include the header from inside itself
1548 declareIncludes.discard(prefix + ".h")
1550 # Let the machinery do its thing.
1551 def _includeString(includes):
1552 def headerName(include):
1553 # System headers are specified inside angle brackets.
1554 if include.startswith("<"):
1555 return include
1556 # Non-system headers need to be placed in quotes.
1557 return '"%s"' % include
1559 return "".join(["#include %s\n" % headerName(i) for i in includes]) + "\n"
1561 CGWrapper.__init__(
1562 self,
1563 child,
1564 declarePre=_includeString(sorted(declareIncludes)),
1565 definePre=_includeString(
1566 sorted(
1567 set(defineIncludes)
1568 | bindingIncludes
1569 | bindingHeaders
1570 | implementationIncludes
1575 @staticmethod
1576 def getDeclarationFilename(decl):
1577 # Use our local version of the header, not the exported one, so that
1578 # test bindings, which don't export, will work correctly.
1579 basename = os.path.basename(decl.filename())
1580 return basename.replace(".webidl", "Binding.h")
1582 @staticmethod
1583 def getUnionDeclarationFilename(config, unionType):
1584 assert unionType.isUnion()
1585 assert unionType.unroll() == unionType
1586 # If a union is "defined" in multiple files, it goes in UnionTypes.h.
1587 if len(config.filenamesPerUnion[unionType.name]) > 1:
1588 return "mozilla/dom/UnionTypes.h"
1589 # If a union is defined by a built-in typedef, it also goes in
1590 # UnionTypes.h.
1591 assert len(config.filenamesPerUnion[unionType.name]) == 1
1592 if "<unknown>" in config.filenamesPerUnion[unionType.name]:
1593 return "mozilla/dom/UnionTypes.h"
1594 return CGHeaders.getDeclarationFilename(unionType)
1597 def SortedDictValues(d):
1599 Returns a list of values from the dict sorted by key.
1601 return [v for k, v in sorted(d.items())]
1604 def UnionsForFile(config, webIDLFile):
1606 Returns a list of union types for all union types that are only used in
1607 webIDLFile. If webIDLFile is None this will return the list of tuples for
1608 union types that are used in more than one WebIDL file.
1610 return config.unionsPerFilename.get(webIDLFile, [])
1613 def UnionTypes(unionTypes, config):
1615 The unionTypes argument should be a list of union types. This is typically
1616 the list generated by UnionsForFile.
1618 Returns a tuple containing a set of header filenames to include in
1619 the header for the types in unionTypes, a set of header filenames to
1620 include in the implementation file for the types in unionTypes, a set
1621 of tuples containing a type declaration and a boolean if the type is a
1622 struct for member types of the union, a list of traverse methods,
1623 unlink methods and a list of union types. These last three lists only
1624 contain unique union types.
1627 headers = set()
1628 implheaders = set()
1629 declarations = set()
1630 unionStructs = dict()
1631 traverseMethods = dict()
1632 unlinkMethods = dict()
1634 for t in unionTypes:
1635 name = str(t)
1636 if name not in unionStructs:
1637 unionStructs[name] = t
1639 def addHeadersForType(f):
1640 if f.nullable():
1641 headers.add("mozilla/dom/Nullable.h")
1642 isSequence = f.isSequence()
1643 if isSequence:
1644 # Dealing with sequences requires for-of-compatible
1645 # iteration.
1646 implheaders.add("js/ForOfIterator.h")
1647 # Sequences can always throw "not an object" exceptions.
1648 implheaders.add("mozilla/dom/BindingCallContext.h")
1649 f = f.unroll()
1650 if idlTypeNeedsCallContext(f):
1651 implheaders.add("mozilla/dom/BindingCallContext.h")
1652 if f.isPromise():
1653 headers.add("mozilla/dom/Promise.h")
1654 # We need ToJSValue to do the Promise to JS conversion.
1655 headers.add("mozilla/dom/ToJSValue.h")
1656 elif f.isInterface():
1657 if f.isSpiderMonkeyInterface():
1658 headers.add("js/RootingAPI.h")
1659 headers.add("js/Value.h")
1660 if f.isReadableStream():
1661 headers.add("mozilla/dom/ReadableStream.h")
1662 else:
1663 headers.add("mozilla/dom/TypedArray.h")
1664 else:
1665 try:
1666 typeDesc = config.getDescriptor(f.inner.identifier.name)
1667 except NoSuchDescriptorError:
1668 return
1669 if typeDesc.interface.isCallback() or isSequence:
1670 # Callback interfaces always use strong refs, so
1671 # we need to include the right header to be able
1672 # to Release() in our inlined code.
1674 # Similarly, sequences always contain strong
1675 # refs, so we'll need the header to handler
1676 # those.
1677 headers.add(typeDesc.headerFile)
1678 elif typeDesc.interface.identifier.name == "WindowProxy":
1679 # In UnionTypes.h we need to see the declaration of the
1680 # WindowProxyHolder that we use to store the WindowProxy, so
1681 # we have its sizeof and know how big to make our union.
1682 headers.add(typeDesc.headerFile)
1683 else:
1684 declarations.add((typeDesc.nativeType, False))
1685 implheaders.add(typeDesc.headerFile)
1686 elif f.isDictionary():
1687 # For a dictionary, we need to see its declaration in
1688 # UnionTypes.h so we have its sizeof and know how big to
1689 # make our union.
1690 headers.add(CGHeaders.getDeclarationFilename(f.inner))
1691 # And if it needs rooting, we need RootedDictionary too
1692 if typeNeedsRooting(f):
1693 headers.add("mozilla/dom/RootedDictionary.h")
1694 elif f.isFloat() and not f.isUnrestricted():
1695 # Restricted floats are tested for finiteness
1696 implheaders.add("mozilla/FloatingPoint.h")
1697 implheaders.add("mozilla/dom/PrimitiveConversions.h")
1698 elif f.isEnum():
1699 # Need to see the actual definition of the enum,
1700 # unfortunately.
1701 headers.add(CGHeaders.getDeclarationFilename(f.inner))
1702 elif f.isPrimitive():
1703 implheaders.add("mozilla/dom/PrimitiveConversions.h")
1704 elif f.isCallback():
1705 # Callbacks always use strong refs, so we need to include
1706 # the right header to be able to Release() in our inlined
1707 # code.
1708 headers.add(CGHeaders.getDeclarationFilename(f.callback))
1709 elif f.isRecord():
1710 headers.add("mozilla/dom/Record.h")
1711 # And add headers for the type we're parametrized over
1712 addHeadersForType(f.inner)
1714 implheaders.add(CGHeaders.getUnionDeclarationFilename(config, t))
1715 for f in t.flatMemberTypes:
1716 assert not f.nullable()
1717 addHeadersForType(f)
1719 if idlTypeNeedsCycleCollection(t):
1720 declarations.add(
1721 ("mozilla::dom::%s" % CGUnionStruct.unionTypeName(t, True), False)
1723 traverseMethods[name] = CGCycleCollectionTraverseForOwningUnionMethod(t)
1724 unlinkMethods[name] = CGCycleCollectionUnlinkForOwningUnionMethod(t)
1726 # The order of items in CGList is important.
1727 # Since the union structs friend the unlinkMethods, the forward-declaration
1728 # for these methods should come before the class declaration. Otherwise
1729 # some compilers treat the friend declaration as a forward-declaration in
1730 # the class scope.
1731 return (
1732 headers,
1733 implheaders,
1734 declarations,
1735 SortedDictValues(traverseMethods),
1736 SortedDictValues(unlinkMethods),
1737 SortedDictValues(unionStructs),
1741 def UnionConversions(unionTypes, config):
1743 The unionTypes argument should be a list of tuples, each containing two
1744 elements: a union type and a descriptor. This is typically the list
1745 generated by UnionsForFile.
1747 Returns a tuple containing a list of headers and a CGThing to declare all
1748 union argument conversion helper structs.
1750 headers = set()
1751 unionConversions = dict()
1753 for t in unionTypes:
1754 name = str(t)
1755 if name not in unionConversions:
1756 unionConversions[name] = CGUnionConversionStruct(t, config)
1758 def addHeadersForType(f):
1759 if f.isSequence():
1760 # Sequences require JSAPI C++ for-of iteration code to fill
1761 # them.
1762 headers.add("js/ForOfIterator.h")
1763 # Sequences can always throw "not an object" exceptions.
1764 headers.add("mozilla/dom/BindingCallContext.h")
1765 f = f.unroll()
1766 if idlTypeNeedsCallContext(f):
1767 headers.add("mozilla/dom/BindingCallContext.h")
1768 if f.isPromise():
1769 headers.add("mozilla/dom/Promise.h")
1770 # We need ToJSValue to do the Promise to JS conversion.
1771 headers.add("mozilla/dom/ToJSValue.h")
1772 elif f.isInterface():
1773 if f.isSpiderMonkeyInterface():
1774 headers.add("js/RootingAPI.h")
1775 if f.isReadableStream():
1776 headers.add("mozilla/dom/ReadableStream.h")
1777 else:
1778 headers.add("mozilla/dom/TypedArray.h")
1779 elif f.inner.isExternal():
1780 try:
1781 typeDesc = config.getDescriptor(f.inner.identifier.name)
1782 except NoSuchDescriptorError:
1783 return
1784 headers.add(typeDesc.headerFile)
1785 else:
1786 headers.add(CGHeaders.getDeclarationFilename(f.inner))
1787 elif f.isDictionary():
1788 headers.add(CGHeaders.getDeclarationFilename(f.inner))
1789 elif f.isFloat() and not f.isUnrestricted():
1790 # Restricted floats are tested for finiteness
1791 headers.add("mozilla/FloatingPoint.h")
1792 headers.add("mozilla/dom/PrimitiveConversions.h")
1793 elif f.isPrimitive():
1794 headers.add("mozilla/dom/PrimitiveConversions.h")
1795 elif f.isRecord():
1796 headers.add("mozilla/dom/Record.h")
1797 # And the internal type of the record
1798 addHeadersForType(f.inner)
1800 # We plan to include UnionTypes.h no matter what, so it's
1801 # OK if we throw it into the set here.
1802 headers.add(CGHeaders.getUnionDeclarationFilename(config, t))
1804 for f in t.flatMemberTypes:
1805 addHeadersForType(f)
1807 return (
1808 headers,
1809 CGWrapper(CGList(SortedDictValues(unionConversions), "\n"), post="\n\n"),
1813 class Argument:
1815 A class for outputting the type and name of an argument
1818 def __init__(self, argType, name, default=None):
1819 self.argType = argType
1820 self.name = name
1821 self.default = default
1823 def declare(self):
1824 string = self.argType + " " + self.name
1825 if self.default is not None:
1826 string += " = " + self.default
1827 return string
1829 def define(self):
1830 return self.argType + " " + self.name
1833 class CGAbstractMethod(CGThing):
1835 An abstract class for generating code for a method. Subclasses
1836 should override definition_body to create the actual code.
1838 descriptor is the descriptor for the interface the method is associated with
1840 name is the name of the method as a string
1842 returnType is the IDLType of the return value
1844 args is a list of Argument objects
1846 inline should be True to generate an inline method, whose body is
1847 part of the declaration.
1849 alwaysInline should be True to generate an inline method annotated with
1850 MOZ_ALWAYS_INLINE.
1852 static should be True to generate a static method, which only has
1853 a definition.
1855 If templateArgs is not None it should be a list of strings containing
1856 template arguments, and the function will be templatized using those
1857 arguments.
1859 canRunScript should be True to generate a MOZ_CAN_RUN_SCRIPT annotation.
1862 def __init__(
1863 self,
1864 descriptor,
1865 name,
1866 returnType,
1867 args,
1868 inline=False,
1869 alwaysInline=False,
1870 static=False,
1871 templateArgs=None,
1872 canRunScript=False,
1874 CGThing.__init__(self)
1875 self.descriptor = descriptor
1876 self.name = name
1877 self.returnType = returnType
1878 self.args = args
1879 self.inline = inline
1880 self.alwaysInline = alwaysInline
1881 self.static = static
1882 self.templateArgs = templateArgs
1883 self.canRunScript = canRunScript
1885 def _argstring(self, declare):
1886 return ", ".join([a.declare() if declare else a.define() for a in self.args])
1888 def _template(self):
1889 if self.templateArgs is None:
1890 return ""
1891 return "template <%s>\n" % ", ".join(self.templateArgs)
1893 def _decorators(self):
1894 decorators = []
1895 if self.canRunScript:
1896 decorators.append("MOZ_CAN_RUN_SCRIPT")
1897 if self.alwaysInline:
1898 decorators.append("MOZ_ALWAYS_INLINE")
1899 elif self.inline:
1900 decorators.append("inline")
1901 if self.static:
1902 decorators.append("static")
1903 decorators.append(self.returnType)
1904 maybeNewline = " " if self.inline else "\n"
1905 return " ".join(decorators) + maybeNewline
1907 def declare(self):
1908 if self.inline:
1909 return self._define(True)
1910 return "%s%s%s(%s);\n" % (
1911 self._template(),
1912 self._decorators(),
1913 self.name,
1914 self._argstring(True),
1917 def indent_body(self, body):
1919 Indent the code returned by self.definition_body(). Most classes
1920 simply indent everything two spaces. This is here for
1921 CGRegisterProtos, which needs custom indentation.
1923 return indent(body)
1925 def _define(self, fromDeclare=False):
1926 return (
1927 self.definition_prologue(fromDeclare)
1928 + self.indent_body(self.definition_body())
1929 + self.definition_epilogue()
1932 def define(self):
1933 return "" if self.inline else self._define()
1935 def definition_prologue(self, fromDeclare):
1936 error_reporting_label = self.error_reporting_label()
1937 if error_reporting_label:
1938 # We're going to want a BindingCallContext. Rename our JSContext*
1939 # arg accordingly.
1940 i = 0
1941 while i < len(self.args):
1942 arg = self.args[i]
1943 if arg.argType == "JSContext*":
1944 cxname = arg.name
1945 self.args[i] = Argument(arg.argType, "cx_", arg.default)
1946 break
1947 i += 1
1948 if i == len(self.args):
1949 raise TypeError("Must have a JSContext* to create a BindingCallContext")
1951 prologue = "%s%s%s(%s)\n{\n" % (
1952 self._template(),
1953 self._decorators(),
1954 self.name,
1955 self._argstring(fromDeclare),
1957 if error_reporting_label:
1958 prologue += indent(
1959 fill(
1961 BindingCallContext ${cxname}(cx_, "${label}");
1962 """,
1963 cxname=cxname,
1964 label=error_reporting_label,
1968 profiler_label = self.auto_profiler_label()
1969 if profiler_label:
1970 prologue += indent(profiler_label) + "\n"
1972 return prologue
1974 def definition_epilogue(self):
1975 return "}\n"
1977 def definition_body(self):
1978 assert False # Override me!
1981 Override this method to return a pair of (descriptive string, name of a
1982 JSContext* variable) in order to generate a profiler label for this method.
1985 def auto_profiler_label(self):
1986 return None # Override me!
1989 Override this method to return a string to be used as the label for a
1990 BindingCallContext. If this does not return None, one of the arguments of
1991 this method must be of type 'JSContext*'. Its name will be replaced with
1992 'cx_' and a BindingCallContext named 'cx' will be instantiated with the
1993 given label.
1996 def error_reporting_label(self):
1997 return None # Override me!
2000 class CGAbstractStaticMethod(CGAbstractMethod):
2002 Abstract base class for codegen of implementation-only (no
2003 declaration) static methods.
2006 def __init__(self, descriptor, name, returnType, args, canRunScript=False):
2007 CGAbstractMethod.__init__(
2008 self,
2009 descriptor,
2010 name,
2011 returnType,
2012 args,
2013 inline=False,
2014 static=True,
2015 canRunScript=canRunScript,
2018 def declare(self):
2019 # We only have implementation
2020 return ""
2023 class CGAbstractClassHook(CGAbstractStaticMethod):
2025 Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
2026 'this' unwrapping as it assumes that the unwrapped type is always known.
2029 def __init__(self, descriptor, name, returnType, args):
2030 CGAbstractStaticMethod.__init__(self, descriptor, name, returnType, args)
2032 def definition_body_prologue(self):
2033 return "%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" % (
2034 self.descriptor.nativeType,
2035 self.descriptor.nativeType,
2038 def definition_body(self):
2039 return self.definition_body_prologue() + self.generate_code()
2041 def generate_code(self):
2042 assert False # Override me!
2045 class CGGetJSClassMethod(CGAbstractMethod):
2046 def __init__(self, descriptor):
2047 CGAbstractMethod.__init__(self, descriptor, "GetJSClass", "const JSClass*", [])
2049 def definition_body(self):
2050 return "return sClass.ToJSClass();\n"
2053 class CGAddPropertyHook(CGAbstractClassHook):
2055 A hook for addProperty, used to preserve our wrapper from GC.
2058 def __init__(self, descriptor):
2059 args = [
2060 Argument("JSContext*", "cx"),
2061 Argument("JS::Handle<JSObject*>", "obj"),
2062 Argument("JS::Handle<jsid>", "id"),
2063 Argument("JS::Handle<JS::Value>", "val"),
2065 CGAbstractClassHook.__init__(
2066 self, descriptor, ADDPROPERTY_HOOK_NAME, "bool", args
2069 def generate_code(self):
2070 assert self.descriptor.wrapperCache
2071 # This hook is also called by TryPreserveWrapper on non-nsISupports
2072 # cycle collected objects, so if addProperty is ever changed to do
2073 # anything more or less than preserve the wrapper, TryPreserveWrapper
2074 # will need to be changed.
2075 return dedent(
2077 // We don't want to preserve if we don't have a wrapper, and we
2078 // obviously can't preserve if we're not initialized.
2079 if (self && self->GetWrapperPreserveColor()) {
2080 PreserveWrapper(self);
2082 return true;
2087 class CGGetWrapperCacheHook(CGAbstractClassHook):
2089 A hook for GetWrapperCache, used by HasReleasedWrapper to get the
2090 nsWrapperCache pointer for a non-nsISupports object.
2093 def __init__(self, descriptor):
2094 args = [Argument("JS::Handle<JSObject*>", "obj")]
2095 CGAbstractClassHook.__init__(
2096 self, descriptor, GETWRAPPERCACHE_HOOK_NAME, "nsWrapperCache*", args
2099 def generate_code(self):
2100 assert self.descriptor.wrapperCache
2101 return dedent(
2103 return self;
2108 def finalizeHook(descriptor, hookName, freeOp, obj):
2109 finalize = "JS::SetReservedSlot(%s, DOM_OBJECT_SLOT, JS::UndefinedValue());\n" % obj
2110 if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
2111 finalize += fill(
2113 // Either our proxy created an expando object or not. If it did,
2114 // then we would have preserved ourselves, and hence if we're going
2115 // away so is our C++ object and we should reset its expando value.
2116 // It's possible that in this situation the C++ object's reflector
2117 // pointer has been nulled out, but if not it's pointing to us. If
2118 // our proxy did _not_ create an expando object then it's possible
2119 // that we're no longer the reflector for our C++ object (and
2120 // incremental finalization is finally getting to us), and that in
2121 // the meantime the new reflector has created an expando object.
2122 // In that case we do NOT want to clear the expando pointer in the
2123 // C++ object.
2125 // It's important to do this before we ClearWrapper, of course.
2126 JSObject* reflector = self->GetWrapperMaybeDead();
2127 if (!reflector || reflector == ${obj}) {
2128 self->mExpandoAndGeneration.expando = JS::UndefinedValue();
2130 """,
2131 obj=obj,
2133 if descriptor.wrapperCache:
2134 finalize += "ClearWrapper(self, self, %s);\n" % obj
2135 if descriptor.isGlobal():
2136 finalize += "mozilla::dom::FinalizeGlobal(%s, %s);\n" % (freeOp, obj)
2137 finalize += fill(
2139 if (size_t mallocBytes = BindingJSObjectMallocBytes(self)) {
2140 JS::RemoveAssociatedMemory(${obj}, mallocBytes,
2141 JS::MemoryUse::DOMBinding);
2143 """,
2144 obj=obj,
2146 finalize += "AddForDeferredFinalization<%s>(self);\n" % descriptor.nativeType
2147 return CGIfWrapper(CGGeneric(finalize), "self")
2150 class CGClassFinalizeHook(CGAbstractClassHook):
2152 A hook for finalize, used to release our native object.
2155 def __init__(self, descriptor):
2156 args = [Argument("JSFreeOp*", "fop"), Argument("JSObject*", "obj")]
2157 CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, "void", args)
2159 def generate_code(self):
2160 return finalizeHook(
2161 self.descriptor, self.name, self.args[0].name, self.args[1].name
2162 ).define()
2165 def objectMovedHook(descriptor, hookName, obj, old):
2166 assert descriptor.wrapperCache
2167 return fill(
2169 if (self) {
2170 UpdateWrapper(self, self, ${obj}, ${old});
2173 return 0;
2174 """,
2175 obj=obj,
2176 old=old,
2180 class CGClassObjectMovedHook(CGAbstractClassHook):
2182 A hook for objectMovedOp, used to update the wrapper cache when an object it
2183 is holding moves.
2186 def __init__(self, descriptor):
2187 args = [Argument("JSObject*", "obj"), Argument("JSObject*", "old")]
2188 CGAbstractClassHook.__init__(
2189 self, descriptor, OBJECT_MOVED_HOOK_NAME, "size_t", args
2192 def generate_code(self):
2193 return objectMovedHook(
2194 self.descriptor, self.name, self.args[0].name, self.args[1].name
2198 def JSNativeArguments():
2199 return [
2200 Argument("JSContext*", "cx"),
2201 Argument("unsigned", "argc"),
2202 Argument("JS::Value*", "vp"),
2206 class CGClassConstructor(CGAbstractStaticMethod):
2208 JS-visible constructor for our objects
2211 def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
2212 CGAbstractStaticMethod.__init__(
2213 self, descriptor, name, "bool", JSNativeArguments()
2215 self._ctor = ctor
2217 def define(self):
2218 if not self._ctor:
2219 return ""
2220 return CGAbstractStaticMethod.define(self)
2222 def definition_body(self):
2223 return self.generate_code()
2225 def generate_code(self):
2226 if self._ctor.isHTMLConstructor():
2227 # We better have a prototype object. Otherwise our proto
2228 # id won't make sense.
2229 assert self.descriptor.interface.hasInterfacePrototypeObject()
2230 # We also better have a constructor object, if this is
2231 # getting called!
2232 assert self.descriptor.interface.hasInterfaceObject()
2233 # We can't just pass null for the CreateInterfaceObjects callback,
2234 # because our newTarget might be in a different compartment, in
2235 # which case we'll need to look up constructor objects in that
2236 # compartment.
2237 return fill(
2239 return HTMLConstructor(cx, argc, vp,
2240 constructors::id::${name},
2241 prototypes::id::${name},
2242 CreateInterfaceObjects);
2243 """,
2244 name=self.descriptor.name,
2247 # If the interface is already SecureContext, notify getConditionList to skip that check,
2248 # because the constructor won't be exposed in non-secure contexts to start with.
2249 alreadySecureContext = self.descriptor.interface.getExtendedAttribute(
2250 "SecureContext"
2253 # We want to throw if any of the conditions returned by getConditionList are false.
2254 conditionsCheck = ""
2255 rawConditions = getRawConditionList(
2256 self._ctor, "cx", "obj", alreadySecureContext
2258 if len(rawConditions) > 0:
2259 notConditions = " ||\n".join("!" + cond for cond in rawConditions)
2260 failedCheckAction = CGGeneric("return ThrowingConstructor(cx, argc, vp);\n")
2261 conditionsCheck = (
2262 CGIfWrapper(failedCheckAction, notConditions).define() + "\n"
2265 # Additionally, we want to throw if a caller does a bareword invocation
2266 # of a constructor without |new|.
2267 ctorName = GetConstructorNameForReporting(self.descriptor, self._ctor)
2269 preamble = fill(
2271 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
2272 JS::Rooted<JSObject*> obj(cx, &args.callee());
2273 $*{conditionsCheck}
2274 if (!args.isConstructing()) {
2275 return ThrowConstructorWithoutNew(cx, "${ctorName}");
2278 JS::Rooted<JSObject*> desiredProto(cx);
2279 if (!GetDesiredProto(cx, args,
2280 prototypes::id::${name},
2281 CreateInterfaceObjects,
2282 &desiredProto)) {
2283 return false;
2285 """,
2286 conditionsCheck=conditionsCheck,
2287 ctorName=ctorName,
2288 name=self.descriptor.name,
2291 name = self._ctor.identifier.name
2292 nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
2293 callGenerator = CGMethodCall(
2294 nativeName, True, self.descriptor, self._ctor, isConstructor=True
2296 return preamble + "\n" + callGenerator.define()
2298 def auto_profiler_label(self):
2299 return fill(
2301 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
2302 "${ctorName}", "constructor", DOM, cx,
2303 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
2304 """,
2305 ctorName=GetConstructorNameForReporting(self.descriptor, self._ctor),
2308 def error_reporting_label(self):
2309 return CGSpecializedMethod.error_reporting_label_helper(
2310 self.descriptor, self._ctor, isConstructor=True
2314 def LegacyFactoryFunctionName(m):
2315 return "_" + m.identifier.name
2318 class CGLegacyFactoryFunctions(CGThing):
2319 def __init__(self, descriptor):
2320 self.descriptor = descriptor
2321 CGThing.__init__(self)
2323 def declare(self):
2324 return ""
2326 def define(self):
2327 if len(self.descriptor.interface.legacyFactoryFunctions) == 0:
2328 return ""
2330 constructorID = "constructors::id::"
2331 if self.descriptor.interface.hasInterfaceObject():
2332 constructorID += self.descriptor.name
2333 else:
2334 constructorID += "_ID_Count"
2336 namedConstructors = ""
2337 for n in self.descriptor.interface.legacyFactoryFunctions:
2338 namedConstructors += (
2339 '{ "%s", { %s, &sLegacyFactoryFunctionNativePropertyHooks }, %i },\n'
2340 % (n.identifier.name, LegacyFactoryFunctionName(n), methodLength(n))
2343 return fill(
2345 const NativePropertyHooks sLegacyFactoryFunctionNativePropertyHooks = {
2346 nullptr,
2347 nullptr,
2348 nullptr,
2349 { nullptr, nullptr },
2350 prototypes::id::${name},
2351 ${constructorID},
2352 nullptr
2355 static const LegacyFactoryFunction namedConstructors[] = {
2356 $*{namedConstructors}
2357 { nullptr, { nullptr, nullptr }, 0 }
2359 """,
2360 name=self.descriptor.name,
2361 constructorID=constructorID,
2362 namedConstructors=namedConstructors,
2366 def isChromeOnly(m):
2367 return m.getExtendedAttribute("ChromeOnly")
2370 def prefIdentifier(pref):
2371 return pref.replace(".", "_").replace("-", "_")
2374 def prefHeader(pref):
2375 return "mozilla/StaticPrefs_%s.h" % pref.partition(".")[0]
2378 class MemberCondition:
2380 An object representing the condition for a member to actually be
2381 exposed. Any of the arguments can be None. If not
2382 None, they should have the following types:
2384 pref: The name of the preference.
2385 func: The name of the function.
2386 secureContext: A bool indicating whether a secure context is required.
2387 nonExposedGlobals: A set of names of globals. Can be empty, in which case
2388 it's treated the same way as None.
2391 def __init__(
2392 self, pref=None, func=None, secureContext=False, nonExposedGlobals=None
2394 assert pref is None or isinstance(pref, str)
2395 assert func is None or isinstance(func, str)
2396 assert isinstance(secureContext, bool)
2397 assert nonExposedGlobals is None or isinstance(nonExposedGlobals, set)
2398 self.pref = pref
2399 if self.pref:
2400 identifier = prefIdentifier(self.pref)
2401 self.prefFuncIndex = "WebIDLPrefIndex::" + identifier
2402 else:
2403 self.prefFuncIndex = "WebIDLPrefIndex::NoPref"
2405 self.secureContext = secureContext
2407 def toFuncPtr(val):
2408 if val is None:
2409 return "nullptr"
2410 return "&" + val
2412 self.func = toFuncPtr(func)
2414 if nonExposedGlobals:
2415 # Nonempty set
2416 self.nonExposedGlobals = " | ".join(
2417 map(lambda g: "GlobalNames::%s" % g, sorted(nonExposedGlobals))
2419 else:
2420 self.nonExposedGlobals = "0"
2422 def __eq__(self, other):
2423 return (
2424 self.pref == other.pref
2425 and self.func == other.func
2426 and self.secureContext == other.secureContext
2427 and self.nonExposedGlobals == other.nonExposedGlobals
2430 def __ne__(self, other):
2431 return not self.__eq__(other)
2433 def hasDisablers(self):
2434 return (
2435 self.pref is not None
2436 or self.secureContext
2437 or self.func != "nullptr"
2438 or self.nonExposedGlobals != "0"
2442 class PropertyDefiner:
2444 A common superclass for defining things on prototype objects.
2446 Subclasses should implement generateArray to generate the actual arrays of
2447 things we're defining. They should also set self.chrome to the list of
2448 things only exposed to chrome and self.regular to the list of things exposed
2449 to both chrome and web pages.
2452 def __init__(self, descriptor, name):
2453 self.descriptor = descriptor
2454 self.name = name
2456 def hasChromeOnly(self):
2457 return len(self.chrome) > 0
2459 def hasNonChromeOnly(self):
2460 return len(self.regular) > 0
2462 def variableName(self, chrome):
2463 if chrome:
2464 if self.hasChromeOnly():
2465 return "sChrome" + self.name
2466 else:
2467 if self.hasNonChromeOnly():
2468 return "s" + self.name
2469 return "nullptr"
2471 def usedForXrays(self):
2472 return self.descriptor.wantsXrays
2474 def length(self, chrome):
2475 return len(self.chrome) if chrome else len(self.regular)
2477 def __str__(self):
2478 # We only need to generate id arrays for things that will end
2479 # up used via ResolveProperty or EnumerateProperties.
2480 str = self.generateArray(self.regular, self.variableName(False))
2481 if self.hasChromeOnly():
2482 str += self.generateArray(self.chrome, self.variableName(True))
2483 return str
2485 @staticmethod
2486 def getStringAttr(member, name):
2487 attr = member.getExtendedAttribute(name)
2488 if attr is None:
2489 return None
2490 # It's a list of strings
2491 assert len(attr) == 1
2492 assert attr[0] is not None
2493 return attr[0]
2495 @staticmethod
2496 def getControllingCondition(interfaceMember, descriptor):
2497 interface = descriptor.interface
2498 nonExposureSet = interface.exposureSet - interfaceMember.exposureSet
2500 return MemberCondition(
2501 PropertyDefiner.getStringAttr(interfaceMember, "Pref"),
2502 PropertyDefiner.getStringAttr(interfaceMember, "Func"),
2503 interfaceMember.getExtendedAttribute("SecureContext") is not None,
2504 nonExposureSet,
2507 @staticmethod
2508 def generatePrefableArrayValues(
2509 array,
2510 descriptor,
2511 specFormatter,
2512 specTerminator,
2513 getCondition,
2514 getDataTuple,
2515 switchToCondition=None,
2518 This method generates an array of spec entries for interface members. It returns
2519 a tuple containing the array of spec entries and the maximum of the number of
2520 spec entries per condition.
2522 array is an array of interface members.
2524 descriptor is the descriptor for the interface that array contains members of.
2526 specFormatter is a function that takes a single argument, a tuple,
2527 and returns a string, a spec array entry.
2529 specTerminator is a terminator for the spec array (inserted every time
2530 our controlling pref changes and at the end of the array).
2532 getCondition is a callback function that takes an array entry and
2533 returns the corresponding MemberCondition.
2535 getDataTuple is a callback function that takes an array entry and
2536 returns a tuple suitable to be passed to specFormatter.
2538 switchToCondition is a function that takes a MemberCondition and an array of
2539 previously generated spec entries. If None is passed for this function then all
2540 the interface members should return the same value from getCondition.
2543 def unsupportedSwitchToCondition(condition, specs):
2544 # If no specs have been added yet then this is just the first call to
2545 # switchToCondition that we call to avoid putting a specTerminator at the
2546 # front of the list.
2547 if len(specs) == 0:
2548 return
2549 raise "Not supported"
2551 if switchToCondition is None:
2552 switchToCondition = unsupportedSwitchToCondition
2554 specs = []
2555 numSpecsInCurPrefable = 0
2556 maxNumSpecsInPrefable = 0
2558 # So we won't put a specTerminator at the very front of the list:
2559 lastCondition = getCondition(array[0], descriptor)
2561 switchToCondition(lastCondition, specs)
2563 for member in array:
2564 curCondition = getCondition(member, descriptor)
2565 if lastCondition != curCondition:
2566 # Terminate previous list
2567 specs.append(specTerminator)
2568 if numSpecsInCurPrefable > maxNumSpecsInPrefable:
2569 maxNumSpecsInPrefable = numSpecsInCurPrefable
2570 numSpecsInCurPrefable = 0
2571 # And switch to our new condition
2572 switchToCondition(curCondition, specs)
2573 lastCondition = curCondition
2574 # And the actual spec
2575 specs.append(specFormatter(getDataTuple(member, descriptor)))
2576 numSpecsInCurPrefable += 1
2577 if numSpecsInCurPrefable > maxNumSpecsInPrefable:
2578 maxNumSpecsInPrefable = numSpecsInCurPrefable
2579 specs.append(specTerminator)
2581 return (specs, maxNumSpecsInPrefable)
2583 def generatePrefableArray(
2584 self,
2585 array,
2586 name,
2587 specFormatter,
2588 specTerminator,
2589 specType,
2590 getCondition,
2591 getDataTuple,
2594 This method generates our various arrays.
2596 array is an array of interface members as passed to generateArray
2598 name is the name as passed to generateArray
2600 specFormatter is a function that takes a single argument, a tuple,
2601 and returns a string, a spec array entry
2603 specTerminator is a terminator for the spec array (inserted every time
2604 our controlling pref changes and at the end of the array)
2606 specType is the actual typename of our spec
2608 getCondition is a callback function that takes an array entry and
2609 returns the corresponding MemberCondition.
2611 getDataTuple is a callback function that takes an array entry and
2612 returns a tuple suitable to be passed to specFormatter.
2615 # We want to generate a single list of specs, but with specTerminator
2616 # inserted at every point where the pref name controlling the member
2617 # changes. That will make sure the order of the properties as exposed
2618 # on the interface and interface prototype objects does not change when
2619 # pref control is added to members while still allowing us to define all
2620 # the members in the smallest number of JSAPI calls.
2621 assert len(array) != 0
2623 disablers = []
2624 prefableSpecs = []
2626 disablersTemplate = dedent(
2628 static const PrefableDisablers %s_disablers%d = {
2629 %s, %s, %s, %s
2633 prefableWithDisablersTemplate = " { &%s_disablers%d, &%s_specs[%d] }"
2634 prefableWithoutDisablersTemplate = " { nullptr, &%s_specs[%d] }"
2635 prefCacheTemplate = "&%s[%d].disablers->enabled"
2637 def switchToCondition(condition, specs):
2638 # Set up pointers to the new sets of specs inside prefableSpecs
2639 if condition.hasDisablers():
2640 prefableSpecs.append(
2641 prefableWithDisablersTemplate % (name, len(specs), name, len(specs))
2643 disablers.append(
2644 disablersTemplate
2646 name,
2647 len(specs),
2648 condition.prefFuncIndex,
2649 toStringBool(condition.secureContext),
2650 condition.nonExposedGlobals,
2651 condition.func,
2654 else:
2655 prefableSpecs.append(
2656 prefableWithoutDisablersTemplate % (name, len(specs))
2659 specs, maxNumSpecsInPrefable = self.generatePrefableArrayValues(
2660 array,
2661 self.descriptor,
2662 specFormatter,
2663 specTerminator,
2664 getCondition,
2665 getDataTuple,
2666 switchToCondition,
2668 prefableSpecs.append(" { nullptr, nullptr }")
2670 specType = "const " + specType
2671 arrays = fill(
2673 // We deliberately use brace-elision to make Visual Studio produce better initalization code.
2674 static ${specType} ${name}_specs[] = {
2675 ${specs}
2678 ${disablers}
2679 static const Prefable<${specType}> ${name}[] = {
2680 ${prefableSpecs}
2683 """,
2684 specType=specType,
2685 name=name,
2686 disablers="\n".join(disablers),
2687 specs=",\n".join(specs),
2688 prefableSpecs=",\n".join(prefableSpecs),
2691 if self.usedForXrays():
2692 arrays = fill(
2694 $*{arrays}
2695 static_assert(${numPrefableSpecs} <= 1ull << NUM_BITS_PROPERTY_INFO_PREF_INDEX,
2696 "We have a prefable index that is >= (1 << NUM_BITS_PROPERTY_INFO_PREF_INDEX)");
2697 static_assert(${maxNumSpecsInPrefable} <= 1ull << NUM_BITS_PROPERTY_INFO_SPEC_INDEX,
2698 "We have a spec index that is >= (1 << NUM_BITS_PROPERTY_INFO_SPEC_INDEX)");
2700 """,
2701 arrays=arrays,
2702 # Minus 1 because there's a list terminator in prefableSpecs.
2703 numPrefableSpecs=len(prefableSpecs) - 1,
2704 maxNumSpecsInPrefable=maxNumSpecsInPrefable,
2707 return arrays
2710 # The length of a method is the minimum of the lengths of the
2711 # argument lists of all its overloads.
2712 def overloadLength(arguments):
2713 i = len(arguments)
2714 while i > 0 and arguments[i - 1].optional:
2715 i -= 1
2716 return i
2719 def methodLength(method):
2720 signatures = method.signatures()
2721 return min(overloadLength(arguments) for retType, arguments in signatures)
2724 def clearableCachedAttrs(descriptor):
2725 return (
2727 for m in descriptor.interface.members
2728 if m.isAttr() and
2729 # Constants should never need clearing!
2730 m.dependsOn != "Nothing" and m.slotIndices is not None
2734 def MakeClearCachedValueNativeName(member):
2735 return "ClearCached%sValue" % MakeNativeName(member.identifier.name)
2738 def IDLToCIdentifier(name):
2739 return name.replace("-", "_")
2742 def EnumerabilityFlags(member):
2743 if member.getExtendedAttribute("NonEnumerable"):
2744 return "0"
2745 return "JSPROP_ENUMERATE"
2748 class MethodDefiner(PropertyDefiner):
2750 A class for defining methods on a prototype object.
2753 def __init__(self, descriptor, name, crossOriginOnly, static, unforgeable=False):
2754 assert not (static and unforgeable)
2755 PropertyDefiner.__init__(self, descriptor, name)
2757 # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
2758 # We should be able to check for special operations without an
2759 # identifier. For now we check if the name starts with __
2761 # Ignore non-static methods for interfaces without a proto object
2762 if descriptor.interface.hasInterfacePrototypeObject() or static:
2763 methods = [
2765 for m in descriptor.interface.members
2766 if m.isMethod()
2767 and m.isStatic() == static
2768 and MemberIsLegacyUnforgeable(m, descriptor) == unforgeable
2769 and (
2770 not crossOriginOnly or m.getExtendedAttribute("CrossOriginCallable")
2772 and not m.isIdentifierLess()
2773 and not m.getExtendedAttribute("Unexposed")
2775 else:
2776 methods = []
2777 self.chrome = []
2778 self.regular = []
2779 for m in methods:
2780 method = self.methodData(m, descriptor)
2782 if m.isStatic():
2783 method["nativeName"] = CppKeywords.checkMethodName(
2784 IDLToCIdentifier(m.identifier.name)
2787 if isChromeOnly(m):
2788 self.chrome.append(method)
2789 else:
2790 self.regular.append(method)
2792 # TODO: Once iterable is implemented, use tiebreak rules instead of
2793 # failing. Also, may be more tiebreak rules to implement once spec bug
2794 # is resolved.
2795 # https://www.w3.org/Bugs/Public/show_bug.cgi?id=28592
2796 def hasIterator(methods, regular):
2797 return any("@@iterator" in m.aliases for m in methods) or any(
2798 "@@iterator" == r["name"] for r in regular
2801 # Check whether we need to output an @@iterator due to having an indexed
2802 # getter. We only do this while outputting non-static and
2803 # non-unforgeable methods, since the @@iterator function will be
2804 # neither.
2805 if not static and not unforgeable and descriptor.supportsIndexedProperties():
2806 if hasIterator(methods, self.regular):
2807 raise TypeError(
2808 "Cannot have indexed getter/attr on "
2809 "interface %s with other members "
2810 "that generate @@iterator, such as "
2811 "maplike/setlike or aliased functions."
2812 % self.descriptor.interface.identifier.name
2814 self.regular.append(
2816 "name": "@@iterator",
2817 "methodInfo": False,
2818 "selfHostedName": "$ArrayValues",
2819 "length": 0,
2820 "flags": "0", # Not enumerable, per spec.
2821 "condition": MemberCondition(),
2825 # Generate the keys/values/entries aliases for value iterables.
2826 maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
2827 if (
2828 not static
2829 and not unforgeable
2830 and maplikeOrSetlikeOrIterable
2831 and maplikeOrSetlikeOrIterable.isIterable()
2832 and maplikeOrSetlikeOrIterable.isValueIterator()
2834 # Add our keys/values/entries/forEach
2835 self.regular.append(
2837 "name": "keys",
2838 "methodInfo": False,
2839 "selfHostedName": "ArrayKeys",
2840 "length": 0,
2841 "flags": "JSPROP_ENUMERATE",
2842 "condition": PropertyDefiner.getControllingCondition(
2843 maplikeOrSetlikeOrIterable, descriptor
2847 self.regular.append(
2849 "name": "values",
2850 "methodInfo": False,
2851 "selfHostedName": "$ArrayValues",
2852 "length": 0,
2853 "flags": "JSPROP_ENUMERATE",
2854 "condition": PropertyDefiner.getControllingCondition(
2855 maplikeOrSetlikeOrIterable, descriptor
2859 self.regular.append(
2861 "name": "entries",
2862 "methodInfo": False,
2863 "selfHostedName": "ArrayEntries",
2864 "length": 0,
2865 "flags": "JSPROP_ENUMERATE",
2866 "condition": PropertyDefiner.getControllingCondition(
2867 maplikeOrSetlikeOrIterable, descriptor
2871 self.regular.append(
2873 "name": "forEach",
2874 "methodInfo": False,
2875 "selfHostedName": "ArrayForEach",
2876 "length": 1,
2877 "flags": "JSPROP_ENUMERATE",
2878 "condition": PropertyDefiner.getControllingCondition(
2879 maplikeOrSetlikeOrIterable, descriptor
2884 if not static:
2885 stringifier = descriptor.operations["Stringifier"]
2886 if stringifier and unforgeable == MemberIsLegacyUnforgeable(
2887 stringifier, descriptor
2889 toStringDesc = {
2890 "name": GetWebExposedName(stringifier, descriptor),
2891 "nativeName": stringifier.identifier.name,
2892 "length": 0,
2893 "flags": "JSPROP_ENUMERATE",
2894 "condition": PropertyDefiner.getControllingCondition(
2895 stringifier, descriptor
2898 if isChromeOnly(stringifier):
2899 self.chrome.append(toStringDesc)
2900 else:
2901 self.regular.append(toStringDesc)
2902 if unforgeable and descriptor.interface.getExtendedAttribute(
2903 "LegacyUnforgeable"
2905 # Synthesize our valueOf method
2906 self.regular.append(
2908 "name": "valueOf",
2909 "selfHostedName": "Object_valueOf",
2910 "methodInfo": False,
2911 "length": 0,
2912 "flags": "0", # readonly/permanent added automatically.
2913 "condition": MemberCondition(),
2917 if descriptor.interface.isJSImplemented():
2918 if static:
2919 if descriptor.interface.hasInterfaceObject():
2920 self.chrome.append(
2922 "name": "_create",
2923 "nativeName": ("%s::_Create" % descriptor.name),
2924 "methodInfo": False,
2925 "length": 2,
2926 "flags": "0",
2927 "condition": MemberCondition(),
2931 self.unforgeable = unforgeable
2933 if static:
2934 if not descriptor.interface.hasInterfaceObject():
2935 # static methods go on the interface object
2936 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
2937 else:
2938 if not descriptor.interface.hasInterfacePrototypeObject():
2939 # non-static methods go on the interface prototype object
2940 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
2942 @staticmethod
2943 def methodData(m, descriptor, overrideFlags=None):
2944 return {
2945 "name": m.identifier.name,
2946 "methodInfo": not m.isStatic(),
2947 "length": methodLength(m),
2948 "flags": EnumerabilityFlags(m)
2949 if (overrideFlags is None)
2950 else overrideFlags,
2951 "condition": PropertyDefiner.getControllingCondition(m, descriptor),
2952 "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
2953 "returnsPromise": m.returnsPromise(),
2954 "hasIteratorAlias": "@@iterator" in m.aliases,
2957 @staticmethod
2958 def formatSpec(fields):
2959 if fields[0].startswith("@@"):
2960 fields = (fields[0][2:],) + fields[1:]
2961 return " JS_SYM_FNSPEC(%s, %s, %s, %s, %s, %s)" % fields
2962 return ' JS_FNSPEC("%s", %s, %s, %s, %s, %s)' % fields
2964 @staticmethod
2965 def specData(m, descriptor, unforgeable=False):
2966 def flags(m, unforgeable):
2967 unforgeable = " | JSPROP_PERMANENT | JSPROP_READONLY" if unforgeable else ""
2968 return m["flags"] + unforgeable
2970 if "selfHostedName" in m:
2971 selfHostedName = '"%s"' % m["selfHostedName"]
2972 assert not m.get("methodInfo", True)
2973 accessor = "nullptr"
2974 jitinfo = "nullptr"
2975 else:
2976 selfHostedName = "nullptr"
2977 # When defining symbols, function name may not match symbol name
2978 methodName = m.get("methodName", m["name"])
2979 accessor = m.get("nativeName", IDLToCIdentifier(methodName))
2980 if m.get("methodInfo", True):
2981 if m.get("returnsPromise", False):
2982 exceptionPolicy = "ConvertExceptionsToPromises"
2983 else:
2984 exceptionPolicy = "ThrowExceptions"
2986 # Cast this in case the methodInfo is a
2987 # JSTypedMethodJitInfo.
2988 jitinfo = (
2989 "reinterpret_cast<const JSJitInfo*>(&%s_methodinfo)" % accessor
2991 if m.get("allowCrossOriginThis", False):
2992 accessor = (
2993 "(GenericMethod<CrossOriginThisPolicy, %s>)" % exceptionPolicy
2995 elif descriptor.interface.hasDescendantWithCrossOriginMembers:
2996 accessor = (
2997 "(GenericMethod<MaybeCrossOriginObjectThisPolicy, %s>)"
2998 % exceptionPolicy
3000 elif descriptor.interface.isOnGlobalProtoChain():
3001 accessor = (
3002 "(GenericMethod<MaybeGlobalThisPolicy, %s>)" % exceptionPolicy
3004 else:
3005 accessor = "(GenericMethod<NormalThisPolicy, %s>)" % exceptionPolicy
3006 else:
3007 if m.get("returnsPromise", False):
3008 jitinfo = "&%s_methodinfo" % accessor
3009 accessor = "StaticMethodPromiseWrapper"
3010 else:
3011 jitinfo = "nullptr"
3013 return (
3014 m["name"],
3015 accessor,
3016 jitinfo,
3017 m["length"],
3018 flags(m, unforgeable),
3019 selfHostedName,
3022 @staticmethod
3023 def condition(m, d):
3024 return m["condition"]
3026 def generateArray(self, array, name):
3027 if len(array) == 0:
3028 return ""
3030 return self.generatePrefableArray(
3031 array,
3032 name,
3033 self.formatSpec,
3034 " JS_FS_END",
3035 "JSFunctionSpec",
3036 self.condition,
3037 functools.partial(self.specData, unforgeable=self.unforgeable),
3041 class AttrDefiner(PropertyDefiner):
3042 def __init__(self, descriptor, name, crossOriginOnly, static, unforgeable=False):
3043 assert not (static and unforgeable)
3044 PropertyDefiner.__init__(self, descriptor, name)
3045 self.name = name
3046 # Ignore non-static attributes for interfaces without a proto object
3047 if descriptor.interface.hasInterfacePrototypeObject() or static:
3048 idlAttrs = [
3050 for m in descriptor.interface.members
3051 if m.isAttr()
3052 and m.isStatic() == static
3053 and MemberIsLegacyUnforgeable(m, descriptor) == unforgeable
3054 and (
3055 not crossOriginOnly
3056 or m.getExtendedAttribute("CrossOriginReadable")
3057 or m.getExtendedAttribute("CrossOriginWritable")
3060 else:
3061 idlAttrs = []
3063 attributes = []
3064 for attr in idlAttrs:
3065 attributes.extend(self.attrData(attr, unforgeable))
3066 self.chrome = [m for m in attributes if isChromeOnly(m["attr"])]
3067 self.regular = [m for m in attributes if not isChromeOnly(m["attr"])]
3068 self.static = static
3070 if (
3071 not static
3072 and not unforgeable
3073 and descriptor.interface.hasInterfacePrototypeObject()
3075 self.regular.append(
3076 {"name": "@@toStringTag", "attr": None, "flags": "JSPROP_READONLY"}
3079 if static:
3080 if not descriptor.interface.hasInterfaceObject():
3081 # static attributes go on the interface object
3082 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
3083 else:
3084 if not descriptor.interface.hasInterfacePrototypeObject():
3085 # non-static attributes go on the interface prototype object
3086 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
3088 @staticmethod
3089 def attrData(attr, unforgeable=False, overrideFlags=None):
3090 if overrideFlags is None:
3091 permanent = " | JSPROP_PERMANENT" if unforgeable else ""
3092 flags = EnumerabilityFlags(attr) + permanent
3093 else:
3094 flags = overrideFlags
3095 return (
3096 {"name": name, "attr": attr, "flags": flags}
3097 for name in [attr.identifier.name] + attr.bindingAliases
3100 @staticmethod
3101 def condition(m, d):
3102 if m["name"] == "@@toStringTag":
3103 return MemberCondition()
3104 return PropertyDefiner.getControllingCondition(m["attr"], d)
3106 @staticmethod
3107 def specData(entry, descriptor, static=False, crossOriginOnly=False):
3108 if entry["name"] == "@@toStringTag":
3109 return (entry["name"], entry["flags"], descriptor.interface.getClassName())
3111 def getter(attr):
3112 if crossOriginOnly and not attr.getExtendedAttribute("CrossOriginReadable"):
3113 return "nullptr, nullptr"
3114 if static:
3115 if attr.type.isPromise():
3116 raise TypeError(
3117 "Don't know how to handle "
3118 "static Promise-returning "
3119 "attribute %s.%s" % (descriptor.name, attr.identifier.name)
3121 accessor = "get_" + IDLToCIdentifier(attr.identifier.name)
3122 jitinfo = "nullptr"
3123 else:
3124 if attr.type.isPromise():
3125 exceptionPolicy = "ConvertExceptionsToPromises"
3126 else:
3127 exceptionPolicy = "ThrowExceptions"
3129 if attr.hasLegacyLenientThis():
3130 if attr.getExtendedAttribute("CrossOriginReadable"):
3131 raise TypeError(
3132 "Can't handle lenient cross-origin "
3133 "readable attribute %s.%s"
3134 % (self.descriptor.name, attr.identifier.name)
3136 if descriptor.interface.hasDescendantWithCrossOriginMembers:
3137 accessor = (
3138 "GenericGetter<MaybeCrossOriginObjectLenientThisPolicy, %s>"
3139 % exceptionPolicy
3141 else:
3142 accessor = (
3143 "GenericGetter<LenientThisPolicy, %s>" % exceptionPolicy
3145 elif attr.getExtendedAttribute("CrossOriginReadable"):
3146 accessor = (
3147 "GenericGetter<CrossOriginThisPolicy, %s>" % exceptionPolicy
3149 elif descriptor.interface.hasDescendantWithCrossOriginMembers:
3150 accessor = (
3151 "GenericGetter<MaybeCrossOriginObjectThisPolicy, %s>"
3152 % exceptionPolicy
3154 elif descriptor.interface.isOnGlobalProtoChain():
3155 accessor = (
3156 "GenericGetter<MaybeGlobalThisPolicy, %s>" % exceptionPolicy
3158 else:
3159 accessor = "GenericGetter<NormalThisPolicy, %s>" % exceptionPolicy
3160 jitinfo = "&%s_getterinfo" % IDLToCIdentifier(attr.identifier.name)
3161 return "%s, %s" % (accessor, jitinfo)
3163 def setter(attr):
3164 if (
3165 attr.readonly
3166 and attr.getExtendedAttribute("PutForwards") is None
3167 and attr.getExtendedAttribute("Replaceable") is None
3168 and attr.getExtendedAttribute("LegacyLenientSetter") is None
3170 return "nullptr, nullptr"
3171 if crossOriginOnly and not attr.getExtendedAttribute("CrossOriginWritable"):
3172 return "nullptr, nullptr"
3173 if static:
3174 accessor = "set_" + IDLToCIdentifier(attr.identifier.name)
3175 jitinfo = "nullptr"
3176 else:
3177 if attr.hasLegacyLenientThis():
3178 if attr.getExtendedAttribute("CrossOriginWritable"):
3179 raise TypeError(
3180 "Can't handle lenient cross-origin "
3181 "writable attribute %s.%s"
3182 % (descriptor.name, attr.identifier.name)
3184 if descriptor.interface.hasDescendantWithCrossOriginMembers:
3185 accessor = (
3186 "GenericSetter<MaybeCrossOriginObjectLenientThisPolicy>"
3188 else:
3189 accessor = "GenericSetter<LenientThisPolicy>"
3190 elif attr.getExtendedAttribute("CrossOriginWritable"):
3191 accessor = "GenericSetter<CrossOriginThisPolicy>"
3192 elif descriptor.interface.hasDescendantWithCrossOriginMembers:
3193 accessor = "GenericSetter<MaybeCrossOriginObjectThisPolicy>"
3194 elif descriptor.interface.isOnGlobalProtoChain():
3195 accessor = "GenericSetter<MaybeGlobalThisPolicy>"
3196 else:
3197 accessor = "GenericSetter<NormalThisPolicy>"
3198 jitinfo = "&%s_setterinfo" % IDLToCIdentifier(attr.identifier.name)
3199 return "%s, %s" % (accessor, jitinfo)
3201 name, attr, flags = entry["name"], entry["attr"], entry["flags"]
3202 return (name, flags, getter(attr), setter(attr))
3204 @staticmethod
3205 def formatSpec(fields):
3206 if fields[0] == "@@toStringTag":
3207 return ' JS_STRING_SYM_PS(%s, "%s", %s)' % (
3208 fields[0][2:],
3209 fields[2],
3210 fields[1],
3213 return ' JSPropertySpec::nativeAccessors("%s", %s, %s, %s)' % fields
3215 def generateArray(self, array, name):
3216 if len(array) == 0:
3217 return ""
3219 return self.generatePrefableArray(
3220 array,
3221 name,
3222 self.formatSpec,
3223 " JS_PS_END",
3224 "JSPropertySpec",
3225 self.condition,
3226 functools.partial(self.specData, static=self.static),
3230 class ConstDefiner(PropertyDefiner):
3232 A class for definining constants on the interface object
3235 def __init__(self, descriptor, name):
3236 PropertyDefiner.__init__(self, descriptor, name)
3237 self.name = name
3238 constants = [m for m in descriptor.interface.members if m.isConst()]
3239 self.chrome = [m for m in constants if isChromeOnly(m)]
3240 self.regular = [m for m in constants if not isChromeOnly(m)]
3242 def generateArray(self, array, name):
3243 if len(array) == 0:
3244 return ""
3246 def specData(const, descriptor):
3247 return (const.identifier.name, convertConstIDLValueToJSVal(const.value))
3249 return self.generatePrefableArray(
3250 array,
3251 name,
3252 lambda fields: ' { "%s", %s }' % fields,
3253 " { 0, JS::UndefinedValue() }",
3254 "ConstantSpec",
3255 PropertyDefiner.getControllingCondition,
3256 specData,
3260 class PropertyArrays:
3261 def __init__(self, descriptor, crossOriginOnly=False):
3262 self.staticMethods = MethodDefiner(
3263 descriptor, "StaticMethods", crossOriginOnly, static=True
3265 self.staticAttrs = AttrDefiner(
3266 descriptor, "StaticAttributes", crossOriginOnly, static=True
3268 self.methods = MethodDefiner(
3269 descriptor, "Methods", crossOriginOnly, static=False
3271 self.attrs = AttrDefiner(
3272 descriptor, "Attributes", crossOriginOnly, static=False
3274 self.unforgeableMethods = MethodDefiner(
3275 descriptor,
3276 "UnforgeableMethods",
3277 crossOriginOnly,
3278 static=False,
3279 unforgeable=True,
3281 self.unforgeableAttrs = AttrDefiner(
3282 descriptor,
3283 "UnforgeableAttributes",
3284 crossOriginOnly,
3285 static=False,
3286 unforgeable=True,
3288 self.consts = ConstDefiner(descriptor, "Constants")
3290 @staticmethod
3291 def arrayNames():
3292 return [
3293 "staticMethods",
3294 "staticAttrs",
3295 "methods",
3296 "attrs",
3297 "unforgeableMethods",
3298 "unforgeableAttrs",
3299 "consts",
3302 def hasChromeOnly(self):
3303 return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())
3305 def hasNonChromeOnly(self):
3306 return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames())
3308 def __str__(self):
3309 define = ""
3310 for array in self.arrayNames():
3311 define += str(getattr(self, array))
3312 return define
3315 class CGConstDefinition(CGThing):
3317 Given a const member of an interface, return the C++ static const definition
3318 for the member. Should be part of the interface namespace in the header
3319 file.
3322 def __init__(self, member):
3323 assert (
3324 member.isConst()
3325 and member.value.type.isPrimitive()
3326 and not member.value.type.nullable()
3329 name = CppKeywords.checkMethodName(IDLToCIdentifier(member.identifier.name))
3330 tag = member.value.type.tag()
3331 value = member.value.value
3332 if tag == IDLType.Tags.bool:
3333 value = toStringBool(member.value.value)
3334 self.const = "static const %s %s = %s;" % (builtinNames[tag], name, value)
3336 def declare(self):
3337 return self.const
3339 def define(self):
3340 return ""
3342 def deps(self):
3343 return []
3346 class CGNativeProperties(CGList):
3347 def __init__(self, descriptor, properties):
3348 def generateNativeProperties(name, chrome):
3349 def check(p):
3350 return p.hasChromeOnly() if chrome else p.hasNonChromeOnly()
3352 nativePropsInts = []
3353 nativePropsPtrs = []
3354 nativePropsDuos = []
3356 duosOffset = 0
3357 idsOffset = 0
3358 for array in properties.arrayNames():
3359 propertyArray = getattr(properties, array)
3360 if check(propertyArray):
3361 varName = propertyArray.variableName(chrome)
3362 bitfields = "true, %d /* %s */" % (duosOffset, varName)
3363 duosOffset += 1
3364 nativePropsInts.append(CGGeneric(bitfields))
3366 if propertyArray.usedForXrays():
3367 ids = "&%s_propertyInfos[%d]" % (name, idsOffset)
3368 idsOffset += propertyArray.length(chrome)
3369 else:
3370 ids = "nullptr"
3371 duo = "{ %s, %s }" % (varName, ids)
3372 nativePropsDuos.append(CGGeneric(duo))
3373 else:
3374 bitfields = "false, 0"
3375 nativePropsInts.append(CGGeneric(bitfields))
3377 iteratorAliasIndex = -1
3378 for index, item in enumerate(properties.methods.regular):
3379 if item.get("hasIteratorAlias"):
3380 iteratorAliasIndex = index
3381 break
3382 nativePropsInts.append(CGGeneric(str(iteratorAliasIndex)))
3384 nativePropsDuos = [
3385 CGWrapper(
3386 CGIndenter(CGList(nativePropsDuos, ",\n")), pre="{\n", post="\n}"
3390 pre = "static const NativePropertiesN<%d> %s = {\n" % (duosOffset, name)
3391 post = "\n};\n"
3392 if descriptor.wantsXrays:
3393 pre = fill(
3395 static uint16_t ${name}_sortedPropertyIndices[${size}];
3396 static PropertyInfo ${name}_propertyInfos[${size}];
3398 $*{pre}
3399 """,
3400 name=name,
3401 size=idsOffset,
3402 pre=pre,
3404 if iteratorAliasIndex > 0:
3405 # The iteratorAliasMethodIndex is a signed integer, so the
3406 # max value it can store is 2^(nbits-1)-1.
3407 post = fill(
3409 $*{post}
3410 static_assert(${iteratorAliasIndex} < 1ull << (CHAR_BIT * sizeof(${name}.iteratorAliasMethodIndex) - 1),
3411 "We have an iterator alias index that is oversized");
3412 """,
3413 post=post,
3414 iteratorAliasIndex=iteratorAliasIndex,
3415 name=name,
3417 post = fill(
3419 $*{post}
3420 static_assert(${propertyInfoCount} < 1ull << (CHAR_BIT * sizeof(${name}.propertyInfoCount)),
3421 "We have a property info count that is oversized");
3422 """,
3423 post=post,
3424 propertyInfoCount=idsOffset,
3425 name=name,
3427 nativePropsInts.append(CGGeneric("%d" % idsOffset))
3428 nativePropsPtrs.append(CGGeneric("%s_sortedPropertyIndices" % name))
3429 else:
3430 nativePropsInts.append(CGGeneric("0"))
3431 nativePropsPtrs.append(CGGeneric("nullptr"))
3432 nativeProps = nativePropsInts + nativePropsPtrs + nativePropsDuos
3433 return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")), pre=pre, post=post)
3435 nativeProperties = []
3436 if properties.hasNonChromeOnly():
3437 nativeProperties.append(
3438 generateNativeProperties("sNativeProperties", False)
3440 if properties.hasChromeOnly():
3441 nativeProperties.append(
3442 generateNativeProperties("sChromeOnlyNativeProperties", True)
3445 CGList.__init__(self, nativeProperties, "\n")
3447 def declare(self):
3448 return ""
3450 def define(self):
3451 return CGList.define(self)
3454 class CGCollectJSONAttributesMethod(CGAbstractMethod):
3456 Generate the CollectJSONAttributes method for an interface descriptor
3459 def __init__(self, descriptor, toJSONMethod):
3460 args = [
3461 Argument("JSContext*", "cx"),
3462 Argument("JS::Handle<JSObject*>", "obj"),
3463 Argument("%s*" % descriptor.nativeType, "self"),
3464 Argument("JS::Rooted<JSObject*>&", "result"),
3466 CGAbstractMethod.__init__(
3467 self, descriptor, "CollectJSONAttributes", "bool", args, canRunScript=True
3469 self.toJSONMethod = toJSONMethod
3471 def definition_body(self):
3472 ret = ""
3473 interface = self.descriptor.interface
3474 toJSONCondition = PropertyDefiner.getControllingCondition(
3475 self.toJSONMethod, self.descriptor
3477 needUnwrappedObj = False
3478 for m in interface.members:
3479 if m.isAttr() and not m.isStatic() and m.type.isJSONType():
3480 getAndDefine = fill(
3482 JS::Rooted<JS::Value> temp(cx);
3483 if (!get_${name}(cx, obj, self, JSJitGetterCallArgs(&temp))) {
3484 return false;
3486 if (!JS_DefineProperty(cx, result, "${name}", temp, JSPROP_ENUMERATE)) {
3487 return false;
3489 """,
3490 name=IDLToCIdentifier(m.identifier.name),
3492 # Make sure we don't include things which are supposed to be
3493 # disabled. Things that either don't have disablers or whose
3494 # disablers match the disablers for our toJSON method can't
3495 # possibly be disabled, but other things might be.
3496 condition = PropertyDefiner.getControllingCondition(m, self.descriptor)
3497 if condition.hasDisablers() and condition != toJSONCondition:
3498 needUnwrappedObj = True
3499 ret += fill(
3501 // This is unfortunately a linear scan through sAttributes, but we
3502 // only do it for things which _might_ be disabled, which should
3503 // help keep the performance problems down.
3504 if (IsGetterEnabled(cx, unwrappedObj, (JSJitGetterOp)get_${name}, sAttributes)) {
3505 $*{getAndDefine}
3507 """,
3508 name=IDLToCIdentifier(m.identifier.name),
3509 getAndDefine=getAndDefine,
3511 else:
3512 ret += fill(
3514 { // scope for "temp"
3515 $*{getAndDefine}
3517 """,
3518 getAndDefine=getAndDefine,
3520 ret += "return true;\n"
3522 if needUnwrappedObj:
3523 # If we started allowing cross-origin objects here, we'd need to
3524 # use CheckedUnwrapDynamic and figure out whether it makes sense.
3525 # But in practice no one is trying to add toJSON methods to those,
3526 # so let's just guard against it.
3527 assert not self.descriptor.isMaybeCrossOriginObject()
3528 ret = fill(
3530 JS::Rooted<JSObject*> unwrappedObj(cx, js::CheckedUnwrapStatic(obj));
3531 if (!unwrappedObj) {
3532 // How did that happen? We managed to get called with that
3533 // object as "this"! Just give up on sanity.
3534 return false;
3537 $*{ret}
3538 """,
3539 ret=ret,
3542 return ret
3545 class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
3547 Generate the CreateInterfaceObjects method for an interface descriptor.
3549 properties should be a PropertyArrays instance.
3552 def __init__(
3553 self, descriptor, properties, haveUnscopables, haveLegacyWindowAliases
3555 args = [
3556 Argument("JSContext*", "aCx"),
3557 Argument("JS::Handle<JSObject*>", "aGlobal"),
3558 Argument("ProtoAndIfaceCache&", "aProtoAndIfaceCache"),
3559 Argument("bool", "aDefineOnGlobal"),
3561 CGAbstractMethod.__init__(
3562 self, descriptor, "CreateInterfaceObjects", "void", args
3564 self.properties = properties
3565 self.haveUnscopables = haveUnscopables
3566 self.haveLegacyWindowAliases = haveLegacyWindowAliases
3568 def definition_body(self):
3569 (protoGetter, protoHandleGetter) = InterfacePrototypeObjectProtoGetter(
3570 self.descriptor
3572 if protoHandleGetter is None:
3573 parentProtoType = "Rooted"
3574 getParentProto = "aCx, " + protoGetter
3575 else:
3576 parentProtoType = "Handle"
3577 getParentProto = protoHandleGetter
3578 getParentProto = getParentProto + "(aCx)"
3580 (protoGetter, protoHandleGetter) = InterfaceObjectProtoGetter(self.descriptor)
3581 if protoHandleGetter is None:
3582 getConstructorProto = "aCx, " + protoGetter
3583 constructorProtoType = "Rooted"
3584 else:
3585 getConstructorProto = protoHandleGetter
3586 constructorProtoType = "Handle"
3587 getConstructorProto += "(aCx)"
3589 needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
3590 needInterfacePrototypeObject = (
3591 self.descriptor.interface.hasInterfacePrototypeObject()
3594 # if we don't need to create anything, why are we generating this?
3595 assert needInterfaceObject or needInterfacePrototypeObject
3597 getParentProto = fill(
3599 JS::${type}<JSObject*> parentProto(${getParentProto});
3600 if (!parentProto) {
3601 return;
3603 """,
3604 type=parentProtoType,
3605 getParentProto=getParentProto,
3608 getConstructorProto = fill(
3610 JS::${type}<JSObject*> constructorProto(${getConstructorProto});
3611 if (!constructorProto) {
3612 return;
3614 """,
3615 type=constructorProtoType,
3616 getConstructorProto=getConstructorProto,
3619 idsToInit = []
3620 # There is no need to init any IDs in bindings that don't want Xrays.
3621 if self.descriptor.wantsXrays:
3622 if self.properties.hasNonChromeOnly():
3623 idsToInit.append("sNativeProperties")
3624 if self.properties.hasChromeOnly():
3625 idsToInit.append("sChromeOnlyNativeProperties")
3626 if len(idsToInit) > 0:
3627 initIdCalls = [
3628 "!InitIds(aCx, %s.Upcast())" % (properties) for properties in idsToInit
3630 idsInitedFlag = CGGeneric(
3631 "static Atomic<bool, Relaxed> sIdsInited(false);\n"
3633 setFlag = CGGeneric("sIdsInited = true;\n")
3634 initIdConditionals = [
3635 CGIfWrapper(CGGeneric("return;\n"), call) for call in initIdCalls
3637 initIds = CGList(
3639 idsInitedFlag,
3640 CGIfWrapper(
3641 CGList(initIdConditionals + [setFlag]),
3642 "!sIdsInited && NS_IsMainThread()",
3646 else:
3647 initIds = None
3649 if self.descriptor.interface.ctor():
3650 constructArgs = methodLength(self.descriptor.interface.ctor())
3651 else:
3652 constructArgs = 0
3653 if len(self.descriptor.interface.legacyFactoryFunctions) > 0:
3654 namedConstructors = "namedConstructors"
3655 else:
3656 namedConstructors = "nullptr"
3658 if needInterfacePrototypeObject:
3659 protoClass = "&sPrototypeClass.mBase"
3660 protoCache = (
3661 "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)"
3662 % self.descriptor.name
3664 parentProto = "parentProto"
3665 getParentProto = CGGeneric(getParentProto)
3666 else:
3667 protoClass = "nullptr"
3668 protoCache = "nullptr"
3669 parentProto = "nullptr"
3670 getParentProto = None
3672 if needInterfaceObject:
3673 interfaceClass = "&sInterfaceObjectClass.mBase"
3674 interfaceCache = (
3675 "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)"
3676 % self.descriptor.name
3678 getConstructorProto = CGGeneric(getConstructorProto)
3679 constructorProto = "constructorProto"
3680 else:
3681 # We don't have slots to store the legacy factory functions.
3682 assert len(self.descriptor.interface.legacyFactoryFunctions) == 0
3683 interfaceClass = "nullptr"
3684 interfaceCache = "nullptr"
3685 getConstructorProto = None
3686 constructorProto = "nullptr"
3688 isGlobal = self.descriptor.isGlobal() is not None
3689 if self.properties.hasNonChromeOnly():
3690 properties = "sNativeProperties.Upcast()"
3691 else:
3692 properties = "nullptr"
3693 if self.properties.hasChromeOnly():
3694 chromeProperties = "sChromeOnlyNativeProperties.Upcast()"
3695 else:
3696 chromeProperties = "nullptr"
3698 call = fill(
3700 JS::Heap<JSObject*>* protoCache = ${protoCache};
3701 JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
3702 dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
3703 ${protoClass}, protoCache,
3704 ${constructorProto}, ${interfaceClass}, ${constructArgs}, ${namedConstructors},
3705 interfaceCache,
3706 ${properties},
3707 ${chromeProperties},
3708 ${name}, aDefineOnGlobal,
3709 ${unscopableNames},
3710 ${isGlobal},
3711 ${legacyWindowAliases},
3712 ${isNamespace});
3713 """,
3714 protoClass=protoClass,
3715 parentProto=parentProto,
3716 protoCache=protoCache,
3717 constructorProto=constructorProto,
3718 interfaceClass=interfaceClass,
3719 constructArgs=constructArgs,
3720 namedConstructors=namedConstructors,
3721 interfaceCache=interfaceCache,
3722 properties=properties,
3723 chromeProperties=chromeProperties,
3724 name='"' + self.descriptor.interface.identifier.name + '"'
3725 if needInterfaceObject
3726 else "nullptr",
3727 unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr",
3728 isGlobal=toStringBool(isGlobal),
3729 legacyWindowAliases="legacyWindowAliases"
3730 if self.haveLegacyWindowAliases
3731 else "nullptr",
3732 isNamespace=toStringBool(self.descriptor.interface.isNamespace()),
3735 # If we fail after here, we must clear interface and prototype caches
3736 # using this code: intermediate failure must not expose the interface in
3737 # partially-constructed state. Note that every case after here needs an
3738 # interface prototype object.
3739 failureCode = dedent(
3741 *protoCache = nullptr;
3742 if (interfaceCache) {
3743 *interfaceCache = nullptr;
3745 return;
3749 aliasedMembers = [
3750 m for m in self.descriptor.interface.members if m.isMethod() and m.aliases
3752 if aliasedMembers:
3753 assert needInterfacePrototypeObject
3755 def defineAlias(alias):
3756 if alias == "@@iterator":
3757 symbolJSID = "SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::iterator))"
3758 getSymbolJSID = CGGeneric(
3759 fill(
3760 "JS::Rooted<jsid> iteratorId(aCx, ${symbolJSID});",
3761 symbolJSID=symbolJSID,
3764 defineFn = "JS_DefinePropertyById"
3765 prop = "iteratorId"
3766 enumFlags = "0" # Not enumerable, per spec.
3767 elif alias.startswith("@@"):
3768 raise TypeError(
3769 "Can't handle any well-known Symbol other than @@iterator"
3771 else:
3772 getSymbolJSID = None
3773 defineFn = "JS_DefineProperty"
3774 prop = '"%s"' % alias
3775 # XXX If we ever create non-enumerable properties that can
3776 # be aliased, we should consider making the aliases
3777 # match the enumerability of the property being aliased.
3778 enumFlags = "JSPROP_ENUMERATE"
3779 return CGList(
3781 getSymbolJSID,
3782 CGGeneric(
3783 fill(
3785 if (!${defineFn}(aCx, proto, ${prop}, aliasedVal, ${enumFlags})) {
3786 $*{failureCode}
3788 """,
3789 defineFn=defineFn,
3790 prop=prop,
3791 enumFlags=enumFlags,
3792 failureCode=failureCode,
3796 "\n",
3799 def defineAliasesFor(m):
3800 return CGList(
3802 CGGeneric(
3803 fill(
3805 if (!JS_GetProperty(aCx, proto, \"${prop}\", &aliasedVal)) {
3806 $*{failureCode}
3808 """,
3809 failureCode=failureCode,
3810 prop=m.identifier.name,
3814 + [defineAlias(alias) for alias in sorted(m.aliases)]
3817 defineAliases = CGList(
3819 CGGeneric(
3820 fill(
3822 // Set up aliases on the interface prototype object we just created.
3823 JS::Handle<JSObject*> proto = GetProtoObjectHandle(aCx);
3824 if (!proto) {
3825 $*{failureCode}
3828 """,
3829 failureCode=failureCode,
3832 CGGeneric("JS::Rooted<JS::Value> aliasedVal(aCx);\n\n"),
3835 defineAliasesFor(m)
3836 for m in sorted(aliasedMembers, key=lambda m: m.identifier.name)
3839 else:
3840 defineAliases = None
3842 # Globals handle unforgeables directly in Wrap() instead of
3843 # via a holder.
3844 if (
3845 self.descriptor.hasLegacyUnforgeableMembers
3846 and not self.descriptor.isGlobal()
3848 assert needInterfacePrototypeObject
3850 # We want to use the same JSClass and prototype as the object we'll
3851 # end up defining the unforgeable properties on in the end, so that
3852 # we can use JS_InitializePropertiesFromCompatibleNativeObject to do
3853 # a fast copy. In the case of proxies that's null, because the
3854 # expando object is a vanilla object, but in the case of other DOM
3855 # objects it's whatever our class is.
3856 if self.descriptor.proxy:
3857 holderClass = "nullptr"
3858 holderProto = "nullptr"
3859 else:
3860 holderClass = "sClass.ToJSClass()"
3861 holderProto = "*protoCache"
3862 createUnforgeableHolder = CGGeneric(
3863 fill(
3865 JS::Rooted<JSObject*> unforgeableHolder(aCx);
3867 JS::Rooted<JSObject*> holderProto(aCx, ${holderProto});
3868 unforgeableHolder = JS_NewObjectWithoutMetadata(aCx, ${holderClass}, holderProto);
3869 if (!unforgeableHolder) {
3870 $*{failureCode}
3873 """,
3874 holderProto=holderProto,
3875 holderClass=holderClass,
3876 failureCode=failureCode,
3879 defineUnforgeables = InitUnforgeablePropertiesOnHolder(
3880 self.descriptor, self.properties, failureCode
3882 createUnforgeableHolder = CGList(
3883 [createUnforgeableHolder, defineUnforgeables]
3886 installUnforgeableHolder = CGGeneric(
3887 dedent(
3889 if (*protoCache) {
3890 JS::SetReservedSlot(*protoCache, DOM_INTERFACE_PROTO_SLOTS_BASE,
3891 JS::ObjectValue(*unforgeableHolder));
3897 unforgeableHolderSetup = CGList(
3898 [createUnforgeableHolder, installUnforgeableHolder], "\n"
3900 else:
3901 unforgeableHolderSetup = None
3903 if (
3904 self.descriptor.interface.isOnGlobalProtoChain()
3905 and needInterfacePrototypeObject
3907 makeProtoPrototypeImmutable = CGGeneric(
3908 fill(
3910 if (*${protoCache}) {
3911 bool succeeded;
3912 JS::Handle<JSObject*> prot = GetProtoObjectHandle(aCx);
3913 if (!JS_SetImmutablePrototype(aCx, prot, &succeeded)) {
3914 $*{failureCode}
3917 MOZ_ASSERT(succeeded,
3918 "making a fresh prototype object's [[Prototype]] "
3919 "immutable can internally fail, but it should "
3920 "never be unsuccessful");
3922 """,
3923 protoCache=protoCache,
3924 failureCode=failureCode,
3927 else:
3928 makeProtoPrototypeImmutable = None
3930 return CGList(
3932 getParentProto,
3933 getConstructorProto,
3934 initIds,
3935 CGGeneric(call),
3936 defineAliases,
3937 unforgeableHolderSetup,
3938 makeProtoPrototypeImmutable,
3940 "\n",
3941 ).define()
3944 class CGGetProtoObjectHandleMethod(CGAbstractMethod):
3946 A method for getting the interface prototype object.
3949 def __init__(self, descriptor):
3950 CGAbstractMethod.__init__(
3951 self,
3952 descriptor,
3953 "GetProtoObjectHandle",
3954 "JS::Handle<JSObject*>",
3955 [Argument("JSContext*", "aCx")],
3956 inline=True,
3959 def definition_body(self):
3960 return fill(
3962 /* Get the interface prototype object for this class. This will create the
3963 object as needed. */
3964 return GetPerInterfaceObjectHandle(aCx, prototypes::id::${name},
3965 &CreateInterfaceObjects,
3966 /* aDefineOnGlobal = */ true);
3968 """,
3969 name=self.descriptor.name,
3973 class CGGetProtoObjectMethod(CGAbstractMethod):
3975 A method for getting the interface prototype object.
3978 def __init__(self, descriptor):
3979 CGAbstractMethod.__init__(
3980 self,
3981 descriptor,
3982 "GetProtoObject",
3983 "JSObject*",
3984 [Argument("JSContext*", "aCx")],
3987 def definition_body(self):
3988 return "return GetProtoObjectHandle(aCx);\n"
3991 class CGGetConstructorObjectHandleMethod(CGAbstractMethod):
3993 A method for getting the interface constructor object.
3996 def __init__(self, descriptor):
3997 CGAbstractMethod.__init__(
3998 self,
3999 descriptor,
4000 "GetConstructorObjectHandle",
4001 "JS::Handle<JSObject*>",
4003 Argument("JSContext*", "aCx"),
4004 Argument("bool", "aDefineOnGlobal", "true"),
4006 inline=True,
4009 def definition_body(self):
4010 return fill(
4012 /* Get the interface object for this class. This will create the object as
4013 needed. */
4015 return GetPerInterfaceObjectHandle(aCx, constructors::id::${name},
4016 &CreateInterfaceObjects,
4017 aDefineOnGlobal);
4018 """,
4019 name=self.descriptor.name,
4023 class CGGetConstructorObjectMethod(CGAbstractMethod):
4025 A method for getting the interface constructor object.
4028 def __init__(self, descriptor):
4029 CGAbstractMethod.__init__(
4030 self,
4031 descriptor,
4032 "GetConstructorObject",
4033 "JSObject*",
4034 [Argument("JSContext*", "aCx")],
4037 def definition_body(self):
4038 return "return GetConstructorObjectHandle(aCx);\n"
4041 class CGGetNamedPropertiesObjectMethod(CGAbstractStaticMethod):
4042 def __init__(self, descriptor):
4043 args = [Argument("JSContext*", "aCx")]
4044 CGAbstractStaticMethod.__init__(
4045 self, descriptor, "GetNamedPropertiesObject", "JSObject*", args
4048 def definition_body(self):
4049 parentProtoName = self.descriptor.parentPrototypeName
4050 if parentProtoName is None:
4051 getParentProto = ""
4052 parentProto = "nullptr"
4053 else:
4054 getParentProto = fill(
4056 JS::Rooted<JSObject*> parentProto(aCx, ${parent}::GetProtoObjectHandle(aCx));
4057 if (!parentProto) {
4058 return nullptr;
4060 """,
4061 parent=toBindingNamespace(parentProtoName),
4063 parentProto = "parentProto"
4064 return fill(
4066 /* Make sure our global is sane. Hopefully we can remove this sometime */
4067 JSObject* global = JS::CurrentGlobalOrNull(aCx);
4068 if (!(JS::GetClass(global)->flags & JSCLASS_DOM_GLOBAL)) {
4069 return nullptr;
4072 /* Check to see whether the named properties object has already been created */
4073 ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
4075 JS::Heap<JSObject*>& namedPropertiesObject = protoAndIfaceCache.EntrySlotOrCreate(namedpropertiesobjects::id::${ifaceName});
4076 if (!namedPropertiesObject) {
4077 $*{getParentProto}
4078 namedPropertiesObject = ${nativeType}::CreateNamedPropertiesObject(aCx, ${parentProto});
4079 DebugOnly<const DOMIfaceAndProtoJSClass*> clasp =
4080 DOMIfaceAndProtoJSClass::FromJSClass(JS::GetClass(namedPropertiesObject));
4081 MOZ_ASSERT(clasp->mType == eNamedPropertiesObject,
4082 "Expected ${nativeType}::CreateNamedPropertiesObject to return a named properties object");
4083 MOZ_ASSERT(clasp->mNativeHooks,
4084 "The named properties object for ${nativeType} should have NativePropertyHooks.");
4085 MOZ_ASSERT(!clasp->mNativeHooks->mResolveOwnProperty,
4086 "Shouldn't resolve the properties of the named properties object for ${nativeType} for Xrays.");
4087 MOZ_ASSERT(!clasp->mNativeHooks->mEnumerateOwnProperties,
4088 "Shouldn't enumerate the properties of the named properties object for ${nativeType} for Xrays.");
4090 return namedPropertiesObject.get();
4091 """,
4092 getParentProto=getParentProto,
4093 ifaceName=self.descriptor.name,
4094 parentProto=parentProto,
4095 nativeType=self.descriptor.nativeType,
4099 def getRawConditionList(idlobj, cxName, objName, ignoreSecureContext=False):
4101 Get the list of conditions for idlobj (to be used in "is this enabled"
4102 checks). This will be returned as a CGList with " &&\n" as the separator,
4103 for readability.
4105 objName is the name of the object that we're working with, because some of
4106 our test functions want that.
4108 ignoreSecureContext is used only for constructors in which the WebIDL interface
4109 itself is already marked as [SecureContext]. There is no need to do the work twice.
4111 conditions = []
4112 pref = idlobj.getExtendedAttribute("Pref")
4113 if pref:
4114 assert isinstance(pref, list) and len(pref) == 1
4115 conditions.append("StaticPrefs::%s()" % prefIdentifier(pref[0]))
4116 if isChromeOnly(idlobj):
4117 conditions.append("nsContentUtils::ThreadsafeIsSystemCaller(%s)" % cxName)
4118 func = idlobj.getExtendedAttribute("Func")
4119 if func:
4120 assert isinstance(func, list) and len(func) == 1
4121 conditions.append("%s(%s, %s)" % (func[0], cxName, objName))
4122 if not ignoreSecureContext and idlobj.getExtendedAttribute("SecureContext"):
4123 conditions.append(
4124 "mozilla::dom::IsSecureContextOrObjectIsFromSecureContext(%s, %s)"
4125 % (cxName, objName)
4127 return conditions
4130 def getConditionList(idlobj, cxName, objName, ignoreSecureContext=False):
4132 Get the list of conditions from getRawConditionList
4133 See comment on getRawConditionList above for more info about arguments.
4135 The return value is a possibly-empty conjunctive CGList of conditions.
4137 conditions = getRawConditionList(idlobj, cxName, objName, ignoreSecureContext)
4138 return CGList((CGGeneric(cond) for cond in conditions), " &&\n")
4141 class CGConstructorEnabled(CGAbstractMethod):
4143 A method for testing whether we should be exposing this interface object.
4144 This can perform various tests depending on what conditions are specified
4145 on the interface.
4148 def __init__(self, descriptor):
4149 CGAbstractMethod.__init__(
4150 self,
4151 descriptor,
4152 "ConstructorEnabled",
4153 "bool",
4154 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
4157 def definition_body(self):
4158 body = CGList([], "\n")
4160 iface = self.descriptor.interface
4162 if not iface.isExposedInWindow():
4163 exposedInWindowCheck = dedent(
4165 MOZ_ASSERT(!NS_IsMainThread(), "Why did we even get called?");
4168 body.append(CGGeneric(exposedInWindowCheck))
4170 if iface.isExposedInSomeButNotAllWorkers():
4171 workerGlobals = sorted(iface.getWorkerExposureSet())
4172 workerCondition = CGList(
4174 CGGeneric('strcmp(name, "%s")' % workerGlobal)
4175 for workerGlobal in workerGlobals
4177 " && ",
4179 exposedInWorkerCheck = fill(
4181 const char* name = JS::GetClass(aObj)->name;
4182 if (${workerCondition}) {
4183 return false;
4185 """,
4186 workerCondition=workerCondition.define(),
4188 exposedInWorkerCheck = CGGeneric(exposedInWorkerCheck)
4189 if iface.isExposedInWindow():
4190 exposedInWorkerCheck = CGIfWrapper(
4191 exposedInWorkerCheck, "!NS_IsMainThread()"
4193 body.append(exposedInWorkerCheck)
4195 conditions = getConditionList(iface, "aCx", "aObj")
4197 # We should really have some conditions
4198 assert len(body) or len(conditions)
4200 conditionsWrapper = ""
4201 if len(conditions):
4202 conditionsWrapper = CGWrapper(
4203 conditions, pre="return ", post=";\n", reindent=True
4205 else:
4206 conditionsWrapper = CGGeneric("return true;\n")
4208 body.append(conditionsWrapper)
4209 return body.define()
4212 def StructuredCloneTag(name):
4213 return "SCTAG_DOM_%s" % name.upper()
4216 class CGSerializer(CGAbstractStaticMethod):
4218 Implementation of serialization for things marked [Serializable].
4219 This gets stored in our DOMJSClass, so it can be static.
4221 The caller is expected to pass in the object whose DOMJSClass it
4222 used to get the serializer.
4225 def __init__(self, descriptor):
4226 args = [
4227 Argument("JSContext*", "aCx"),
4228 Argument("JSStructuredCloneWriter*", "aWriter"),
4229 Argument("JS::Handle<JSObject*>", "aObj"),
4231 CGAbstractStaticMethod.__init__(self, descriptor, "Serialize", "bool", args)
4233 def definition_body(self):
4234 return fill(
4236 MOZ_ASSERT(IsDOMObject(aObj), "Non-DOM object passed");
4237 MOZ_ASSERT(GetDOMClass(aObj)->mSerializer == &Serialize,
4238 "Wrong object passed");
4239 return JS_WriteUint32Pair(aWriter, ${tag}, 0) &&
4240 UnwrapDOMObject<${type}>(aObj)->WriteStructuredClone(aCx, aWriter);
4241 """,
4242 tag=StructuredCloneTag(self.descriptor.name),
4243 type=self.descriptor.nativeType,
4247 class CGDeserializer(CGAbstractMethod):
4249 Implementation of deserialization for things marked [Serializable].
4250 This will need to be accessed from WebIDLSerializable, so can't be static.
4253 def __init__(self, descriptor):
4254 args = [
4255 Argument("JSContext*", "aCx"),
4256 Argument("nsIGlobalObject*", "aGlobal"),
4257 Argument("JSStructuredCloneReader*", "aReader"),
4259 CGAbstractMethod.__init__(self, descriptor, "Deserialize", "JSObject*", args)
4261 def definition_body(self):
4262 # WrapObject has different signatures depending on whether
4263 # the object is wrappercached.
4264 if self.descriptor.wrapperCache:
4265 wrapCall = dedent(
4267 result = obj->WrapObject(aCx, nullptr);
4268 if (!result) {
4269 return nullptr;
4273 else:
4274 wrapCall = dedent(
4276 if (!obj->WrapObject(aCx, nullptr, &result)) {
4277 return nullptr;
4282 return fill(
4284 // Protect the result from a moving GC in ~RefPtr
4285 JS::Rooted<JSObject*> result(aCx);
4286 { // Scope for the RefPtr
4287 RefPtr<${type}> obj = ${type}::ReadStructuredClone(aCx, aGlobal, aReader);
4288 if (!obj) {
4289 return nullptr;
4291 $*{wrapCall}
4293 return result;
4294 """,
4295 type=self.descriptor.nativeType,
4296 wrapCall=wrapCall,
4300 def CreateBindingJSObject(descriptor, properties):
4301 objDecl = "BindingJSObjectCreator<%s> creator(aCx);\n" % descriptor.nativeType
4303 # We don't always need to root obj, but there are a variety
4304 # of cases where we do, so for simplicity, just always root it.
4305 if descriptor.proxy:
4306 if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
4307 assert not descriptor.isMaybeCrossOriginObject()
4308 create = dedent(
4310 aObject->mExpandoAndGeneration.expando.setUndefined();
4311 JS::Rooted<JS::Value> expandoValue(aCx, JS::PrivateValue(&aObject->mExpandoAndGeneration));
4312 creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(),
4313 proto, /* aLazyProto = */ false, aObject,
4314 expandoValue, aReflector);
4317 else:
4318 if descriptor.isMaybeCrossOriginObject():
4319 proto = "nullptr"
4320 lazyProto = "true"
4321 else:
4322 proto = "proto"
4323 lazyProto = "false"
4324 create = fill(
4326 creator.CreateProxyObject(aCx, &sClass.mBase, DOMProxyHandler::getInstance(),
4327 ${proto}, /* aLazyProto = */ ${lazyProto},
4328 aObject, JS::UndefinedHandleValue, aReflector);
4329 """,
4330 proto=proto,
4331 lazyProto=lazyProto,
4333 else:
4334 create = dedent(
4336 creator.CreateObject(aCx, sClass.ToJSClass(), proto, aObject, aReflector);
4339 return (
4340 objDecl
4341 + create
4342 + dedent(
4344 if (!aReflector) {
4345 return false;
4352 def InitUnforgeablePropertiesOnHolder(
4353 descriptor, properties, failureCode, holderName="unforgeableHolder"
4356 Define the unforgeable properties on the unforgeable holder for
4357 the interface represented by descriptor.
4359 properties is a PropertyArrays instance.
4362 assert (
4363 properties.unforgeableAttrs.hasNonChromeOnly()
4364 or properties.unforgeableAttrs.hasChromeOnly()
4365 or properties.unforgeableMethods.hasNonChromeOnly()
4366 or properties.unforgeableMethods.hasChromeOnly()
4369 unforgeables = []
4371 defineUnforgeableAttrs = fill(
4373 if (!DefineLegacyUnforgeableAttributes(aCx, ${holderName}, %s)) {
4374 $*{failureCode}
4376 """,
4377 failureCode=failureCode,
4378 holderName=holderName,
4380 defineUnforgeableMethods = fill(
4382 if (!DefineLegacyUnforgeableMethods(aCx, ${holderName}, %s)) {
4383 $*{failureCode}
4385 """,
4386 failureCode=failureCode,
4387 holderName=holderName,
4390 unforgeableMembers = [
4391 (defineUnforgeableAttrs, properties.unforgeableAttrs),
4392 (defineUnforgeableMethods, properties.unforgeableMethods),
4394 for (template, array) in unforgeableMembers:
4395 if array.hasNonChromeOnly():
4396 unforgeables.append(CGGeneric(template % array.variableName(False)))
4397 if array.hasChromeOnly():
4398 unforgeables.append(
4399 CGIfWrapper(
4400 CGGeneric(template % array.variableName(True)),
4401 "nsContentUtils::ThreadsafeIsSystemCaller(aCx)",
4405 if descriptor.interface.getExtendedAttribute("LegacyUnforgeable"):
4406 # We do our undefined toPrimitive here, not as a regular property
4407 # because we don't have a concept of value props anywhere in IDL.
4408 unforgeables.append(
4409 CGGeneric(
4410 fill(
4412 JS::RootedId toPrimitive(aCx,
4413 SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::toPrimitive)));
4414 if (!JS_DefinePropertyById(aCx, ${holderName}, toPrimitive,
4415 JS::UndefinedHandleValue,
4416 JSPROP_READONLY | JSPROP_PERMANENT)) {
4417 $*{failureCode}
4419 """,
4420 failureCode=failureCode,
4421 holderName=holderName,
4426 return CGWrapper(CGList(unforgeables), pre="\n")
4429 def CopyUnforgeablePropertiesToInstance(descriptor, failureCode):
4431 Copy the unforgeable properties from the unforgeable holder for
4432 this interface to the instance object we have.
4434 assert not descriptor.isGlobal()
4436 if not descriptor.hasLegacyUnforgeableMembers:
4437 return ""
4439 copyCode = [
4440 CGGeneric(
4441 dedent(
4443 // Important: do unforgeable property setup after we have handed
4444 // over ownership of the C++ object to obj as needed, so that if
4445 // we fail and it ends up GCed it won't have problems in the
4446 // finalizer trying to drop its ownership of the C++ object.
4452 # For proxies, we want to define on the expando object, not directly on the
4453 # reflector, so we can make sure we don't get confused by named getters.
4454 if descriptor.proxy:
4455 copyCode.append(
4456 CGGeneric(
4457 fill(
4459 JS::Rooted<JSObject*> expando(aCx,
4460 DOMProxyHandler::EnsureExpandoObject(aCx, aReflector));
4461 if (!expando) {
4462 $*{failureCode}
4464 """,
4465 failureCode=failureCode,
4469 obj = "expando"
4470 else:
4471 obj = "aReflector"
4473 copyCode.append(
4474 CGGeneric(
4475 fill(
4477 JS::Rooted<JSObject*> unforgeableHolder(aCx,
4478 &JS::GetReservedSlot(canonicalProto, DOM_INTERFACE_PROTO_SLOTS_BASE).toObject());
4479 if (!JS_InitializePropertiesFromCompatibleNativeObject(aCx, ${obj}, unforgeableHolder)) {
4480 $*{failureCode}
4482 """,
4483 obj=obj,
4484 failureCode=failureCode,
4489 return CGWrapper(CGList(copyCode), pre="\n").define()
4492 def AssertInheritanceChain(descriptor):
4493 asserts = ""
4494 iface = descriptor.interface
4495 while iface:
4496 desc = descriptor.getDescriptor(iface.identifier.name)
4497 asserts += (
4498 "MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
4499 " reinterpret_cast<%s*>(aObject),\n"
4500 ' "Multiple inheritance for %s is broken.");\n'
4501 % (desc.nativeType, desc.nativeType, desc.nativeType)
4503 iface = iface.parent
4504 asserts += "MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n"
4505 return asserts
4508 def InitMemberSlots(descriptor, failureCode):
4510 Initialize member slots on our JS object if we're supposed to have some.
4512 Note that this is called after the SetWrapper() call in the
4513 wrapperCache case, since that can affect how our getters behave
4514 and we plan to invoke them here. So if we fail, we need to
4515 ClearWrapper.
4517 if not descriptor.interface.hasMembersInSlots():
4518 return ""
4519 return fill(
4521 if (!UpdateMemberSlots(aCx, aReflector, aObject)) {
4522 $*{failureCode}
4524 """,
4525 failureCode=failureCode,
4529 def DeclareProto(descriptor):
4531 Declare the canonicalProto and proto we have for our wrapping operation.
4533 preamble = dedent(
4535 JS::Handle<JSObject*> canonicalProto = GetProtoObjectHandle(aCx);
4536 if (!canonicalProto) {
4537 return false;
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.
4576 properties should be a PropertyArrays instance.
4579 def __init__(self, descriptor, properties):
4580 assert descriptor.interface.hasInterfacePrototypeObject()
4581 args = [
4582 Argument("JSContext*", "aCx"),
4583 Argument(descriptor.nativeType + "*", "aObject"),
4584 Argument("nsWrapperCache*", "aCache"),
4585 Argument("JS::Handle<JSObject*>", "aGivenProto"),
4586 Argument("JS::MutableHandle<JSObject*>", "aReflector"),
4588 CGAbstractMethod.__init__(self, descriptor, "Wrap", "bool", args)
4589 self.properties = properties
4591 def definition_body(self):
4592 failureCode = dedent(
4594 aCache->ReleaseWrapper(aObject);
4595 aCache->ClearWrapper();
4596 return false;
4600 if self.descriptor.proxy:
4601 finalize = "DOMProxyHandler::getInstance()->finalize"
4602 else:
4603 finalize = FINALIZE_HOOK_NAME
4605 return fill(
4607 static_assert(!std::is_base_of_v<NonRefcountedDOMObject, ${nativeType}>,
4608 "Shouldn't have wrappercached things that are not refcounted.");
4609 $*{assertInheritance}
4610 MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx));
4611 MOZ_ASSERT(!aCache->GetWrapper(),
4612 "You should probably not be using Wrap() directly; use "
4613 "GetOrCreateDOMReflector instead");
4615 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
4616 "nsISupports must be on our primary inheritance chain");
4618 // If the wrapper cache contains a dead reflector then finalize that
4619 // now, ensuring that the finalizer for the old reflector always
4620 // runs before the new reflector is created and attached. This
4621 // avoids the awkward situation where there are multiple reflector
4622 // objects that contain pointers to the same native.
4624 if (JSObject* oldReflector = aCache->GetWrapperMaybeDead()) {
4625 ${finalize}(nullptr /* unused */, oldReflector);
4626 MOZ_ASSERT(!aCache->GetWrapperMaybeDead());
4629 JS::Rooted<JSObject*> global(aCx, FindAssociatedGlobal(aCx, aObject->GetParentObject()));
4630 if (!global) {
4631 return false;
4633 MOZ_ASSERT(JS_IsGlobalObject(global));
4634 JS::AssertObjectIsNotGray(global);
4636 // That might have ended up wrapping us already, due to the wonders
4637 // of XBL. Check for that, and bail out as needed.
4638 aReflector.set(aCache->GetWrapper());
4639 if (aReflector) {
4640 #ifdef DEBUG
4641 AssertReflectorHasGivenProto(aCx, aReflector, aGivenProto);
4642 #endif // DEBUG
4643 return true;
4646 JSAutoRealm ar(aCx, global);
4647 $*{declareProto}
4649 $*{createObject}
4651 aCache->SetWrapper(aReflector);
4652 $*{unforgeable}
4653 $*{slots}
4654 creator.InitializationSucceeded();
4656 MOZ_ASSERT(aCache->GetWrapperPreserveColor() &&
4657 aCache->GetWrapperPreserveColor() == aReflector);
4658 // If proto != canonicalProto, we have to preserve our wrapper;
4659 // otherwise we won't be able to properly recreate it later, since
4660 // we won't know what proto to use. Note that we don't check
4661 // aGivenProto here, since it's entirely possible (and even
4662 // somewhat common) to have a non-null aGivenProto which is the
4663 // same as canonicalProto.
4664 if (proto != canonicalProto) {
4665 PreserveWrapper(aObject);
4668 return true;
4669 """,
4670 nativeType=self.descriptor.nativeType,
4671 assertInheritance=AssertInheritanceChain(self.descriptor),
4672 declareProto=DeclareProto(self.descriptor),
4673 createObject=CreateBindingJSObject(self.descriptor, self.properties),
4674 unforgeable=CopyUnforgeablePropertiesToInstance(
4675 self.descriptor, failureCode
4677 slots=InitMemberSlots(self.descriptor, failureCode),
4678 finalize=finalize,
4682 class CGWrapMethod(CGAbstractMethod):
4683 def __init__(self, descriptor):
4684 # XXX can we wrap if we don't have an interface prototype object?
4685 assert descriptor.interface.hasInterfacePrototypeObject()
4686 args = [
4687 Argument("JSContext*", "aCx"),
4688 Argument("T*", "aObject"),
4689 Argument("JS::Handle<JSObject*>", "aGivenProto"),
4691 CGAbstractMethod.__init__(
4692 self,
4693 descriptor,
4694 "Wrap",
4695 "JSObject*",
4696 args,
4697 inline=True,
4698 templateArgs=["class T"],
4701 def definition_body(self):
4702 return dedent(
4704 JS::Rooted<JSObject*> reflector(aCx);
4705 return Wrap(aCx, aObject, aObject, aGivenProto, &reflector) ? reflector.get() : nullptr;
4710 class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
4712 Create a wrapper JSObject for a given native that does not implement
4713 nsWrapperCache.
4715 properties should be a PropertyArrays instance.
4718 def __init__(self, descriptor, properties):
4719 # XXX can we wrap if we don't have an interface prototype object?
4720 assert descriptor.interface.hasInterfacePrototypeObject()
4721 args = [
4722 Argument("JSContext*", "aCx"),
4723 Argument(descriptor.nativeType + "*", "aObject"),
4724 Argument("JS::Handle<JSObject*>", "aGivenProto"),
4725 Argument("JS::MutableHandle<JSObject*>", "aReflector"),
4727 CGAbstractMethod.__init__(self, descriptor, "Wrap", "bool", args)
4728 self.properties = properties
4730 def definition_body(self):
4731 failureCode = "return false;\n"
4733 return fill(
4735 $*{assertions}
4736 MOZ_ASSERT_IF(aGivenProto, js::IsObjectInContextCompartment(aGivenProto, aCx));
4738 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
4739 $*{declareProto}
4741 $*{createObject}
4743 $*{unforgeable}
4745 $*{slots}
4747 creator.InitializationSucceeded();
4748 return true;
4749 """,
4750 assertions=AssertInheritanceChain(self.descriptor),
4751 declareProto=DeclareProto(self.descriptor),
4752 createObject=CreateBindingJSObject(self.descriptor, self.properties),
4753 unforgeable=CopyUnforgeablePropertiesToInstance(
4754 self.descriptor, failureCode
4756 slots=InitMemberSlots(self.descriptor, failureCode),
4760 class CGWrapGlobalMethod(CGAbstractMethod):
4762 Create a wrapper JSObject for a global. The global must implement
4763 nsWrapperCache.
4765 properties should be a PropertyArrays instance.
4768 def __init__(self, descriptor, properties):
4769 assert descriptor.interface.hasInterfacePrototypeObject()
4770 args = [
4771 Argument("JSContext*", "aCx"),
4772 Argument(descriptor.nativeType + "*", "aObject"),
4773 Argument("nsWrapperCache*", "aCache"),
4774 Argument("JS::RealmOptions&", "aOptions"),
4775 Argument("JSPrincipals*", "aPrincipal"),
4776 Argument("bool", "aInitStandardClasses"),
4777 Argument("JS::MutableHandle<JSObject*>", "aReflector"),
4779 CGAbstractMethod.__init__(self, descriptor, "Wrap", "bool", args)
4780 self.descriptor = descriptor
4781 self.properties = properties
4783 def definition_body(self):
4784 if self.properties.hasNonChromeOnly():
4785 properties = "sNativeProperties.Upcast()"
4786 else:
4787 properties = "nullptr"
4788 if self.properties.hasChromeOnly():
4789 chromeProperties = "nsContentUtils::ThreadsafeIsSystemCaller(aCx) ? sChromeOnlyNativeProperties.Upcast() : nullptr"
4790 else:
4791 chromeProperties = "nullptr"
4793 failureCode = dedent(
4795 aCache->ReleaseWrapper(aObject);
4796 aCache->ClearWrapper();
4797 return false;
4801 if self.descriptor.hasLegacyUnforgeableMembers:
4802 unforgeable = InitUnforgeablePropertiesOnHolder(
4803 self.descriptor, self.properties, failureCode, "aReflector"
4804 ).define()
4805 else:
4806 unforgeable = ""
4808 return fill(
4810 $*{assertions}
4811 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
4812 "nsISupports must be on our primary inheritance chain");
4814 if (!CreateGlobal<${nativeType}, GetProtoObjectHandle>(aCx,
4815 aObject,
4816 aCache,
4817 sClass.ToJSClass(),
4818 aOptions,
4819 aPrincipal,
4820 aInitStandardClasses,
4821 aReflector)) {
4822 $*{failureCode}
4825 // aReflector is a new global, so has a new realm. Enter it
4826 // before doing anything with it.
4827 JSAutoRealm ar(aCx, aReflector);
4829 if (!DefineProperties(aCx, aReflector, ${properties}, ${chromeProperties})) {
4830 $*{failureCode}
4832 $*{unforgeable}
4834 $*{slots}
4836 return true;
4837 """,
4838 assertions=AssertInheritanceChain(self.descriptor),
4839 nativeType=self.descriptor.nativeType,
4840 properties=properties,
4841 chromeProperties=chromeProperties,
4842 failureCode=failureCode,
4843 unforgeable=unforgeable,
4844 slots=InitMemberSlots(self.descriptor, failureCode),
4848 class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod):
4849 def __init__(self, descriptor):
4850 args = [
4851 Argument("JSContext*", "aCx"),
4852 Argument("JS::Handle<JSObject*>", "aWrapper"),
4853 Argument(descriptor.nativeType + "*", "aObject"),
4855 CGAbstractStaticMethod.__init__(
4856 self, descriptor, "UpdateMemberSlots", "bool", args
4859 def definition_body(self):
4860 body = "JS::Rooted<JS::Value> temp(aCx);\n" "JSJitGetterCallArgs args(&temp);\n"
4861 for m in self.descriptor.interface.members:
4862 if m.isAttr() and m.getExtendedAttribute("StoreInSlot"):
4863 # Skip doing this for the "window" and "self" attributes on the
4864 # Window interface, because those can't be gotten safely until
4865 # we have hooked it up correctly to the outer window. The
4866 # window code handles doing the get itself.
4867 if self.descriptor.interface.identifier.name == "Window" and (
4868 m.identifier.name == "window" or m.identifier.name == "self"
4870 continue
4871 body += fill(
4874 static_assert(${slot} < JS::shadow::Object::MAX_FIXED_SLOTS,
4875 "Not enough fixed slots to fit '${interface}.${member}. Ion's visitGetDOMMemberV/visitGetDOMMemberT assume StoreInSlot things are all in fixed slots.");
4876 if (!get_${member}(aCx, aWrapper, aObject, args)) {
4877 return false;
4879 // Getter handled setting our reserved slots
4880 """,
4881 slot=memberReservedSlot(m, self.descriptor),
4882 interface=self.descriptor.interface.identifier.name,
4883 member=m.identifier.name,
4886 body += "\nreturn true;\n"
4887 return body
4890 class CGClearCachedValueMethod(CGAbstractMethod):
4891 def __init__(self, descriptor, member):
4892 self.member = member
4893 # If we're StoreInSlot, we'll need to call the getter
4894 if member.getExtendedAttribute("StoreInSlot"):
4895 args = [Argument("JSContext*", "aCx")]
4896 returnType = "bool"
4897 else:
4898 args = []
4899 returnType = "void"
4900 args.append(Argument(descriptor.nativeType + "*", "aObject"))
4901 name = MakeClearCachedValueNativeName(member)
4902 CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
4904 def definition_body(self):
4905 slotIndex = memberReservedSlot(self.member, self.descriptor)
4906 if self.member.getExtendedAttribute("StoreInSlot"):
4907 # We have to root things and save the old value in case
4908 # regetting fails, so we can restore it.
4909 declObj = "JS::Rooted<JSObject*> obj(aCx);\n"
4910 noopRetval = " true"
4911 saveMember = (
4912 "JS::Rooted<JS::Value> oldValue(aCx, JS::GetReservedSlot(obj, %s));\n"
4913 % slotIndex
4915 regetMember = fill(
4917 JS::Rooted<JS::Value> temp(aCx);
4918 JSJitGetterCallArgs args(&temp);
4919 JSAutoRealm ar(aCx, obj);
4920 if (!get_${name}(aCx, obj, aObject, args)) {
4921 JS::SetReservedSlot(obj, ${slotIndex}, oldValue);
4922 return false;
4924 return true;
4925 """,
4926 name=self.member.identifier.name,
4927 slotIndex=slotIndex,
4929 else:
4930 declObj = "JSObject* obj;\n"
4931 noopRetval = ""
4932 saveMember = ""
4933 regetMember = ""
4935 if self.descriptor.wantsXrays:
4936 clearXrayExpandoSlots = fill(
4938 xpc::ClearXrayExpandoSlots(obj, ${xraySlotIndex});
4939 """,
4940 xraySlotIndex=memberXrayExpandoReservedSlot(
4941 self.member, self.descriptor
4944 else:
4945 clearXrayExpandoSlots = ""
4947 return fill(
4949 $*{declObj}
4950 obj = aObject->GetWrapper();
4951 if (!obj) {
4952 return${noopRetval};
4954 $*{saveMember}
4955 JS::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue());
4956 $*{clearXrayExpandoSlots}
4957 $*{regetMember}
4958 """,
4959 declObj=declObj,
4960 noopRetval=noopRetval,
4961 saveMember=saveMember,
4962 slotIndex=slotIndex,
4963 clearXrayExpandoSlots=clearXrayExpandoSlots,
4964 regetMember=regetMember,
4968 class CGCrossOriginProperties(CGThing):
4969 def __init__(self, descriptor):
4970 attrs = []
4971 chromeOnlyAttrs = []
4972 methods = []
4973 chromeOnlyMethods = []
4974 for m in descriptor.interface.members:
4975 if m.isAttr() and (
4976 m.getExtendedAttribute("CrossOriginReadable")
4977 or m.getExtendedAttribute("CrossOriginWritable")
4979 if m.isStatic():
4980 raise TypeError(
4981 "Don't know how to deal with static method %s"
4982 % m.identifier.name
4984 if PropertyDefiner.getControllingCondition(
4985 m, descriptor
4986 ).hasDisablers():
4987 raise TypeError(
4988 "Don't know how to deal with disabler for %s"
4989 % m.identifier.name
4991 if len(m.bindingAliases) > 0:
4992 raise TypeError(
4993 "Don't know how to deal with aliases for %s" % m.identifier.name
4995 if m.getExtendedAttribute("ChromeOnly") is not None:
4996 chromeOnlyAttrs.extend(AttrDefiner.attrData(m, overrideFlags="0"))
4997 else:
4998 attrs.extend(AttrDefiner.attrData(m, overrideFlags="0"))
4999 elif m.isMethod() and m.getExtendedAttribute("CrossOriginCallable"):
5000 if m.isStatic():
5001 raise TypeError(
5002 "Don't know how to deal with static method %s"
5003 % m.identifier.name
5005 if PropertyDefiner.getControllingCondition(
5006 m, descriptor
5007 ).hasDisablers():
5008 raise TypeError(
5009 "Don't know how to deal with disabler for %s"
5010 % m.identifier.name
5012 if len(m.aliases) > 0:
5013 raise TypeError(
5014 "Don't know how to deal with aliases for %s" % m.identifier.name
5016 if m.getExtendedAttribute("ChromeOnly") is not None:
5017 chromeOnlyMethods.append(
5018 MethodDefiner.methodData(
5019 m, descriptor, overrideFlags="JSPROP_READONLY"
5022 else:
5023 methods.append(
5024 MethodDefiner.methodData(
5025 m, descriptor, overrideFlags="JSPROP_READONLY"
5029 if len(attrs) > 0:
5030 self.attributeSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
5031 attrs,
5032 descriptor,
5033 AttrDefiner.formatSpec,
5034 " JS_PS_END\n",
5035 AttrDefiner.condition,
5036 functools.partial(AttrDefiner.specData, crossOriginOnly=True),
5038 else:
5039 self.attributeSpecs = [" JS_PS_END\n"]
5040 if len(methods) > 0:
5041 self.methodSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
5042 methods,
5043 descriptor,
5044 MethodDefiner.formatSpec,
5045 " JS_FS_END\n",
5046 MethodDefiner.condition,
5047 MethodDefiner.specData,
5049 else:
5050 self.methodSpecs = [" JS_FS_END\n"]
5052 if len(chromeOnlyAttrs) > 0:
5054 self.chromeOnlyAttributeSpecs,
5056 ) = PropertyDefiner.generatePrefableArrayValues(
5057 chromeOnlyAttrs,
5058 descriptor,
5059 AttrDefiner.formatSpec,
5060 " JS_PS_END\n",
5061 AttrDefiner.condition,
5062 functools.partial(AttrDefiner.specData, crossOriginOnly=True),
5064 else:
5065 self.chromeOnlyAttributeSpecs = []
5066 if len(chromeOnlyMethods) > 0:
5067 self.chromeOnlyMethodSpecs, _ = PropertyDefiner.generatePrefableArrayValues(
5068 chromeOnlyMethods,
5069 descriptor,
5070 MethodDefiner.formatSpec,
5071 " JS_FS_END\n",
5072 MethodDefiner.condition,
5073 MethodDefiner.specData,
5075 else:
5076 self.chromeOnlyMethodSpecs = []
5078 def declare(self):
5079 return dedent(
5081 extern const CrossOriginProperties sCrossOriginProperties;
5085 def define(self):
5086 def defineChromeOnly(name, specs, specType):
5087 if len(specs) == 0:
5088 return ("", "nullptr")
5089 name = "sChromeOnlyCrossOrigin" + name
5090 define = fill(
5092 static const ${specType} ${name}[] = {
5093 $*{specs}
5095 """,
5096 specType=specType,
5097 name=name,
5098 specs=",\n".join(specs),
5100 return (define, name)
5102 chromeOnlyAttributes = defineChromeOnly(
5103 "Attributes", self.chromeOnlyAttributeSpecs, "JSPropertySpec"
5105 chromeOnlyMethods = defineChromeOnly(
5106 "Methods", self.chromeOnlyMethodSpecs, "JSFunctionSpec"
5108 return fill(
5110 // We deliberately use brace-elision to make Visual Studio produce better initalization code.
5111 static const JSPropertySpec sCrossOriginAttributes[] = {
5112 $*{attributeSpecs}
5114 static const JSFunctionSpec sCrossOriginMethods[] = {
5115 $*{methodSpecs}
5117 $*{chromeOnlyAttributeSpecs}
5118 $*{chromeOnlyMethodSpecs}
5119 const CrossOriginProperties sCrossOriginProperties = {
5120 sCrossOriginAttributes,
5121 sCrossOriginMethods,
5122 ${chromeOnlyAttributes},
5123 ${chromeOnlyMethods}
5125 """,
5126 attributeSpecs=",\n".join(self.attributeSpecs),
5127 methodSpecs=",\n".join(self.methodSpecs),
5128 chromeOnlyAttributeSpecs=chromeOnlyAttributes[0],
5129 chromeOnlyMethodSpecs=chromeOnlyMethods[0],
5130 chromeOnlyAttributes=chromeOnlyAttributes[1],
5131 chromeOnlyMethods=chromeOnlyMethods[1],
5135 class CGCycleCollectionTraverseForOwningUnionMethod(CGAbstractMethod):
5137 ImplCycleCollectionUnlink for owning union type.
5140 def __init__(self, type):
5141 self.type = type
5142 args = [
5143 Argument("nsCycleCollectionTraversalCallback&", "aCallback"),
5144 Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion"),
5145 Argument("const char*", "aName"),
5146 Argument("uint32_t", "aFlags", "0"),
5148 CGAbstractMethod.__init__(
5149 self, None, "ImplCycleCollectionTraverse", "void", args
5152 def deps(self):
5153 return self.type.getDeps()
5155 def definition_body(self):
5156 memberNames = [
5157 getUnionMemberName(t)
5158 for t in self.type.flatMemberTypes
5159 if idlTypeNeedsCycleCollection(t)
5161 assert memberNames
5163 conditionTemplate = "aUnion.Is%s()"
5164 functionCallTemplate = (
5165 'ImplCycleCollectionTraverse(aCallback, aUnion.GetAs%s(), "m%s", aFlags);\n'
5168 ifStaments = (
5169 CGIfWrapper(CGGeneric(functionCallTemplate % (m, m)), conditionTemplate % m)
5170 for m in memberNames
5173 return CGElseChain(ifStaments).define()
5176 class CGCycleCollectionUnlinkForOwningUnionMethod(CGAbstractMethod):
5178 ImplCycleCollectionUnlink for owning union type.
5181 def __init__(self, type):
5182 self.type = type
5183 args = [Argument("%s&" % CGUnionStruct.unionTypeName(type, True), "aUnion")]
5184 CGAbstractMethod.__init__(self, None, "ImplCycleCollectionUnlink", "void", args)
5186 def deps(self):
5187 return self.type.getDeps()
5189 def definition_body(self):
5190 return "aUnion.Uninit();\n"
5193 builtinNames = {
5194 IDLType.Tags.bool: "bool",
5195 IDLType.Tags.int8: "int8_t",
5196 IDLType.Tags.int16: "int16_t",
5197 IDLType.Tags.int32: "int32_t",
5198 IDLType.Tags.int64: "int64_t",
5199 IDLType.Tags.uint8: "uint8_t",
5200 IDLType.Tags.uint16: "uint16_t",
5201 IDLType.Tags.uint32: "uint32_t",
5202 IDLType.Tags.uint64: "uint64_t",
5203 IDLType.Tags.unrestricted_float: "float",
5204 IDLType.Tags.float: "float",
5205 IDLType.Tags.unrestricted_double: "double",
5206 IDLType.Tags.double: "double",
5209 numericSuffixes = {
5210 IDLType.Tags.int8: "",
5211 IDLType.Tags.uint8: "",
5212 IDLType.Tags.int16: "",
5213 IDLType.Tags.uint16: "",
5214 IDLType.Tags.int32: "",
5215 IDLType.Tags.uint32: "U",
5216 IDLType.Tags.int64: "LL",
5217 IDLType.Tags.uint64: "ULL",
5218 IDLType.Tags.unrestricted_float: "F",
5219 IDLType.Tags.float: "F",
5220 IDLType.Tags.unrestricted_double: "",
5221 IDLType.Tags.double: "",
5225 def numericValue(t, v):
5226 if t == IDLType.Tags.unrestricted_double or t == IDLType.Tags.unrestricted_float:
5227 typeName = builtinNames[t]
5228 if v == float("inf"):
5229 return "mozilla::PositiveInfinity<%s>()" % typeName
5230 if v == float("-inf"):
5231 return "mozilla::NegativeInfinity<%s>()" % typeName
5232 if math.isnan(v):
5233 return "mozilla::UnspecifiedNaN<%s>()" % typeName
5234 return "%s%s" % (v, numericSuffixes[t])
5237 class CastableObjectUnwrapper:
5239 A class for unwrapping an object stored in a JS Value (or
5240 MutableHandle<Value> or Handle<Value>) named by the "source" and
5241 "mutableSource" arguments based on the passed-in descriptor and storing it
5242 in a variable called by the name in the "target" argument. The "source"
5243 argument should be able to produce a Value or Handle<Value>; the
5244 "mutableSource" argument should be able to produce a MutableHandle<Value>
5246 codeOnFailure is the code to run if unwrapping fails.
5248 If isCallbackReturnValue is "JSImpl" and our descriptor is also
5249 JS-implemented, fall back to just creating the right object if what we
5250 have isn't one already.
5253 def __init__(
5254 self,
5255 descriptor,
5256 source,
5257 mutableSource,
5258 target,
5259 codeOnFailure,
5260 exceptionCode=None,
5261 isCallbackReturnValue=False,
5263 self.substitution = {
5264 "type": descriptor.nativeType,
5265 "protoID": "prototypes::id::" + descriptor.name,
5266 "target": target,
5267 "codeOnFailure": codeOnFailure,
5268 "source": source,
5269 "mutableSource": mutableSource,
5272 if isCallbackReturnValue == "JSImpl" and descriptor.interface.isJSImplemented():
5273 exceptionCode = exceptionCode or codeOnFailure
5274 self.substitution["codeOnFailure"] = fill(
5276 // Be careful to not wrap random DOM objects here, even if
5277 // they're wrapped in opaque security wrappers for some reason.
5278 // XXXbz Wish we could check for a JS-implemented object
5279 // that already has a content reflection...
5280 if (!IsDOMObject(js::UncheckedUnwrap(&${source}.toObject()))) {
5281 nsCOMPtr<nsIGlobalObject> contentGlobal;
5282 JS::Rooted<JSObject*> callback(cx, CallbackOrNull());
5283 if (!callback ||
5284 !GetContentGlobalForJSImplementedObject(cx, callback, getter_AddRefs(contentGlobal))) {
5285 $*{exceptionCode}
5287 JS::Rooted<JSObject*> jsImplSourceObj(cx, &${source}.toObject());
5288 MOZ_RELEASE_ASSERT(!js::IsWrapper(jsImplSourceObj),
5289 "Don't return JS implementations from other compartments");
5290 JS::Rooted<JSObject*> jsImplSourceGlobal(cx, JS::GetNonCCWObjectGlobal(jsImplSourceObj));
5291 ${target} = new ${type}(jsImplSourceObj, jsImplSourceGlobal, contentGlobal);
5292 } else {
5293 $*{codeOnFailure}
5295 """,
5296 exceptionCode=exceptionCode,
5297 **self.substitution
5299 else:
5300 self.substitution["codeOnFailure"] = codeOnFailure
5302 def __str__(self):
5303 substitution = self.substitution.copy()
5304 substitution["codeOnFailure"] %= {
5305 "securityError": "rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO"
5307 return fill(
5310 // Our JSContext should be in the right global to do unwrapping in.
5311 nsresult rv = UnwrapObject<${protoID}, ${type}>(${mutableSource}, ${target}, cx);
5312 if (NS_FAILED(rv)) {
5313 $*{codeOnFailure}
5316 """,
5317 **substitution
5321 class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
5323 As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
5326 def __init__(
5327 self,
5328 descriptor,
5329 source,
5330 mutableSource,
5331 target,
5332 exceptionCode,
5333 isCallbackReturnValue,
5334 sourceDescription,
5336 CastableObjectUnwrapper.__init__(
5337 self,
5338 descriptor,
5339 source,
5340 mutableSource,
5341 target,
5342 'cx.ThrowErrorMessage<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("%s", "%s");\n'
5343 "%s"
5344 % (sourceDescription, descriptor.interface.identifier.name, exceptionCode),
5345 exceptionCode,
5346 isCallbackReturnValue,
5350 def getCallbackConversionInfo(
5351 type, idlObject, isMember, isCallbackReturnValue, isOptional
5354 Returns a tuple containing the declType, declArgs, and basic
5355 conversion for the given callback type, with the given callback
5356 idl object in the given context (isMember/isCallbackReturnValue/isOptional).
5358 name = idlObject.identifier.name
5360 # We can't use fast callbacks if isOptional because then we get an
5361 # Optional<RootedCallback> thing, which is not transparent to consumers.
5362 useFastCallback = not isMember and not isCallbackReturnValue and not isOptional
5363 if useFastCallback:
5364 name = "binding_detail::Fast%s" % name
5365 rootArgs = ""
5366 args = "&${val}.toObject(), JS::CurrentGlobalOrNull(cx)"
5367 else:
5368 rootArgs = dedent(
5370 JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject());
5371 JS::Rooted<JSObject*> tempGlobalRoot(cx, JS::CurrentGlobalOrNull(cx));
5374 args = "cx, tempRoot, tempGlobalRoot, GetIncumbentGlobal()"
5376 if type.nullable() or isCallbackReturnValue:
5377 declType = CGGeneric("RefPtr<%s>" % name)
5378 else:
5379 declType = CGGeneric("OwningNonNull<%s>" % name)
5381 if useFastCallback:
5382 declType = CGTemplatedType("RootedCallback", declType)
5383 declArgs = "cx"
5384 else:
5385 declArgs = None
5387 conversion = fill(
5389 { // scope for tempRoot and tempGlobalRoot if needed
5390 $*{rootArgs}
5391 $${declName} = new ${name}(${args});
5393 """,
5394 rootArgs=rootArgs,
5395 name=name,
5396 args=args,
5398 return (declType, declArgs, conversion)
5401 class JSToNativeConversionInfo:
5403 An object representing information about a JS-to-native conversion.
5406 def __init__(
5407 self,
5408 template,
5409 declType=None,
5410 holderType=None,
5411 dealWithOptional=False,
5412 declArgs=None,
5413 holderArgs=None,
5416 template: A string representing the conversion code. This will have
5417 template substitution performed on it as follows:
5419 ${val} is a handle to the JS::Value in question
5420 ${maybeMutableVal} May be a mutable handle to the JS::Value in
5421 question. This is only OK to use if ${val} is
5422 known to not be undefined.
5423 ${holderName} replaced by the holder's name, if any
5424 ${declName} replaced by the declaration's name
5425 ${haveValue} replaced by an expression that evaluates to a boolean
5426 for whether we have a JS::Value. Only used when
5427 defaultValue is not None or when True is passed for
5428 checkForValue to instantiateJSToNativeConversion.
5429 This expression may not be already-parenthesized, so if
5430 you use it with && or || make sure to put parens
5431 around it.
5432 ${passedToJSImpl} replaced by an expression that evaluates to a boolean
5433 for whether this value is being passed to a JS-
5434 implemented interface.
5436 declType: A CGThing representing the native C++ type we're converting
5437 to. This is allowed to be None if the conversion code is
5438 supposed to be used as-is.
5440 holderType: A CGThing representing the type of a "holder" which will
5441 hold a possible reference to the C++ thing whose type we
5442 returned in declType, or None if no such holder is needed.
5444 dealWithOptional: A boolean indicating whether the caller has to do
5445 optional-argument handling. This should only be set
5446 to true if the JS-to-native conversion is being done
5447 for an optional argument or dictionary member with no
5448 default value and if the returned template expects
5449 both declType and holderType to be wrapped in
5450 Optional<>, with ${declName} and ${holderName}
5451 adjusted to point to the Value() of the Optional, and
5452 Construct() calls to be made on the Optional<>s as
5453 needed.
5455 declArgs: If not None, the arguments to pass to the ${declName}
5456 constructor. These will have template substitution performed
5457 on them so you can use things like ${val}. This is a
5458 single string, not a list of strings.
5460 holderArgs: If not None, the arguments to pass to the ${holderName}
5461 constructor. These will have template substitution
5462 performed on them so you can use things like ${val}.
5463 This is a single string, not a list of strings.
5465 ${declName} must be in scope before the code from 'template' is entered.
5467 If holderType is not None then ${holderName} must be in scope before
5468 the code from 'template' is entered.
5470 assert isinstance(template, str)
5471 assert declType is None or isinstance(declType, CGThing)
5472 assert holderType is None or isinstance(holderType, CGThing)
5473 self.template = template
5474 self.declType = declType
5475 self.holderType = holderType
5476 self.dealWithOptional = dealWithOptional
5477 self.declArgs = declArgs
5478 self.holderArgs = holderArgs
5481 def getHandleDefault(defaultValue):
5482 tag = defaultValue.type.tag()
5483 if tag in numericSuffixes:
5484 # Some numeric literals require a suffix to compile without warnings
5485 return numericValue(tag, defaultValue.value)
5486 assert tag == IDLType.Tags.bool
5487 return toStringBool(defaultValue.value)
5490 def handleDefaultStringValue(defaultValue, method):
5492 Returns a string which ends up calling 'method' with a (char_t*, length)
5493 pair that sets this string default value. This string is suitable for
5494 passing as the second argument of handleDefault.
5496 assert (
5497 defaultValue.type.isDOMString()
5498 or defaultValue.type.isUSVString()
5499 or defaultValue.type.isUTF8String()
5500 or defaultValue.type.isByteString()
5502 # There shouldn't be any non-ASCII or embedded nulls in here; if
5503 # it ever sneaks in we will need to think about how to properly
5504 # represent that in the C++.
5505 assert all(ord(c) < 128 and ord(c) > 0 for c in defaultValue.value)
5506 if defaultValue.type.isByteString() or defaultValue.type.isUTF8String():
5507 prefix = ""
5508 else:
5509 prefix = "u"
5510 return fill(
5512 ${method}(${prefix}"${value}");
5513 """,
5514 method=method,
5515 prefix=prefix,
5516 value=defaultValue.value,
5520 def recordKeyType(recordType):
5521 assert recordType.keyType.isString()
5522 if recordType.keyType.isByteString() or recordType.keyType.isUTF8String():
5523 return "nsCString"
5524 return "nsString"
5527 def recordKeyDeclType(recordType):
5528 return CGGeneric(recordKeyType(recordType))
5531 def initializerForType(type):
5533 Get the right initializer for the given type for a data location where we
5534 plan to then initialize it from a JS::Value. Some types need to always be
5535 initialized even before we start the JS::Value-to-IDL-value conversion.
5537 Returns a string or None if no initialization is needed.
5539 if type.isObject():
5540 return "nullptr"
5541 # We could probably return CGDictionary.getNonInitializingCtorArg() for the
5542 # dictionary case, but code outside DictionaryBase subclasses can't use
5543 # that, so we can't do it across the board.
5544 return None
5547 # If this function is modified, modify CGNativeMember.getArg and
5548 # CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype
5549 # and holdertype we end up using, because it needs to be able to return the code
5550 # that will convert those to the actual return value of the callback function.
5551 def getJSToNativeConversionInfo(
5552 type,
5553 descriptorProvider,
5554 failureCode=None,
5555 isDefinitelyObject=False,
5556 isMember=False,
5557 isOptional=False,
5558 invalidEnumValueFatal=True,
5559 defaultValue=None,
5560 isNullOrUndefined=False,
5561 isKnownMissing=False,
5562 exceptionCode=None,
5563 lenientFloatCode=None,
5564 allowTreatNonCallableAsNull=False,
5565 isCallbackReturnValue=False,
5566 sourceDescription="value",
5567 nestingLevel="",
5570 Get a template for converting a JS value to a native object based on the
5571 given type and descriptor. If failureCode is given, then we're actually
5572 testing whether we can convert the argument to the desired type. That
5573 means that failures to convert due to the JS value being the wrong type of
5574 value need to use failureCode instead of throwing exceptions. Failures to
5575 convert that are due to JS exceptions (from toString or valueOf methods) or
5576 out of memory conditions need to throw exceptions no matter what
5577 failureCode is. However what actually happens when throwing an exception
5578 can be controlled by exceptionCode. The only requirement on that is that
5579 exceptionCode must end up doing a return, and every return from this
5580 function must happen via exceptionCode if exceptionCode is not None.
5582 If isDefinitelyObject is True, that means we have a value and the value
5583 tests true for isObject(), so we have no need to recheck that.
5585 If isNullOrUndefined is True, that means we have a value and the value
5586 tests true for isNullOrUndefined(), so we have no need to recheck that.
5588 If isKnownMissing is True, that means that we are known-missing, and for
5589 cases when we have a default value we only need to output the default value.
5591 if isMember is not False, we're being converted from a property of some JS
5592 object, not from an actual method argument, so we can't rely on our jsval
5593 being rooted or outliving us in any way. Callers can pass "Dictionary",
5594 "Variadic", "Sequence", or "OwningUnion" to indicate that the conversion is
5595 for something that is a dictionary member, a variadic argument, a sequence,
5596 or an owning union respectively.
5598 If isOptional is true, then we are doing conversion of an optional
5599 argument with no default value.
5601 invalidEnumValueFatal controls whether an invalid enum value conversion
5602 attempt will throw (if true) or simply return without doing anything (if
5603 false).
5605 If defaultValue is not None, it's the IDL default value for this conversion
5607 If isEnforceRange is true, we're converting an integer and throwing if the
5608 value is out of range.
5610 If isClamp is true, we're converting an integer and clamping if the
5611 value is out of range.
5613 If isAllowShared is false, we're converting a buffer source and throwing if
5614 it is a SharedArrayBuffer or backed by a SharedArrayBuffer.
5616 If lenientFloatCode is not None, it should be used in cases when
5617 we're a non-finite float that's not unrestricted.
5619 If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] and
5620 [LegacyTreatNonObjectAsNull] extended attributes on nullable callback functions
5621 will be honored.
5623 If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be
5624 adjusted to make it easier to return from a callback. Since that type is
5625 never directly observable by any consumers of the callback code, this is OK.
5626 Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior
5627 of the FailureFatalCastableObjectUnwrapper conversion; this is used for
5628 implementing auto-wrapping of JS-implemented return values from a
5629 JS-implemented interface.
5631 sourceDescription is a description of what this JS value represents, to be
5632 used in error reporting. Callers should assume that it might get placed in
5633 the middle of a sentence. If it ends up at the beginning of a sentence, its
5634 first character will be automatically uppercased.
5636 The return value from this function is a JSToNativeConversionInfo.
5638 # If we have a defaultValue then we're not actually optional for
5639 # purposes of what we need to be declared as.
5640 assert defaultValue is None or not isOptional
5642 # Also, we should not have a defaultValue if we know we're an object
5643 assert not isDefinitelyObject or defaultValue is None
5645 # And we can't both be an object and be null or undefined
5646 assert not isDefinitelyObject or not isNullOrUndefined
5648 isClamp = type.hasClamp()
5649 isEnforceRange = type.hasEnforceRange()
5650 isAllowShared = type.hasAllowShared()
5652 # If exceptionCode is not set, we'll just rethrow the exception we got.
5653 # Note that we can't just set failureCode to exceptionCode, because setting
5654 # failureCode will prevent pending exceptions from being set in cases when
5655 # they really should be!
5656 if exceptionCode is None:
5657 exceptionCode = "return false;\n"
5659 # Unfortunately, .capitalize() on a string will lowercase things inside the
5660 # string, which we do not want.
5661 def firstCap(string):
5662 return string[0].upper() + string[1:]
5664 # Helper functions for dealing with failures due to the JS value being the
5665 # wrong type of value
5666 def onFailureNotAnObject(failureCode):
5667 return CGGeneric(
5668 failureCode
5669 or (
5670 'cx.ThrowErrorMessage<MSG_NOT_OBJECT>("%s");\n'
5671 "%s" % (firstCap(sourceDescription), exceptionCode)
5675 def onFailureBadType(failureCode, typeName):
5676 return CGGeneric(
5677 failureCode
5678 or (
5679 'cx.ThrowErrorMessage<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("%s", "%s");\n'
5680 "%s" % (firstCap(sourceDescription), typeName, exceptionCode)
5684 # It's a failure in the committed-to conversion, not a failure to match up
5685 # to a type, so we don't want to use failureCode in here. We want to just
5686 # throw an exception unconditionally.
5687 def onFailureIsShared():
5688 return CGGeneric(
5689 'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_SHARED>("%s");\n'
5690 "%s" % (firstCap(sourceDescription), exceptionCode)
5693 def onFailureIsLarge():
5694 return CGGeneric(
5695 'cx.ThrowErrorMessage<MSG_TYPEDARRAY_IS_LARGE>("%s");\n'
5696 "%s" % (firstCap(sourceDescription), exceptionCode)
5699 def onFailureNotCallable(failureCode):
5700 return CGGeneric(
5701 failureCode
5702 or (
5703 'cx.ThrowErrorMessage<MSG_NOT_CALLABLE>("%s");\n'
5704 "%s" % (firstCap(sourceDescription), exceptionCode)
5708 # A helper function for handling default values. Takes a template
5709 # body and the C++ code to set the default value and wraps the
5710 # given template body in handling for the default value.
5711 def handleDefault(template, setDefault):
5712 if defaultValue is None:
5713 return template
5714 if isKnownMissing:
5715 return fill(
5718 // scope for any temporaries our default value setting needs.
5719 $*{setDefault}
5721 """,
5722 setDefault=setDefault,
5724 return fill(
5726 if ($${haveValue}) {
5727 $*{templateBody}
5728 } else {
5729 $*{setDefault}
5731 """,
5732 templateBody=template,
5733 setDefault=setDefault,
5736 # A helper function for wrapping up the template body for
5737 # possibly-nullable objecty stuff
5738 def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None):
5739 if isNullOrUndefined and type.nullable():
5740 # Just ignore templateBody and set ourselves to null.
5741 # Note that we don't have to worry about default values
5742 # here either, since we already examined this value.
5743 return codeToSetNull
5745 if not isDefinitelyObject:
5746 # Handle the non-object cases by wrapping up the whole
5747 # thing in an if cascade.
5748 if type.nullable():
5749 elifLine = "} else if (${val}.isNullOrUndefined()) {\n"
5750 elifBody = codeToSetNull
5751 else:
5752 elifLine = ""
5753 elifBody = ""
5755 # Note that $${val} below expands to ${val}. This string is
5756 # used as a template later, and val will be filled in then.
5757 templateBody = fill(
5759 if ($${val}.isObject()) {
5760 $*{templateBody}
5761 $*{elifLine}
5762 $*{elifBody}
5763 } else {
5764 $*{failureBody}
5766 """,
5767 templateBody=templateBody,
5768 elifLine=elifLine,
5769 elifBody=elifBody,
5770 failureBody=onFailureNotAnObject(failureCode).define(),
5773 if isinstance(defaultValue, IDLNullValue):
5774 assert type.nullable() # Parser should enforce this
5775 templateBody = handleDefault(templateBody, codeToSetNull)
5776 elif isinstance(defaultValue, IDLEmptySequenceValue):
5777 # Our caller will handle it
5778 pass
5779 else:
5780 assert defaultValue is None
5782 return templateBody
5784 # A helper function for converting things that look like a JSObject*.
5785 def handleJSObjectType(
5786 type, isMember, failureCode, exceptionCode, sourceDescription
5788 if not isMember:
5789 if isOptional:
5790 # We have a specialization of Optional that will use a
5791 # Rooted for the storage here.
5792 declType = CGGeneric("JS::Handle<JSObject*>")
5793 else:
5794 declType = CGGeneric("JS::Rooted<JSObject*>")
5795 declArgs = "cx"
5796 else:
5797 assert isMember in (
5798 "Sequence",
5799 "Variadic",
5800 "Dictionary",
5801 "OwningUnion",
5802 "Record",
5804 # We'll get traced by the sequence or dictionary or union tracer
5805 declType = CGGeneric("JSObject*")
5806 declArgs = None
5807 templateBody = "${declName} = &${val}.toObject();\n"
5809 # For JS-implemented APIs, we refuse to allow passing objects that the
5810 # API consumer does not subsume. The extra parens around
5811 # ($${passedToJSImpl}) suppress unreachable code warnings when
5812 # $${passedToJSImpl} is the literal `false`. But Apple is shipping a
5813 # buggy clang (clang 3.9) in Xcode 8.3, so there even the parens are not
5814 # enough. So we manually disable some warnings in clang.
5815 if (
5816 not isinstance(descriptorProvider, Descriptor)
5817 or descriptorProvider.interface.isJSImplemented()
5819 templateBody = (
5820 fill(
5822 #ifdef __clang__
5823 #pragma clang diagnostic push
5824 #pragma clang diagnostic ignored "-Wunreachable-code"
5825 #pragma clang diagnostic ignored "-Wunreachable-code-return"
5826 #endif // __clang__
5827 if (($${passedToJSImpl}) && !CallerSubsumes($${val})) {
5828 cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}");
5829 $*{exceptionCode}
5831 #ifdef __clang__
5832 #pragma clang diagnostic pop
5833 #endif // __clang__
5834 """,
5835 sourceDescription=sourceDescription,
5836 exceptionCode=exceptionCode,
5838 + templateBody
5841 setToNullCode = "${declName} = nullptr;\n"
5842 template = wrapObjectTemplate(templateBody, type, setToNullCode, failureCode)
5843 return JSToNativeConversionInfo(
5844 template, declType=declType, dealWithOptional=isOptional, declArgs=declArgs
5847 def incrementNestingLevel():
5848 if nestingLevel == "":
5849 return 1
5850 return nestingLevel + 1
5852 assert not (isEnforceRange and isClamp) # These are mutually exclusive
5854 if type.isSequence():
5855 assert not isEnforceRange and not isClamp and not isAllowShared
5857 if failureCode is None:
5858 notSequence = 'cx.ThrowErrorMessage<MSG_NOT_SEQUENCE>("%s");\n' "%s" % (
5859 firstCap(sourceDescription),
5860 exceptionCode,
5862 else:
5863 notSequence = failureCode
5865 nullable = type.nullable()
5866 # Be very careful not to change "type": we need it later
5867 if nullable:
5868 elementType = type.inner.inner
5869 else:
5870 elementType = type.inner
5872 # We want to use auto arrays if we can, but we have to be careful with
5873 # reallocation behavior for arrays. In particular, if we use auto
5874 # arrays for sequences and have a sequence of elements which are
5875 # themselves sequences or have sequences as members, we have a problem.
5876 # In that case, resizing the outermost AutoTArray to the right size
5877 # will memmove its elements, but AutoTArrays are not memmovable and
5878 # hence will end up with pointers to bogus memory, which is bad. To
5879 # deal with this, we typically map WebIDL sequences to our Sequence
5880 # type, which is in fact memmovable. The one exception is when we're
5881 # passing in a sequence directly as an argument without any sort of
5882 # optional or nullable complexity going on. In that situation, we can
5883 # use an AutoSequence instead. We have to keep using Sequence in the
5884 # nullable and optional cases because we don't want to leak the
5885 # AutoSequence type to consumers, which would be unavoidable with
5886 # Nullable<AutoSequence> or Optional<AutoSequence>.
5887 if isMember or isOptional or nullable or isCallbackReturnValue:
5888 sequenceClass = "Sequence"
5889 else:
5890 sequenceClass = "binding_detail::AutoSequence"
5892 # XXXbz we can't include the index in the sourceDescription, because
5893 # we don't really have a way to pass one in dynamically at runtime...
5894 elementInfo = getJSToNativeConversionInfo(
5895 elementType,
5896 descriptorProvider,
5897 isMember="Sequence",
5898 exceptionCode=exceptionCode,
5899 lenientFloatCode=lenientFloatCode,
5900 isCallbackReturnValue=isCallbackReturnValue,
5901 sourceDescription="element of %s" % sourceDescription,
5902 nestingLevel=incrementNestingLevel(),
5904 if elementInfo.dealWithOptional:
5905 raise TypeError("Shouldn't have optional things in sequences")
5906 if elementInfo.holderType is not None:
5907 raise TypeError("Shouldn't need holders for sequences")
5909 typeName = CGTemplatedType(sequenceClass, elementInfo.declType)
5910 sequenceType = typeName.define()
5911 if nullable:
5912 typeName = CGTemplatedType("Nullable", typeName)
5913 arrayRef = "${declName}.SetValue()"
5914 else:
5915 arrayRef = "${declName}"
5917 elementConversion = string.Template(elementInfo.template).substitute(
5919 "val": "temp" + str(nestingLevel),
5920 "maybeMutableVal": "&temp" + str(nestingLevel),
5921 "declName": "slot" + str(nestingLevel),
5922 # We only need holderName here to handle isExternal()
5923 # interfaces, which use an internal holder for the
5924 # conversion even when forceOwningType ends up true.
5925 "holderName": "tempHolder" + str(nestingLevel),
5926 "passedToJSImpl": "${passedToJSImpl}",
5930 elementInitializer = initializerForType(elementType)
5931 if elementInitializer is None:
5932 elementInitializer = ""
5933 else:
5934 elementInitializer = elementInitializer + ", "
5936 # NOTE: Keep this in sync with variadic conversions as needed
5937 templateBody = fill(
5939 JS::ForOfIterator iter${nestingLevel}(cx);
5940 if (!iter${nestingLevel}.init($${val}, JS::ForOfIterator::AllowNonIterable)) {
5941 $*{exceptionCode}
5943 if (!iter${nestingLevel}.valueIsIterable()) {
5944 $*{notSequence}
5946 ${sequenceType} &arr${nestingLevel} = ${arrayRef};
5947 JS::Rooted<JS::Value> temp${nestingLevel}(cx);
5948 while (true) {
5949 bool done${nestingLevel};
5950 if (!iter${nestingLevel}.next(&temp${nestingLevel}, &done${nestingLevel})) {
5951 $*{exceptionCode}
5953 if (done${nestingLevel}) {
5954 break;
5956 ${elementType}* slotPtr${nestingLevel} = arr${nestingLevel}.AppendElement(${elementInitializer}mozilla::fallible);
5957 if (!slotPtr${nestingLevel}) {
5958 JS_ReportOutOfMemory(cx);
5959 $*{exceptionCode}
5961 ${elementType}& slot${nestingLevel} = *slotPtr${nestingLevel};
5962 $*{elementConversion}
5964 """,
5965 exceptionCode=exceptionCode,
5966 notSequence=notSequence,
5967 sequenceType=sequenceType,
5968 arrayRef=arrayRef,
5969 elementType=elementInfo.declType.define(),
5970 elementConversion=elementConversion,
5971 elementInitializer=elementInitializer,
5972 nestingLevel=str(nestingLevel),
5975 templateBody = wrapObjectTemplate(
5976 templateBody, type, "${declName}.SetNull();\n", notSequence
5978 if isinstance(defaultValue, IDLEmptySequenceValue):
5979 if type.nullable():
5980 codeToSetEmpty = "${declName}.SetValue();\n"
5981 else:
5982 codeToSetEmpty = (
5983 "/* ${declName} array is already empty; nothing to do */\n"
5985 templateBody = handleDefault(templateBody, codeToSetEmpty)
5987 # Sequence arguments that might contain traceable things need
5988 # to get traced
5989 if not isMember and typeNeedsRooting(elementType):
5990 holderType = CGTemplatedType("SequenceRooter", elementInfo.declType)
5991 # If our sequence is nullable, this will set the Nullable to be
5992 # not-null, but that's ok because we make an explicit SetNull() call
5993 # on it as needed if our JS value is actually null.
5994 holderArgs = "cx, &%s" % arrayRef
5995 else:
5996 holderType = None
5997 holderArgs = None
5999 return JSToNativeConversionInfo(
6000 templateBody,
6001 declType=typeName,
6002 holderType=holderType,
6003 dealWithOptional=isOptional,
6004 holderArgs=holderArgs,
6007 if type.isRecord():
6008 assert not isEnforceRange and not isClamp and not isAllowShared
6009 if failureCode is None:
6010 notRecord = 'cx.ThrowErrorMessage<MSG_NOT_OBJECT>("%s");\n' "%s" % (
6011 firstCap(sourceDescription),
6012 exceptionCode,
6014 else:
6015 notRecord = failureCode
6017 nullable = type.nullable()
6018 # Be very careful not to change "type": we need it later
6019 if nullable:
6020 recordType = type.inner
6021 else:
6022 recordType = type
6023 valueType = recordType.inner
6025 valueInfo = getJSToNativeConversionInfo(
6026 valueType,
6027 descriptorProvider,
6028 isMember="Record",
6029 exceptionCode=exceptionCode,
6030 lenientFloatCode=lenientFloatCode,
6031 isCallbackReturnValue=isCallbackReturnValue,
6032 sourceDescription="value in %s" % sourceDescription,
6033 nestingLevel=incrementNestingLevel(),
6035 if valueInfo.dealWithOptional:
6036 raise TypeError("Shouldn't have optional things in record")
6037 if valueInfo.holderType is not None:
6038 raise TypeError("Shouldn't need holders for record")
6040 declType = CGTemplatedType(
6041 "Record", [recordKeyDeclType(recordType), valueInfo.declType]
6043 typeName = declType.define()
6044 if nullable:
6045 declType = CGTemplatedType("Nullable", declType)
6046 recordRef = "${declName}.SetValue()"
6047 else:
6048 recordRef = "${declName}"
6050 valueConversion = string.Template(valueInfo.template).substitute(
6052 "val": "temp",
6053 "maybeMutableVal": "&temp",
6054 "declName": "slot",
6055 # We only need holderName here to handle isExternal()
6056 # interfaces, which use an internal holder for the
6057 # conversion even when forceOwningType ends up true.
6058 "holderName": "tempHolder",
6059 "passedToJSImpl": "${passedToJSImpl}",
6063 keyType = recordKeyType(recordType)
6064 if recordType.keyType.isJSString():
6065 raise TypeError(
6066 "Have do deal with JSString record type, but don't know how"
6068 if recordType.keyType.isByteString() or recordType.keyType.isUTF8String():
6069 hashKeyType = "nsCStringHashKey"
6070 if recordType.keyType.isByteString():
6071 keyConversionFunction = "ConvertJSValueToByteString"
6072 else:
6073 keyConversionFunction = "ConvertJSValueToString"
6075 else:
6076 hashKeyType = "nsStringHashKey"
6077 if recordType.keyType.isDOMString():
6078 keyConversionFunction = "ConvertJSValueToString"
6079 else:
6080 assert recordType.keyType.isUSVString()
6081 keyConversionFunction = "ConvertJSValueToUSVString"
6083 templateBody = fill(
6085 auto& recordEntries = ${recordRef}.Entries();
6087 JS::Rooted<JSObject*> recordObj(cx, &$${val}.toObject());
6088 JS::RootedVector<jsid> ids(cx);
6089 if (!js::GetPropertyKeys(cx, recordObj,
6090 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, &ids)) {
6091 $*{exceptionCode}
6093 if (!recordEntries.SetCapacity(ids.length(), mozilla::fallible)) {
6094 JS_ReportOutOfMemory(cx);
6095 $*{exceptionCode}
6097 JS::Rooted<JS::Value> propNameValue(cx);
6098 JS::Rooted<JS::Value> temp(cx);
6099 JS::Rooted<jsid> curId(cx);
6100 JS::Rooted<JS::Value> idVal(cx);
6101 // Use a hashset to keep track of ids seen, to avoid
6102 // introducing nasty O(N^2) behavior scanning for them all the
6103 // time. Ideally we'd use a data structure with O(1) lookup
6104 // _and_ ordering for the MozMap, but we don't have one lying
6105 // around.
6106 nsTHashtable<${hashKeyType}> idsSeen;
6107 for (size_t i = 0; i < ids.length(); ++i) {
6108 curId = ids[i];
6110 JS::Rooted<JS::PropertyDescriptor> desc(cx);
6111 if (!JS_GetOwnPropertyDescriptorById(cx, recordObj, curId,
6112 &desc)) {
6113 $*{exceptionCode}
6116 if (!desc.object() /* == undefined in spec terms */ ||
6117 !desc.enumerable()) {
6118 continue;
6121 idVal = js::IdToValue(curId);
6122 ${keyType} propName;
6123 // This will just throw if idVal is a Symbol, like the spec says
6124 // to do.
6125 if (!${keyConversionFunction}(cx, idVal, "key of ${sourceDescription}", propName)) {
6126 $*{exceptionCode}
6129 if (!JS_GetPropertyById(cx, recordObj, curId, &temp)) {
6130 $*{exceptionCode}
6133 ${typeName}::EntryType* entry;
6134 if (!idsSeen.EnsureInserted(propName)) {
6135 // Find the existing entry.
6136 auto idx = recordEntries.IndexOf(propName);
6137 MOZ_ASSERT(idx != recordEntries.NoIndex,
6138 "Why is it not found?");
6139 // Now blow it away to make it look like it was just added
6140 // to the array, because it's not obvious that it's
6141 // safe to write to its already-initialized mValue via our
6142 // normal codegen conversions. For example, the value
6143 // could be a union and this would change its type, but
6144 // codegen assumes we won't do that.
6145 entry = recordEntries.ReconstructElementAt(idx);
6146 } else {
6147 // Safe to do an infallible append here, because we did a
6148 // SetCapacity above to the right capacity.
6149 entry = recordEntries.AppendElement();
6151 entry->mKey = propName;
6152 ${valueType}& slot = entry->mValue;
6153 $*{valueConversion}
6155 """,
6156 exceptionCode=exceptionCode,
6157 recordRef=recordRef,
6158 hashKeyType=hashKeyType,
6159 keyType=keyType,
6160 keyConversionFunction=keyConversionFunction,
6161 sourceDescription=sourceDescription,
6162 typeName=typeName,
6163 valueType=valueInfo.declType.define(),
6164 valueConversion=valueConversion,
6167 templateBody = wrapObjectTemplate(
6168 templateBody, type, "${declName}.SetNull();\n", notRecord
6171 declArgs = None
6172 holderType = None
6173 holderArgs = None
6174 # record arguments that might contain traceable things need
6175 # to get traced
6176 if not isMember and isCallbackReturnValue:
6177 # Go ahead and just convert directly into our actual return value
6178 declType = CGWrapper(declType, post="&")
6179 declArgs = "aRetVal"
6180 elif not isMember and typeNeedsRooting(valueType):
6181 holderType = CGTemplatedType(
6182 "RecordRooter", [recordKeyDeclType(recordType), valueInfo.declType]
6184 # If our record is nullable, this will set the Nullable to be
6185 # not-null, but that's ok because we make an explicit SetNull() call
6186 # on it as needed if our JS value is actually null.
6187 holderArgs = "cx, &%s" % recordRef
6189 return JSToNativeConversionInfo(
6190 templateBody,
6191 declType=declType,
6192 declArgs=declArgs,
6193 holderType=holderType,
6194 dealWithOptional=isOptional,
6195 holderArgs=holderArgs,
6198 if type.isUnion():
6199 nullable = type.nullable()
6200 if nullable:
6201 type = type.inner
6203 isOwningUnion = isMember or isCallbackReturnValue
6204 unionArgumentObj = "${declName}" if isOwningUnion else "${holderName}"
6205 if nullable:
6206 # If we're owning, we're a Nullable, which hasn't been told it has
6207 # a value. Otherwise we're an already-constructed Maybe.
6208 unionArgumentObj += ".SetValue()" if isOwningUnion else ".ref()"
6210 memberTypes = type.flatMemberTypes
6211 prettyNames = []
6213 interfaceMemberTypes = [t for t in memberTypes if t.isNonCallbackInterface()]
6214 if len(interfaceMemberTypes) > 0:
6215 interfaceObject = []
6216 for memberType in interfaceMemberTypes:
6217 name = getUnionMemberName(memberType)
6218 interfaceObject.append(
6219 CGGeneric(
6220 "(failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext"
6221 % (unionArgumentObj, name)
6224 prettyNames.append(memberType.prettyName())
6225 interfaceObject = CGWrapper(
6226 CGList(interfaceObject, " ||\n"),
6227 pre="done = ",
6228 post=";\n\n",
6229 reindent=True,
6231 else:
6232 interfaceObject = None
6234 sequenceObjectMemberTypes = [t for t in memberTypes if t.isSequence()]
6235 if len(sequenceObjectMemberTypes) > 0:
6236 assert len(sequenceObjectMemberTypes) == 1
6237 memberType = sequenceObjectMemberTypes[0]
6238 name = getUnionMemberName(memberType)
6239 sequenceObject = CGGeneric(
6240 "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
6241 % (unionArgumentObj, name)
6243 prettyNames.append(memberType.prettyName())
6244 else:
6245 sequenceObject = None
6247 callbackMemberTypes = [
6248 t for t in memberTypes if t.isCallback() or t.isCallbackInterface()
6250 if len(callbackMemberTypes) > 0:
6251 assert len(callbackMemberTypes) == 1
6252 memberType = callbackMemberTypes[0]
6253 name = getUnionMemberName(memberType)
6254 callbackObject = CGGeneric(
6255 "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
6256 % (unionArgumentObj, name)
6258 prettyNames.append(memberType.prettyName())
6259 else:
6260 callbackObject = None
6262 dictionaryMemberTypes = [t for t in memberTypes if t.isDictionary()]
6263 if len(dictionaryMemberTypes) > 0:
6264 assert len(dictionaryMemberTypes) == 1
6265 memberType = dictionaryMemberTypes[0]
6266 name = getUnionMemberName(memberType)
6267 setDictionary = CGGeneric(
6268 "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
6269 % (unionArgumentObj, name)
6271 prettyNames.append(memberType.prettyName())
6272 else:
6273 setDictionary = None
6275 recordMemberTypes = [t for t in memberTypes if t.isRecord()]
6276 if len(recordMemberTypes) > 0:
6277 assert len(recordMemberTypes) == 1
6278 memberType = recordMemberTypes[0]
6279 name = getUnionMemberName(memberType)
6280 recordObject = CGGeneric(
6281 "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext, ${passedToJSImpl})) || !tryNext;\n"
6282 % (unionArgumentObj, name)
6284 prettyNames.append(memberType.prettyName())
6285 else:
6286 recordObject = None
6288 objectMemberTypes = [t for t in memberTypes if t.isObject()]
6289 if len(objectMemberTypes) > 0:
6290 assert len(objectMemberTypes) == 1
6291 # Very important to NOT construct a temporary Rooted here, since the
6292 # SetToObject call can call a Rooted constructor and we need to keep
6293 # stack discipline for Rooted.
6294 object = CGGeneric(
6295 "if (!%s.SetToObject(cx, &${val}.toObject(), ${passedToJSImpl})) {\n"
6296 "%s"
6297 "}\n"
6298 "done = true;\n" % (unionArgumentObj, indent(exceptionCode))
6300 prettyNames.append(objectMemberTypes[0].prettyName())
6301 else:
6302 object = None
6304 hasObjectTypes = (
6305 interfaceObject
6306 or sequenceObject
6307 or callbackObject
6308 or object
6309 or recordObject
6311 if hasObjectTypes:
6312 # "object" is not distinguishable from other types
6313 assert not object or not (
6314 interfaceObject or sequenceObject or callbackObject or recordObject
6316 if sequenceObject or callbackObject:
6317 # An object can be both an sequence object and a callback or
6318 # dictionary, but we shouldn't have both in the union's members
6319 # because they are not distinguishable.
6320 assert not (sequenceObject and callbackObject)
6321 templateBody = CGElseChain([sequenceObject, callbackObject])
6322 else:
6323 templateBody = None
6324 if interfaceObject:
6325 assert not object
6326 if templateBody:
6327 templateBody = CGIfWrapper(templateBody, "!done")
6328 templateBody = CGList([interfaceObject, templateBody])
6329 else:
6330 templateBody = CGList([templateBody, object])
6332 if recordObject:
6333 templateBody = CGList(
6334 [templateBody, CGIfWrapper(recordObject, "!done")]
6337 templateBody = CGIfWrapper(templateBody, "${val}.isObject()")
6338 else:
6339 templateBody = CGGeneric()
6341 if setDictionary:
6342 assert not object
6343 templateBody = CGList([templateBody, CGIfWrapper(setDictionary, "!done")])
6345 stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
6346 numericTypes = [t for t in memberTypes if t.isNumeric()]
6347 booleanTypes = [t for t in memberTypes if t.isBoolean()]
6348 if stringTypes or numericTypes or booleanTypes:
6349 assert len(stringTypes) <= 1
6350 assert len(numericTypes) <= 1
6351 assert len(booleanTypes) <= 1
6353 # We will wrap all this stuff in a do { } while (0); so we
6354 # can use "break" for flow control.
6355 def getStringOrPrimitiveConversion(memberType):
6356 name = getUnionMemberName(memberType)
6357 return CGGeneric(
6358 "done = (failed = !%s.TrySetTo%s(cx, ${val}, tryNext)) || !tryNext;\n"
6359 "break;\n" % (unionArgumentObj, name)
6362 other = CGList([])
6363 stringConversion = [getStringOrPrimitiveConversion(t) for t in stringTypes]
6364 numericConversion = [
6365 getStringOrPrimitiveConversion(t) for t in numericTypes
6367 booleanConversion = [
6368 getStringOrPrimitiveConversion(t) for t in booleanTypes
6370 if stringConversion:
6371 if booleanConversion:
6372 other.append(
6373 CGIfWrapper(booleanConversion[0], "${val}.isBoolean()")
6375 if numericConversion:
6376 other.append(CGIfWrapper(numericConversion[0], "${val}.isNumber()"))
6377 other.append(stringConversion[0])
6378 elif numericConversion:
6379 if booleanConversion:
6380 other.append(
6381 CGIfWrapper(booleanConversion[0], "${val}.isBoolean()")
6383 other.append(numericConversion[0])
6384 else:
6385 assert booleanConversion
6386 other.append(booleanConversion[0])
6388 other = CGWrapper(
6389 CGIndenter(other), pre="do {\n", post="} while (false);\n"
6391 if hasObjectTypes or setDictionary:
6392 other = CGWrapper(CGIndenter(other), "{\n", post="}\n")
6393 if object:
6394 templateBody = CGElseChain([templateBody, other])
6395 else:
6396 other = CGWrapper(other, pre="if (!done) ")
6397 templateBody = CGList([templateBody, other])
6398 else:
6399 assert templateBody.define() == ""
6400 templateBody = other
6401 else:
6402 other = None
6404 templateBody = CGWrapper(
6405 templateBody, pre="bool done = false, failed = false, tryNext;\n"
6407 throw = CGGeneric(
6408 fill(
6410 if (failed) {
6411 $*{exceptionCode}
6413 if (!done) {
6414 cx.ThrowErrorMessage<MSG_NOT_IN_UNION>("${desc}", "${names}");
6415 $*{exceptionCode}
6417 """,
6418 exceptionCode=exceptionCode,
6419 desc=firstCap(sourceDescription),
6420 names=", ".join(prettyNames),
6424 templateBody = CGWrapper(
6425 CGIndenter(CGList([templateBody, throw])), pre="{\n", post="}\n"
6428 typeName = CGUnionStruct.unionTypeDecl(type, isOwningUnion)
6429 argumentTypeName = typeName + "Argument"
6430 if nullable:
6431 typeName = "Nullable<" + typeName + " >"
6433 def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
6434 nullTest = "%s${val}.isNullOrUndefined()" % extraConditionForNull
6435 return CGIfElseWrapper(
6436 nullTest, CGGeneric("%s.SetNull();\n" % setToNullVar), templateBody
6439 if type.hasNullableType:
6440 assert not nullable
6441 # Make sure to handle a null default value here
6442 if defaultValue and isinstance(defaultValue, IDLNullValue):
6443 assert defaultValue.type == type
6444 extraConditionForNull = "!(${haveValue}) || "
6445 else:
6446 extraConditionForNull = ""
6447 templateBody = handleNull(
6448 templateBody,
6449 unionArgumentObj,
6450 extraConditionForNull=extraConditionForNull,
6453 declType = CGGeneric(typeName)
6454 if isOwningUnion:
6455 holderType = None
6456 else:
6457 holderType = CGGeneric(argumentTypeName)
6458 if nullable:
6459 holderType = CGTemplatedType("Maybe", holderType)
6461 # If we're isOptional and not nullable the normal optional handling will
6462 # handle lazy construction of our holder. If we're nullable and not
6463 # owning we do it all by hand because we do not want our holder
6464 # constructed if we're null. But if we're owning we don't have a
6465 # holder anyway, so we can do the normal Optional codepath.
6466 declLoc = "${declName}"
6467 constructDecl = None
6468 if nullable:
6469 if isOptional and not isOwningUnion:
6470 holderArgs = "${declName}.Value().SetValue()"
6471 declType = CGTemplatedType("Optional", declType)
6472 constructDecl = CGGeneric("${declName}.Construct();\n")
6473 declLoc = "${declName}.Value()"
6474 else:
6475 holderArgs = "${declName}.SetValue()"
6476 if holderType is not None:
6477 constructHolder = CGGeneric("${holderName}.emplace(%s);\n" % holderArgs)
6478 else:
6479 constructHolder = None
6480 # Don't need to pass those args when the holder is being constructed
6481 holderArgs = None
6482 else:
6483 holderArgs = "${declName}"
6484 constructHolder = None
6486 if not isMember and isCallbackReturnValue:
6487 declType = CGWrapper(declType, post="&")
6488 declArgs = "aRetVal"
6489 else:
6490 declArgs = None
6492 if (
6493 defaultValue
6494 and not isinstance(defaultValue, IDLNullValue)
6495 and not isinstance(defaultValue, IDLDefaultDictionaryValue)
6497 tag = defaultValue.type.tag()
6499 if tag in numericSuffixes or tag is IDLType.Tags.bool:
6500 defaultStr = getHandleDefault(defaultValue)
6501 # Make sure we actually construct the thing inside the nullable.
6502 value = declLoc + (".SetValue()" if nullable else "")
6503 name = getUnionMemberName(defaultValue.type)
6504 default = CGGeneric(
6505 "%s.RawSetAs%s() = %s;\n" % (value, name, defaultStr)
6507 elif isinstance(defaultValue, IDLEmptySequenceValue):
6508 name = getUnionMemberName(defaultValue.type)
6509 # Make sure we actually construct the thing inside the nullable.
6510 value = declLoc + (".SetValue()" if nullable else "")
6511 # It's enough to set us to the right type; that will
6512 # create an empty array, which is all we need here.
6513 default = CGGeneric("%s.RawSetAs%s();\n" % (value, name))
6514 elif defaultValue.type.isEnum():
6515 name = getUnionMemberName(defaultValue.type)
6516 # Make sure we actually construct the thing inside the nullable.
6517 value = declLoc + (".SetValue()" if nullable else "")
6518 default = CGGeneric(
6519 "%s.RawSetAs%s() = %s::%s;\n"
6521 value,
6522 name,
6523 defaultValue.type.inner.identifier.name,
6524 getEnumValueName(defaultValue.value),
6527 else:
6528 default = CGGeneric(
6529 handleDefaultStringValue(
6530 defaultValue, "%s.SetStringLiteral" % unionArgumentObj
6534 templateBody = CGIfElseWrapper("!(${haveValue})", default, templateBody)
6536 templateBody = CGList([constructHolder, templateBody])
6538 if nullable:
6539 if defaultValue:
6540 if isinstance(defaultValue, IDLNullValue):
6541 extraConditionForNull = "!(${haveValue}) || "
6542 else:
6543 extraConditionForNull = "(${haveValue}) && "
6544 else:
6545 extraConditionForNull = ""
6546 templateBody = handleNull(
6547 templateBody, declLoc, extraConditionForNull=extraConditionForNull
6549 elif (
6550 not type.hasNullableType
6551 and defaultValue
6552 and isinstance(defaultValue, IDLDefaultDictionaryValue)
6554 assert type.hasDictionaryType()
6555 assert defaultValue.type.isDictionary()
6556 if not isOwningUnion and typeNeedsRooting(defaultValue.type):
6557 ctorArgs = "cx"
6558 else:
6559 ctorArgs = ""
6560 initDictionaryWithNull = CGIfWrapper(
6561 CGGeneric("return false;\n"),
6563 '!%s.RawSetAs%s(%s).Init(cx, JS::NullHandleValue, "Member of %s")'
6565 declLoc,
6566 getUnionMemberName(defaultValue.type),
6567 ctorArgs,
6568 type.prettyName(),
6572 templateBody = CGIfElseWrapper(
6573 "!(${haveValue})", initDictionaryWithNull, templateBody
6576 templateBody = CGList([constructDecl, templateBody])
6578 return JSToNativeConversionInfo(
6579 templateBody.define(),
6580 declType=declType,
6581 declArgs=declArgs,
6582 holderType=holderType,
6583 holderArgs=holderArgs,
6584 dealWithOptional=isOptional and (not nullable or isOwningUnion),
6587 if type.isPromise():
6588 assert not type.nullable()
6589 assert defaultValue is None
6591 # We always have to hold a strong ref to Promise here, because
6592 # Promise::resolve returns an addrefed thing.
6593 argIsPointer = isCallbackReturnValue
6594 if argIsPointer:
6595 declType = CGGeneric("RefPtr<Promise>")
6596 else:
6597 declType = CGGeneric("OwningNonNull<Promise>")
6599 # Per spec, what we're supposed to do is take the original
6600 # Promise.resolve and call it with the original Promise as this
6601 # value to make a Promise out of whatever value we actually have
6602 # here. The question is which global we should use. There are
6603 # several cases to consider:
6605 # 1) Normal call to API with a Promise argument. This is a case the
6606 # spec covers, and we should be using the current Realm's
6607 # Promise. That means the current compartment.
6608 # 2) Call to API with a Promise argument over Xrays. In practice,
6609 # this sort of thing seems to be used for giving an API
6610 # implementation a way to wait for conclusion of an asyc
6611 # operation, _not_ to expose the Promise to content code. So we
6612 # probably want to allow callers to use such an API in a
6613 # "natural" way, by passing chrome-side promises; indeed, that
6614 # may be all that the caller has to represent their async
6615 # operation. That means we really need to do the
6616 # Promise.resolve() in the caller (chrome) compartment: if we do
6617 # it in the content compartment, we will try to call .then() on
6618 # the chrome promise while in the content compartment, which will
6619 # throw and we'll just get a rejected Promise. Note that this is
6620 # also the reason why a caller who has a chrome Promise
6621 # representing an async operation can't itself convert it to a
6622 # content-side Promise (at least not without some serious
6623 # gyrations).
6624 # 3) Promise return value from a callback or callback interface.
6625 # Per spec, this should use the Realm of the callback object. In
6626 # our case, that's the compartment of the underlying callback,
6627 # not the current compartment (which may be the compartment of
6628 # some cross-compartment wrapper around said callback).
6629 # 4) Return value from a JS-implemented interface. In this case we
6630 # have a problem. Our current compartment is the compartment of
6631 # the JS implementation. But if the JS implementation returned
6632 # a page-side Promise (which is a totally sane thing to do, and
6633 # in fact the right thing to do given that this return value is
6634 # going right to content script) then we don't want to
6635 # Promise.resolve with our current compartment Promise, because
6636 # that will wrap it up in a chrome-side Promise, which is
6637 # decidedly _not_ what's desired here. So in that case we
6638 # should really unwrap the return value and use the global of
6639 # the result. CheckedUnwrapStatic should be good enough for that;
6640 # if it fails, then we're failing unwrap while in a
6641 # system-privileged compartment, so presumably we have a dead
6642 # object wrapper. Just error out. Do NOT fall back to using
6643 # the current compartment instead: that will return a
6644 # system-privileged rejected (because getting .then inside
6645 # resolve() failed) Promise to the caller, which they won't be
6646 # able to touch. That's not helpful. If we error out, on the
6647 # other hand, they will get a content-side rejected promise.
6648 # Same thing if the value returned is not even an object.
6649 if isCallbackReturnValue == "JSImpl":
6650 # Case 4 above. Note that globalObj defaults to the current
6651 # compartment global. Note that we don't use $*{exceptionCode}
6652 # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED)
6653 # which we don't really want here.
6654 assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n"
6655 getPromiseGlobal = fill(
6657 if (!$${val}.isObject()) {
6658 aRv.ThrowTypeError<MSG_NOT_OBJECT>("${sourceDescription}");
6659 return nullptr;
6661 JSObject* unwrappedVal = js::CheckedUnwrapStatic(&$${val}.toObject());
6662 if (!unwrappedVal) {
6663 // A slight lie, but not much of one, for a dead object wrapper.
6664 aRv.ThrowTypeError<MSG_NOT_OBJECT>("${sourceDescription}");
6665 return nullptr;
6667 globalObj = JS::GetNonCCWObjectGlobal(unwrappedVal);
6668 """,
6669 sourceDescription=sourceDescription,
6671 elif isCallbackReturnValue == "Callback":
6672 getPromiseGlobal = dedent(
6674 // We basically want our entry global here. Play it safe
6675 // and use GetEntryGlobal() to get it, with whatever
6676 // principal-clamping it ends up doing.
6677 globalObj = GetEntryGlobal()->GetGlobalJSObject();
6680 else:
6681 getPromiseGlobal = dedent(
6683 globalObj = JS::CurrentGlobalOrNull(cx);
6687 templateBody = fill(
6689 { // Scope for our GlobalObject, FastErrorResult, JSAutoRealm,
6690 // etc.
6692 JS::Rooted<JSObject*> globalObj(cx);
6693 $*{getPromiseGlobal}
6694 JSAutoRealm ar(cx, globalObj);
6695 GlobalObject promiseGlobal(cx, globalObj);
6696 if (promiseGlobal.Failed()) {
6697 $*{exceptionCode}
6700 JS::Rooted<JS::Value> valueToResolve(cx, $${val});
6701 if (!JS_WrapValue(cx, &valueToResolve)) {
6702 $*{exceptionCode}
6704 binding_detail::FastErrorResult promiseRv;
6705 nsCOMPtr<nsIGlobalObject> global =
6706 do_QueryInterface(promiseGlobal.GetAsSupports());
6707 if (!global) {
6708 promiseRv.Throw(NS_ERROR_UNEXPECTED);
6709 MOZ_ALWAYS_TRUE(promiseRv.MaybeSetPendingException(cx));
6710 $*{exceptionCode}
6712 $${declName} = Promise::Resolve(global, cx, valueToResolve,
6713 promiseRv);
6714 if (promiseRv.MaybeSetPendingException(cx)) {
6715 $*{exceptionCode}
6718 """,
6719 getPromiseGlobal=getPromiseGlobal,
6720 exceptionCode=exceptionCode,
6723 return JSToNativeConversionInfo(
6724 templateBody, declType=declType, dealWithOptional=isOptional
6727 if type.isGeckoInterface():
6728 assert not isEnforceRange and not isClamp and not isAllowShared
6730 descriptor = descriptorProvider.getDescriptor(
6731 type.unroll().inner.identifier.name
6734 assert descriptor.nativeType != "JSObject"
6736 if descriptor.interface.isCallback():
6737 (declType, declArgs, conversion) = getCallbackConversionInfo(
6738 type, descriptor.interface, isMember, isCallbackReturnValue, isOptional
6740 template = wrapObjectTemplate(
6741 conversion, type, "${declName} = nullptr;\n", failureCode
6743 return JSToNativeConversionInfo(
6744 template,
6745 declType=declType,
6746 declArgs=declArgs,
6747 dealWithOptional=isOptional,
6750 if descriptor.interface.identifier.name == "WindowProxy":
6751 declType = CGGeneric("mozilla::dom::WindowProxyHolder")
6752 if type.nullable():
6753 declType = CGTemplatedType("Nullable", declType)
6754 windowProxyHolderRef = "${declName}.SetValue()"
6755 else:
6756 windowProxyHolderRef = "${declName}"
6758 failureCode = onFailureBadType(
6759 failureCode, descriptor.interface.identifier.name
6760 ).define()
6761 templateBody = fill(
6763 JS::Rooted<JSObject*> source(cx, &$${val}.toObject());
6764 if (NS_FAILED(UnwrapWindowProxyArg(cx, source, ${windowProxyHolderRef}))) {
6765 $*{onFailure}
6767 """,
6768 windowProxyHolderRef=windowProxyHolderRef,
6769 onFailure=failureCode,
6771 templateBody = wrapObjectTemplate(
6772 templateBody, type, "${declName}.SetNull();\n", failureCode
6774 return JSToNativeConversionInfo(
6775 templateBody, declType=declType, dealWithOptional=isOptional
6778 # This is an interface that we implement as a concrete class
6779 # or an XPCOM interface.
6781 # Allow null pointers for nullable types and old-binding classes, and
6782 # use an RefPtr or raw pointer for callback return values to make
6783 # them easier to return.
6784 argIsPointer = (
6785 type.nullable() or type.unroll().inner.isExternal() or isCallbackReturnValue
6788 # Sequence and dictionary members, as well as owning unions (which can
6789 # appear here as return values in JS-implemented interfaces) have to
6790 # hold a strong ref to the thing being passed down. Those all set
6791 # isMember.
6793 # Also, callback return values always end up addrefing anyway, so there
6794 # is no point trying to avoid it here and it makes other things simpler
6795 # since we can assume the return value is a strong ref.
6796 assert not descriptor.interface.isCallback()
6797 forceOwningType = isMember or isCallbackReturnValue
6799 typeName = descriptor.nativeType
6800 typePtr = typeName + "*"
6802 # Compute a few things:
6803 # - declType is the type we want to return as the first element of our
6804 # tuple.
6805 # - holderType is the type we want to return as the third element
6806 # of our tuple.
6808 # Set up some sensible defaults for these things insofar as we can.
6809 holderType = None
6810 if argIsPointer:
6811 if forceOwningType:
6812 declType = "RefPtr<" + typeName + ">"
6813 else:
6814 declType = typePtr
6815 else:
6816 if forceOwningType:
6817 declType = "OwningNonNull<" + typeName + ">"
6818 else:
6819 declType = "NonNull<" + typeName + ">"
6821 templateBody = ""
6822 if forceOwningType:
6823 templateBody += fill(
6825 static_assert(IsRefcounted<${typeName}>::value, "We can only store refcounted classes.");
6826 """,
6827 typeName=typeName,
6830 if not descriptor.interface.isExternal():
6831 if failureCode is not None:
6832 templateBody += str(
6833 CastableObjectUnwrapper(
6834 descriptor,
6835 "${val}",
6836 "${maybeMutableVal}",
6837 "${declName}",
6838 failureCode,
6841 else:
6842 templateBody += str(
6843 FailureFatalCastableObjectUnwrapper(
6844 descriptor,
6845 "${val}",
6846 "${maybeMutableVal}",
6847 "${declName}",
6848 exceptionCode,
6849 isCallbackReturnValue,
6850 firstCap(sourceDescription),
6853 else:
6854 # External interface. We always have a holder for these, because we
6855 # don't actually know whether we have to addref when unwrapping or not.
6856 # So we just pass an getter_AddRefs(RefPtr) to XPConnect and if we'll
6857 # need a release it'll put a non-null pointer in there.
6858 if forceOwningType:
6859 # Don't return a holderType in this case; our declName
6860 # will just own stuff.
6861 templateBody += "RefPtr<" + typeName + "> ${holderName};\n"
6862 else:
6863 holderType = "RefPtr<" + typeName + ">"
6864 templateBody += (
6865 "JS::Rooted<JSObject*> source(cx, &${val}.toObject());\n"
6866 + "if (NS_FAILED(UnwrapArg<"
6867 + typeName
6868 + ">(cx, source, getter_AddRefs(${holderName})))) {\n"
6870 templateBody += CGIndenter(
6871 onFailureBadType(failureCode, descriptor.interface.identifier.name)
6872 ).define()
6873 templateBody += "}\n" "MOZ_ASSERT(${holderName});\n"
6875 # And store our value in ${declName}
6876 templateBody += "${declName} = ${holderName};\n"
6878 # Just pass failureCode, not onFailureBadType, here, so we'll report
6879 # the thing as not an object as opposed to not implementing whatever
6880 # our interface is.
6881 templateBody = wrapObjectTemplate(
6882 templateBody, type, "${declName} = nullptr;\n", failureCode
6885 declType = CGGeneric(declType)
6886 if holderType is not None:
6887 holderType = CGGeneric(holderType)
6888 return JSToNativeConversionInfo(
6889 templateBody,
6890 declType=declType,
6891 holderType=holderType,
6892 dealWithOptional=isOptional,
6895 if type.isSpiderMonkeyInterface():
6896 assert not isEnforceRange and not isClamp
6897 name = type.unroll().name # unroll() because it may be nullable
6898 interfaceType = CGGeneric(name)
6899 declType = interfaceType
6900 if type.nullable():
6901 declType = CGTemplatedType("Nullable", declType)
6902 objRef = "${declName}.SetValue()"
6903 else:
6904 objRef = "${declName}"
6906 # Again, this is a bit strange since we are actually building a
6907 # template string here. ${objRef} and $*{badType} below are filled in
6908 # right now; $${val} expands to ${val}, to be filled in later.
6909 template = fill(
6911 if (!${objRef}.Init(&$${val}.toObject())) {
6912 $*{badType}
6914 """,
6915 objRef=objRef,
6916 badType=onFailureBadType(failureCode, type.name).define(),
6918 if type.isBufferSource():
6919 if type.isArrayBuffer():
6920 isSharedMethod = "JS::IsSharedArrayBufferObject"
6921 isLargeMethod = "JS::IsLargeArrayBufferMaybeShared"
6922 else:
6923 assert type.isArrayBufferView() or type.isTypedArray()
6924 isSharedMethod = "JS::IsArrayBufferViewShared"
6925 isLargeMethod = "JS::IsLargeArrayBufferView"
6926 if not isAllowShared:
6927 template += fill(
6929 if (${isSharedMethod}(${objRef}.Obj())) {
6930 $*{badType}
6932 """,
6933 isSharedMethod=isSharedMethod,
6934 objRef=objRef,
6935 badType=onFailureIsShared().define(),
6937 # For now reject large (> 2 GB) ArrayBuffers and ArrayBufferViews.
6938 # Supporting this will require changing dom::TypedArray and
6939 # consumers.
6940 template += fill(
6942 if (${isLargeMethod}(${objRef}.Obj())) {
6943 $*{badType}
6945 """,
6946 isLargeMethod=isLargeMethod,
6947 objRef=objRef,
6948 badType=onFailureIsLarge().define(),
6950 template = wrapObjectTemplate(
6951 template, type, "${declName}.SetNull();\n", failureCode
6953 if not isMember:
6954 # This is a bit annoying. In a union we don't want to have a
6955 # holder, since unions don't support that. But if we're optional we
6956 # want to have a holder, so that the callee doesn't see
6957 # Optional<RootedSpiderMonkeyInterface<InterfaceType>>. So do a
6958 # holder if we're optional and use a RootedSpiderMonkeyInterface
6959 # otherwise.
6960 if isOptional:
6961 holderType = CGTemplatedType(
6962 "SpiderMonkeyInterfaceRooter", interfaceType
6964 # If our SpiderMonkey interface is nullable, this will set the
6965 # Nullable to be not-null, but that's ok because we make an
6966 # explicit SetNull() call on it as needed if our JS value is
6967 # actually null. XXXbz Because "Maybe" takes const refs for
6968 # constructor arguments, we can't pass a reference here; have
6969 # to pass a pointer.
6970 holderArgs = "cx, &%s" % objRef
6971 declArgs = None
6972 else:
6973 holderType = None
6974 holderArgs = None
6975 declType = CGTemplatedType("RootedSpiderMonkeyInterface", declType)
6976 declArgs = "cx"
6977 else:
6978 holderType = None
6979 holderArgs = None
6980 declArgs = None
6981 return JSToNativeConversionInfo(
6982 template,
6983 declType=declType,
6984 holderType=holderType,
6985 dealWithOptional=isOptional,
6986 declArgs=declArgs,
6987 holderArgs=holderArgs,
6990 if type.isJSString():
6991 assert not isEnforceRange and not isClamp and not isAllowShared
6992 if type.nullable():
6993 raise TypeError("Nullable JSString not supported")
6995 declArgs = "cx"
6996 if isMember:
6997 raise TypeError("JSString not supported as member")
6998 else:
6999 declType = "JS::Rooted<JSString*>"
7001 if isOptional:
7002 raise TypeError("JSString not supported as optional")
7003 templateBody = fill(
7005 if (!($${declName} = ConvertJSValueToJSString(cx, $${val}))) {
7006 $*{exceptionCode}
7008 """,
7009 exceptionCode=exceptionCode,
7012 if defaultValue is not None:
7013 assert not isinstance(defaultValue, IDLNullValue)
7014 defaultCode = fill(
7016 static const char data[] = { ${data} };
7017 $${declName} = JS_NewStringCopyN(cx, data, ArrayLength(data) - 1);
7018 if (!$${declName}) {
7019 $*{exceptionCode}
7021 """,
7022 data=", ".join(
7023 ["'" + char + "'" for char in defaultValue.value] + ["0"]
7025 exceptionCode=exceptionCode,
7028 templateBody = handleDefault(templateBody, defaultCode)
7029 return JSToNativeConversionInfo(
7030 templateBody, declType=CGGeneric(declType), declArgs=declArgs
7033 if type.isDOMString() or type.isUSVString() or type.isUTF8String():
7034 assert not isEnforceRange and not isClamp and not isAllowShared
7036 treatAs = {
7037 "Default": "eStringify",
7038 "EmptyString": "eEmpty",
7039 "Null": "eNull",
7041 if type.nullable():
7042 # For nullable strings null becomes a null string.
7043 treatNullAs = "Null"
7044 # For nullable strings undefined also becomes a null string.
7045 undefinedBehavior = "eNull"
7046 else:
7047 undefinedBehavior = "eStringify"
7048 if type.legacyNullToEmptyString:
7049 treatNullAs = "EmptyString"
7050 else:
7051 treatNullAs = "Default"
7052 nullBehavior = treatAs[treatNullAs]
7054 def getConversionCode(varName):
7055 normalizeCode = ""
7056 if type.isUSVString():
7057 normalizeCode = fill(
7059 if (!NormalizeUSVString(${var})) {
7060 JS_ReportOutOfMemory(cx);
7061 $*{exceptionCode}
7063 """,
7064 var=varName,
7065 exceptionCode=exceptionCode,
7068 conversionCode = fill(
7070 if (!ConvertJSValueToString(cx, $${val}, ${nullBehavior}, ${undefinedBehavior}, ${varName})) {
7071 $*{exceptionCode}
7073 $*{normalizeCode}
7074 """,
7075 nullBehavior=nullBehavior,
7076 undefinedBehavior=undefinedBehavior,
7077 varName=varName,
7078 exceptionCode=exceptionCode,
7079 normalizeCode=normalizeCode,
7082 if defaultValue is None:
7083 return conversionCode
7085 if isinstance(defaultValue, IDLNullValue):
7086 assert type.nullable()
7087 defaultCode = "%s.SetIsVoid(true);\n" % varName
7088 else:
7089 defaultCode = handleDefaultStringValue(
7090 defaultValue, "%s.AssignLiteral" % varName
7092 return handleDefault(conversionCode, defaultCode)
7094 if isMember:
7095 # Convert directly into the ns[C]String member we have.
7096 if type.isUTF8String():
7097 declType = "nsCString"
7098 else:
7099 declType = "nsString"
7100 return JSToNativeConversionInfo(
7101 getConversionCode("${declName}"),
7102 declType=CGGeneric(declType),
7103 dealWithOptional=isOptional,
7106 if isOptional:
7107 if type.isUTF8String():
7108 declType = "Optional<nsACString>"
7109 holderType = CGGeneric("binding_detail::FakeString<char>")
7110 else:
7111 declType = "Optional<nsAString>"
7112 holderType = CGGeneric("binding_detail::FakeString<char16_t>")
7113 conversionCode = "%s" "${declName} = &${holderName};\n" % getConversionCode(
7114 "${holderName}"
7116 else:
7117 if type.isUTF8String():
7118 declType = "binding_detail::FakeString<char>"
7119 else:
7120 declType = "binding_detail::FakeString<char16_t>"
7121 holderType = None
7122 conversionCode = getConversionCode("${declName}")
7124 # No need to deal with optional here; we handled it already
7125 return JSToNativeConversionInfo(
7126 conversionCode, declType=CGGeneric(declType), holderType=holderType
7129 if type.isByteString():
7130 assert not isEnforceRange and not isClamp and not isAllowShared
7132 nullable = toStringBool(type.nullable())
7134 conversionCode = fill(
7136 if (!ConvertJSValueToByteString(cx, $${val}, ${nullable}, "${sourceDescription}", $${declName})) {
7137 $*{exceptionCode}
7139 """,
7140 nullable=nullable,
7141 sourceDescription=sourceDescription,
7142 exceptionCode=exceptionCode,
7145 if defaultValue is not None:
7146 if isinstance(defaultValue, IDLNullValue):
7147 assert type.nullable()
7148 defaultCode = "${declName}.SetIsVoid(true);\n"
7149 else:
7150 defaultCode = handleDefaultStringValue(
7151 defaultValue, "${declName}.AssignLiteral"
7153 conversionCode = handleDefault(conversionCode, defaultCode)
7155 return JSToNativeConversionInfo(
7156 conversionCode, declType=CGGeneric("nsCString"), dealWithOptional=isOptional
7159 if type.isEnum():
7160 assert not isEnforceRange and not isClamp and not isAllowShared
7162 enumName = type.unroll().inner.identifier.name
7163 declType = CGGeneric(enumName)
7164 if type.nullable():
7165 declType = CGTemplatedType("Nullable", declType)
7166 declType = declType.define()
7167 enumLoc = "${declName}.SetValue()"
7168 else:
7169 enumLoc = "${declName}"
7170 declType = declType.define()
7172 if invalidEnumValueFatal:
7173 handleInvalidEnumValueCode = "MOZ_ASSERT(index >= 0);\n"
7174 else:
7175 # invalidEnumValueFatal is false only for attributes. So we won't
7176 # have a non-default exceptionCode here unless attribute "arg
7177 # conversion" code starts passing in an exceptionCode. At which
7178 # point we'll need to figure out what that even means.
7179 assert exceptionCode == "return false;\n"
7180 handleInvalidEnumValueCode = dedent(
7182 if (index < 0) {
7183 return true;
7188 template = fill(
7191 int index;
7192 if (!FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val}, ${values}, "${enumtype}", "${sourceDescription}", &index)) {
7193 $*{exceptionCode}
7195 $*{handleInvalidEnumValueCode}
7196 ${enumLoc} = static_cast<${enumtype}>(index);
7198 """,
7199 enumtype=enumName,
7200 values=enumName + "Values::" + ENUM_ENTRY_VARIABLE_NAME,
7201 invalidEnumValueFatal=toStringBool(invalidEnumValueFatal),
7202 handleInvalidEnumValueCode=handleInvalidEnumValueCode,
7203 exceptionCode=exceptionCode,
7204 enumLoc=enumLoc,
7205 sourceDescription=sourceDescription,
7208 setNull = "${declName}.SetNull();\n"
7210 if type.nullable():
7211 template = CGIfElseWrapper(
7212 "${val}.isNullOrUndefined()", CGGeneric(setNull), CGGeneric(template)
7213 ).define()
7215 if defaultValue is not None:
7216 if isinstance(defaultValue, IDLNullValue):
7217 assert type.nullable()
7218 template = handleDefault(template, setNull)
7219 else:
7220 assert defaultValue.type.tag() == IDLType.Tags.domstring
7221 template = handleDefault(
7222 template,
7224 "%s = %s::%s;\n"
7225 % (enumLoc, enumName, getEnumValueName(defaultValue.value))
7228 return JSToNativeConversionInfo(
7229 template, declType=CGGeneric(declType), dealWithOptional=isOptional
7232 if type.isCallback():
7233 assert not isEnforceRange and not isClamp and not isAllowShared
7234 assert not type.treatNonCallableAsNull() or type.nullable()
7235 assert not type.treatNonObjectAsNull() or type.nullable()
7236 assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
7238 callback = type.unroll().callback
7239 name = callback.identifier.name
7240 (declType, declArgs, conversion) = getCallbackConversionInfo(
7241 type, callback, isMember, isCallbackReturnValue, isOptional
7244 if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
7245 haveCallable = "JS::IsCallable(&${val}.toObject())"
7246 if not isDefinitelyObject:
7247 haveCallable = "${val}.isObject() && " + haveCallable
7248 if defaultValue is not None:
7249 assert isinstance(defaultValue, IDLNullValue)
7250 haveCallable = "(${haveValue}) && " + haveCallable
7251 template = (
7252 ("if (%s) {\n" % haveCallable) + conversion + "} else {\n"
7253 " ${declName} = nullptr;\n"
7254 "}\n"
7256 elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull():
7257 if not isDefinitelyObject:
7258 haveObject = "${val}.isObject()"
7259 if defaultValue is not None:
7260 assert isinstance(defaultValue, IDLNullValue)
7261 haveObject = "(${haveValue}) && " + haveObject
7262 template = CGIfElseWrapper(
7263 haveObject,
7264 CGGeneric(conversion),
7265 CGGeneric("${declName} = nullptr;\n"),
7266 ).define()
7267 else:
7268 template = conversion
7269 else:
7270 template = wrapObjectTemplate(
7271 "if (JS::IsCallable(&${val}.toObject())) {\n"
7272 + conversion
7273 + "} else {\n"
7274 + indent(onFailureNotCallable(failureCode).define())
7275 + "}\n",
7276 type,
7277 "${declName} = nullptr;\n",
7278 failureCode,
7280 return JSToNativeConversionInfo(
7281 template, declType=declType, declArgs=declArgs, dealWithOptional=isOptional
7284 if type.isAny():
7285 assert not isEnforceRange and not isClamp and not isAllowShared
7287 declArgs = None
7288 if isMember in ("Variadic", "Sequence", "Dictionary", "Record"):
7289 # Rooting is handled by the sequence and dictionary tracers.
7290 declType = "JS::Value"
7291 else:
7292 assert not isMember
7293 declType = "JS::Rooted<JS::Value>"
7294 declArgs = "cx"
7296 assert not isOptional
7297 templateBody = "${declName} = ${val};\n"
7299 # For JS-implemented APIs, we refuse to allow passing objects that the
7300 # API consumer does not subsume. The extra parens around
7301 # ($${passedToJSImpl}) suppress unreachable code warnings when
7302 # $${passedToJSImpl} is the literal `false`. But Apple is shipping a
7303 # buggy clang (clang 3.9) in Xcode 8.3, so there even the parens are not
7304 # enough. So we manually disable some warnings in clang.
7305 if (
7306 not isinstance(descriptorProvider, Descriptor)
7307 or descriptorProvider.interface.isJSImplemented()
7309 templateBody = (
7310 fill(
7312 #ifdef __clang__
7313 #pragma clang diagnostic push
7314 #pragma clang diagnostic ignored "-Wunreachable-code"
7315 #pragma clang diagnostic ignored "-Wunreachable-code-return"
7316 #endif // __clang__
7317 if (($${passedToJSImpl}) && !CallerSubsumes($${val})) {
7318 cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}");
7319 $*{exceptionCode}
7321 #ifdef __clang__
7322 #pragma clang diagnostic pop
7323 #endif // __clang__
7324 """,
7325 sourceDescription=sourceDescription,
7326 exceptionCode=exceptionCode,
7328 + templateBody
7331 # We may not have a default value if we're being converted for
7332 # a setter, say.
7333 if defaultValue:
7334 if isinstance(defaultValue, IDLNullValue):
7335 defaultHandling = "${declName} = JS::NullValue();\n"
7336 else:
7337 assert isinstance(defaultValue, IDLUndefinedValue)
7338 defaultHandling = "${declName} = JS::UndefinedValue();\n"
7339 templateBody = handleDefault(templateBody, defaultHandling)
7340 return JSToNativeConversionInfo(
7341 templateBody, declType=CGGeneric(declType), declArgs=declArgs
7344 if type.isObject():
7345 assert not isEnforceRange and not isClamp and not isAllowShared
7346 return handleJSObjectType(
7347 type, isMember, failureCode, exceptionCode, sourceDescription
7350 if type.isDictionary():
7351 # There are no nullable dictionary-typed arguments or dictionary-typed
7352 # dictionary members.
7353 assert (
7354 not type.nullable()
7355 or isCallbackReturnValue
7356 or (isMember and isMember != "Dictionary")
7358 # All optional dictionary-typed arguments always have default values,
7359 # but dictionary-typed dictionary members can be optional.
7360 assert not isOptional or isMember == "Dictionary"
7361 # In the callback return value case we never have to worry
7362 # about a default value; we always have a value.
7363 assert not isCallbackReturnValue or defaultValue is None
7365 typeName = CGDictionary.makeDictionaryName(type.unroll().inner)
7366 if not isMember and not isCallbackReturnValue:
7367 # Since we're not a member and not nullable or optional, no one will
7368 # see our real type, so we can do the fast version of the dictionary
7369 # that doesn't pre-initialize members.
7370 typeName = "binding_detail::Fast" + typeName
7372 declType = CGGeneric(typeName)
7374 # We do manual default value handling here, because we actually do want
7375 # a jsval, and we only handle the default-dictionary case (which we map
7376 # into initialization with the JS value `null`) anyway
7377 # NOTE: if isNullOrUndefined or isDefinitelyObject are true,
7378 # we know we have a value, so we don't have to worry about the
7379 # default value.
7380 if (
7381 not isNullOrUndefined
7382 and not isDefinitelyObject
7383 and defaultValue is not None
7385 assert isinstance(defaultValue, IDLDefaultDictionaryValue)
7386 # Initializing from JS null does the right thing to give
7387 # us a default-initialized dictionary.
7388 val = "(${haveValue}) ? ${val} : JS::NullHandleValue"
7389 else:
7390 val = "${val}"
7392 dictLoc = "${declName}"
7393 if type.nullable():
7394 dictLoc += ".SetValue()"
7396 if type.unroll().inner.needsConversionFromJS:
7397 args = "cx, %s, " % val
7398 else:
7399 # We can end up in this case if a dictionary that does not need
7400 # conversion from JS has a dictionary-typed member with a default
7401 # value of {}.
7402 args = ""
7403 conversionCode = fill(
7405 if (!${dictLoc}.Init(${args}"${desc}", $${passedToJSImpl})) {
7406 $*{exceptionCode}
7408 """,
7409 dictLoc=dictLoc,
7410 args=args,
7411 desc=firstCap(sourceDescription),
7412 exceptionCode=exceptionCode,
7415 if failureCode is not None:
7416 # This means we're part of an overload or union conversion, and
7417 # should simply skip stuff if our value is not convertible to
7418 # dictionary, instead of trying and throwing. If we're either
7419 # isDefinitelyObject or isNullOrUndefined then we're convertible to
7420 # dictionary and don't need to check here.
7421 if isDefinitelyObject or isNullOrUndefined:
7422 template = conversionCode
7423 else:
7424 template = fill(
7426 if (!IsConvertibleToDictionary(${val})) {
7427 $*{failureCode}
7429 $*{conversionCode}
7430 """,
7431 val=val,
7432 failureCode=failureCode,
7433 conversionCode=conversionCode,
7435 else:
7436 template = conversionCode
7438 if type.nullable():
7439 declType = CGTemplatedType("Nullable", declType)
7440 template = CGIfElseWrapper(
7441 "${val}.isNullOrUndefined()",
7442 CGGeneric("${declName}.SetNull();\n"),
7443 CGGeneric(template),
7444 ).define()
7446 # Dictionary arguments that might contain traceable things need to get
7447 # traced
7448 if not isMember and isCallbackReturnValue:
7449 # Go ahead and just convert directly into our actual return value
7450 declType = CGWrapper(declType, post="&")
7451 declArgs = "aRetVal"
7452 elif not isMember and typeNeedsRooting(type):
7453 declType = CGTemplatedType("RootedDictionary", declType)
7454 declArgs = "cx"
7455 else:
7456 declArgs = None
7458 return JSToNativeConversionInfo(
7459 template, declType=declType, declArgs=declArgs, dealWithOptional=isOptional
7462 if type.isVoid():
7463 assert not isOptional
7464 # This one only happens for return values, and its easy: Just
7465 # ignore the jsval.
7466 return JSToNativeConversionInfo("")
7468 if not type.isPrimitive():
7469 raise TypeError("Need conversion for argument type '%s'" % str(type))
7471 typeName = builtinNames[type.tag()]
7473 conversionBehavior = "eDefault"
7474 if isEnforceRange:
7475 assert type.isInteger()
7476 conversionBehavior = "eEnforceRange"
7477 elif isClamp:
7478 assert type.isInteger()
7479 conversionBehavior = "eClamp"
7481 alwaysNull = False
7482 if type.nullable():
7483 declType = CGGeneric("Nullable<" + typeName + ">")
7484 writeLoc = "${declName}.SetValue()"
7485 readLoc = "${declName}.Value()"
7486 nullCondition = "${val}.isNullOrUndefined()"
7487 if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
7488 nullCondition = "!(${haveValue}) || " + nullCondition
7489 if isKnownMissing:
7490 alwaysNull = True
7491 template = dedent(
7493 ${declName}.SetNull();
7496 if not alwaysNull:
7497 template = fill(
7499 if (${nullCondition}) {
7500 $${declName}.SetNull();
7501 } else if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, "${sourceDescription}", &${writeLoc})) {
7502 $*{exceptionCode}
7504 """,
7505 nullCondition=nullCondition,
7506 typeName=typeName,
7507 conversionBehavior=conversionBehavior,
7508 sourceDescription=firstCap(sourceDescription),
7509 writeLoc=writeLoc,
7510 exceptionCode=exceptionCode,
7512 else:
7513 assert defaultValue is None or not isinstance(defaultValue, IDLNullValue)
7514 writeLoc = "${declName}"
7515 readLoc = writeLoc
7516 template = fill(
7518 if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, "${sourceDescription}", &${writeLoc})) {
7519 $*{exceptionCode}
7521 """,
7522 typeName=typeName,
7523 conversionBehavior=conversionBehavior,
7524 sourceDescription=firstCap(sourceDescription),
7525 writeLoc=writeLoc,
7526 exceptionCode=exceptionCode,
7528 declType = CGGeneric(typeName)
7530 if type.isFloat() and not type.isUnrestricted() and not alwaysNull:
7531 if lenientFloatCode is not None:
7532 nonFiniteCode = lenientFloatCode
7533 else:
7534 nonFiniteCode = 'cx.ThrowErrorMessage<MSG_NOT_FINITE>("%s");\n' "%s" % (
7535 firstCap(sourceDescription),
7536 exceptionCode,
7539 # We're appending to an if-block brace, so strip trailing whitespace
7540 # and add an extra space before the else.
7541 template = template.rstrip()
7542 template += fill(
7544 else if (!mozilla::IsFinite(${readLoc})) {
7545 $*{nonFiniteCode}
7547 """,
7548 readLoc=readLoc,
7549 nonFiniteCode=nonFiniteCode,
7552 if (
7553 defaultValue is not None
7555 # We already handled IDLNullValue, so just deal with the other ones
7556 not isinstance(defaultValue, IDLNullValue)
7558 tag = defaultValue.type.tag()
7559 defaultStr = getHandleDefault(defaultValue)
7560 template = handleDefault(template, "%s = %s;\n" % (writeLoc, defaultStr))
7562 return JSToNativeConversionInfo(
7563 template, declType=declType, dealWithOptional=isOptional
7567 def instantiateJSToNativeConversion(info, replacements, checkForValue=False):
7569 Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo
7570 and a set of replacements as required by the strings in such an object, and
7571 generate code to convert into stack C++ types.
7573 If checkForValue is True, then the conversion will get wrapped in
7574 a check for ${haveValue}.
7576 templateBody, declType, holderType, dealWithOptional = (
7577 info.template,
7578 info.declType,
7579 info.holderType,
7580 info.dealWithOptional,
7583 if dealWithOptional and not checkForValue:
7584 raise TypeError("Have to deal with optional things, but don't know how")
7585 if checkForValue and declType is None:
7586 raise TypeError(
7587 "Need to predeclare optional things, so they will be "
7588 "outside the check for big enough arg count!"
7591 # We can't precompute our holder constructor arguments, since
7592 # those might depend on ${declName}, which we change below. Just
7593 # compute arguments at the point when we need them as we go.
7594 def getArgsCGThing(args):
7595 return CGGeneric(string.Template(args).substitute(replacements))
7597 result = CGList([])
7598 # Make a copy of "replacements" since we may be about to start modifying it
7599 replacements = dict(replacements)
7600 originalDeclName = replacements["declName"]
7601 if declType is not None:
7602 if dealWithOptional:
7603 replacements["declName"] = "%s.Value()" % originalDeclName
7604 declType = CGTemplatedType("Optional", declType)
7605 declCtorArgs = None
7606 elif info.declArgs is not None:
7607 declCtorArgs = CGWrapper(getArgsCGThing(info.declArgs), pre="(", post=")")
7608 else:
7609 declCtorArgs = None
7610 result.append(
7611 CGList(
7613 declType,
7614 CGGeneric(" "),
7615 CGGeneric(originalDeclName),
7616 declCtorArgs,
7617 CGGeneric(";\n"),
7622 originalHolderName = replacements["holderName"]
7623 if holderType is not None:
7624 if dealWithOptional:
7625 replacements["holderName"] = "%s.ref()" % originalHolderName
7626 holderType = CGTemplatedType("Maybe", holderType)
7627 holderCtorArgs = None
7628 elif info.holderArgs is not None:
7629 holderCtorArgs = CGWrapper(
7630 getArgsCGThing(info.holderArgs), pre="(", post=")"
7632 else:
7633 holderCtorArgs = None
7634 result.append(
7635 CGList(
7637 holderType,
7638 CGGeneric(" "),
7639 CGGeneric(originalHolderName),
7640 holderCtorArgs,
7641 CGGeneric(";\n"),
7646 if "maybeMutableVal" not in replacements:
7647 replacements["maybeMutableVal"] = replacements["val"]
7649 conversion = CGGeneric(string.Template(templateBody).substitute(replacements))
7651 if checkForValue:
7652 if dealWithOptional:
7653 declConstruct = CGIndenter(
7654 CGGeneric(
7655 "%s.Construct(%s);\n"
7657 originalDeclName,
7658 getArgsCGThing(info.declArgs).define() if info.declArgs else "",
7662 if holderType is not None:
7663 holderConstruct = CGIndenter(
7664 CGGeneric(
7665 "%s.emplace(%s);\n"
7667 originalHolderName,
7668 getArgsCGThing(info.holderArgs).define()
7669 if info.holderArgs
7670 else "",
7674 else:
7675 holderConstruct = None
7676 else:
7677 declConstruct = None
7678 holderConstruct = None
7680 conversion = CGList(
7682 CGGeneric(
7683 string.Template("if (${haveValue}) {\n").substitute(replacements)
7685 declConstruct,
7686 holderConstruct,
7687 CGIndenter(conversion),
7688 CGGeneric("}\n"),
7692 result.append(conversion)
7693 return result
7696 def convertConstIDLValueToJSVal(value):
7697 if isinstance(value, IDLNullValue):
7698 return "JS::NullValue()"
7699 if isinstance(value, IDLUndefinedValue):
7700 return "JS::UndefinedValue()"
7701 tag = value.type.tag()
7702 if tag in [
7703 IDLType.Tags.int8,
7704 IDLType.Tags.uint8,
7705 IDLType.Tags.int16,
7706 IDLType.Tags.uint16,
7707 IDLType.Tags.int32,
7709 return "JS::Int32Value(%s)" % (value.value)
7710 if tag == IDLType.Tags.uint32:
7711 return "JS::NumberValue(%sU)" % (value.value)
7712 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
7713 return "JS::CanonicalizedDoubleValue(%s)" % numericValue(tag, value.value)
7714 if tag == IDLType.Tags.bool:
7715 return "JS::BooleanValue(%s)" % (toStringBool(value.value))
7716 if tag in [IDLType.Tags.float, IDLType.Tags.double]:
7717 return "JS::CanonicalizedDoubleValue(%s)" % (value.value)
7718 raise TypeError("Const value of unhandled type: %s" % value.type)
7721 class CGArgumentConverter(CGThing):
7723 A class that takes an IDL argument object and its index in the
7724 argument list and generates code to unwrap the argument to the
7725 right native type.
7727 argDescription is a description of the argument for error-reporting
7728 purposes. Callers should assume that it might get placed in the middle of a
7729 sentence. If it ends up at the beginning of a sentence, its first character
7730 will be automatically uppercased.
7733 def __init__(
7734 self,
7735 argument,
7736 index,
7737 descriptorProvider,
7738 argDescription,
7739 member,
7740 invalidEnumValueFatal=True,
7741 lenientFloatCode=None,
7743 CGThing.__init__(self)
7744 self.argument = argument
7745 self.argDescription = argDescription
7746 assert not argument.defaultValue or argument.optional
7748 replacer = {"index": index, "argc": "args.length()"}
7749 self.replacementVariables = {
7750 "declName": "arg%d" % index,
7751 "holderName": ("arg%d" % index) + "_holder",
7752 "obj": "obj",
7753 "passedToJSImpl": toStringBool(
7754 isJSImplementedDescriptor(descriptorProvider)
7757 # If we have a method generated by the maplike/setlike portion of an
7758 # interface, arguments can possibly be undefined, but will need to be
7759 # converted to the key/value type of the backing object. In this case,
7760 # use .get() instead of direct access to the argument. This won't
7761 # matter for iterable since generated functions for those interface
7762 # don't take arguments.
7763 if member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod():
7764 self.replacementVariables["val"] = string.Template(
7765 "args.get(${index})"
7766 ).substitute(replacer)
7767 self.replacementVariables["maybeMutableVal"] = string.Template(
7768 "args[${index}]"
7769 ).substitute(replacer)
7770 else:
7771 self.replacementVariables["val"] = string.Template(
7772 "args[${index}]"
7773 ).substitute(replacer)
7774 haveValueCheck = string.Template("args.hasDefined(${index})").substitute(
7775 replacer
7777 self.replacementVariables["haveValue"] = haveValueCheck
7778 self.descriptorProvider = descriptorProvider
7779 if self.argument.canHaveMissingValue():
7780 self.argcAndIndex = replacer
7781 else:
7782 self.argcAndIndex = None
7783 self.invalidEnumValueFatal = invalidEnumValueFatal
7784 self.lenientFloatCode = lenientFloatCode
7786 def define(self):
7787 typeConversion = getJSToNativeConversionInfo(
7788 self.argument.type,
7789 self.descriptorProvider,
7790 isOptional=(self.argcAndIndex is not None and not self.argument.variadic),
7791 invalidEnumValueFatal=self.invalidEnumValueFatal,
7792 defaultValue=self.argument.defaultValue,
7793 lenientFloatCode=self.lenientFloatCode,
7794 isMember="Variadic" if self.argument.variadic else False,
7795 allowTreatNonCallableAsNull=self.argument.allowTreatNonCallableAsNull(),
7796 sourceDescription=self.argDescription,
7799 if not self.argument.variadic:
7800 return instantiateJSToNativeConversion(
7801 typeConversion, self.replacementVariables, self.argcAndIndex is not None
7802 ).define()
7804 # Variadic arguments get turned into a sequence.
7805 if typeConversion.dealWithOptional:
7806 raise TypeError("Shouldn't have optional things in variadics")
7807 if typeConversion.holderType is not None:
7808 raise TypeError("Shouldn't need holders for variadics")
7810 replacer = dict(self.argcAndIndex, **self.replacementVariables)
7811 replacer["seqType"] = CGTemplatedType(
7812 "AutoSequence", typeConversion.declType
7813 ).define()
7814 if typeNeedsRooting(self.argument.type):
7815 rooterDecl = (
7816 "SequenceRooter<%s> ${holderName}(cx, &${declName});\n"
7817 % typeConversion.declType.define()
7819 else:
7820 rooterDecl = ""
7821 replacer["elemType"] = typeConversion.declType.define()
7823 replacer["elementInitializer"] = initializerForType(self.argument.type) or ""
7825 # NOTE: Keep this in sync with sequence conversions as needed
7826 variadicConversion = string.Template(
7827 "${seqType} ${declName};\n"
7828 + rooterDecl
7829 + dedent(
7831 if (${argc} > ${index}) {
7832 if (!${declName}.SetCapacity(${argc} - ${index}, mozilla::fallible)) {
7833 JS_ReportOutOfMemory(cx);
7834 return false;
7836 for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) {
7837 // OK to do infallible append here, since we ensured capacity already.
7838 ${elemType}& slot = *${declName}.AppendElement(${elementInitializer});
7841 ).substitute(replacer)
7843 val = string.Template("args[variadicArg]").substitute(replacer)
7844 variadicConversion += indent(
7845 string.Template(typeConversion.template).substitute(
7847 "val": val,
7848 "maybeMutableVal": val,
7849 "declName": "slot",
7850 # We only need holderName here to handle isExternal()
7851 # interfaces, which use an internal holder for the
7852 # conversion even when forceOwningType ends up true.
7853 "holderName": "tempHolder",
7854 # Use the same ${obj} as for the variadic arg itself
7855 "obj": replacer["obj"],
7856 "passedToJSImpl": toStringBool(
7857 isJSImplementedDescriptor(self.descriptorProvider)
7864 variadicConversion += " }\n" "}\n"
7865 return variadicConversion
7868 def getMaybeWrapValueFuncForType(type):
7869 if type.isJSString():
7870 return "MaybeWrapStringValue"
7871 # Callbacks might actually be DOM objects; nothing prevents a page from
7872 # doing that.
7873 if type.isCallback() or type.isCallbackInterface() or type.isObject():
7874 if type.nullable():
7875 return "MaybeWrapObjectOrNullValue"
7876 return "MaybeWrapObjectValue"
7877 # SpiderMonkey interfaces are never DOM objects. Neither are sequences or
7878 # dictionaries, since those are always plain JS objects.
7879 if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence():
7880 if type.nullable():
7881 return "MaybeWrapNonDOMObjectOrNullValue"
7882 return "MaybeWrapNonDOMObjectValue"
7883 if type.isAny():
7884 return "MaybeWrapValue"
7886 # For other types, just go ahead an fall back on MaybeWrapValue for now:
7887 # it's always safe to do, and shouldn't be particularly slow for any of
7888 # them
7889 return "MaybeWrapValue"
7892 sequenceWrapLevel = 0
7893 recordWrapLevel = 0
7896 def getWrapTemplateForType(
7897 type,
7898 descriptorProvider,
7899 result,
7900 successCode,
7901 returnsNewObject,
7902 exceptionCode,
7903 spiderMonkeyInterfacesAreStructs,
7904 isConstructorRetval=False,
7907 Reflect a C++ value stored in "result", of IDL type "type" into JS. The
7908 "successCode" is the code to run once we have successfully done the
7909 conversion and must guarantee that execution of the conversion template
7910 stops once the successCode has executed (e.g. by doing a 'return', or by
7911 doing a 'break' if the entire conversion template is inside a block that
7912 the 'break' will exit).
7914 If spiderMonkeyInterfacesAreStructs is true, then if the type is a
7915 SpiderMonkey interface, "result" is one of the
7916 dom::SpiderMonkeyInterfaceObjectStorage subclasses, not a JSObject*.
7918 The resulting string should be used with string.Template. It
7919 needs the following keys when substituting:
7921 jsvalHandle: something that can be passed to methods taking a
7922 JS::MutableHandle<JS::Value>. This can be a
7923 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
7924 jsvalRef: something that can have .address() called on it to get a
7925 JS::Value* and .set() called on it to set it to a JS::Value.
7926 This can be a JS::MutableHandle<JS::Value> or a
7927 JS::Rooted<JS::Value>.
7928 obj: a JS::Handle<JSObject*>.
7930 Returns (templateString, infallibility of conversion template)
7932 if successCode is None:
7933 successCode = "return true;\n"
7935 def setUndefined():
7936 return _setValue("", setter="setUndefined")
7938 def setNull():
7939 return _setValue("", setter="setNull")
7941 def setInt32(value):
7942 return _setValue(value, setter="setInt32")
7944 def setString(value):
7945 return _setValue(value, wrapAsType=type, setter="setString")
7947 def setObject(value, wrapAsType=None):
7948 return _setValue(value, wrapAsType=wrapAsType, setter="setObject")
7950 def setObjectOrNull(value, wrapAsType=None):
7951 return _setValue(value, wrapAsType=wrapAsType, setter="setObjectOrNull")
7953 def setUint32(value):
7954 return _setValue(value, setter="setNumber")
7956 def setDouble(value):
7957 return _setValue("JS_NumberValue(%s)" % value)
7959 def setBoolean(value):
7960 return _setValue(value, setter="setBoolean")
7962 def _setValue(value, wrapAsType=None, setter="set"):
7964 Returns the code to set the jsval to value.
7966 If wrapAsType is not None, then will wrap the resulting value using the
7967 function that getMaybeWrapValueFuncForType(wrapAsType) returns.
7968 Otherwise, no wrapping will be done.
7970 if wrapAsType is None:
7971 tail = successCode
7972 else:
7973 tail = fill(
7975 if (!${maybeWrap}(cx, $${jsvalHandle})) {
7976 $*{exceptionCode}
7978 $*{successCode}
7979 """,
7980 maybeWrap=getMaybeWrapValueFuncForType(wrapAsType),
7981 exceptionCode=exceptionCode,
7982 successCode=successCode,
7984 return ("${jsvalRef}.%s(%s);\n" % (setter, value)) + tail
7986 def wrapAndSetPtr(wrapCall, failureCode=None):
7988 Returns the code to set the jsval by calling "wrapCall". "failureCode"
7989 is the code to run if calling "wrapCall" fails
7991 if failureCode is None:
7992 failureCode = exceptionCode
7993 return fill(
7995 if (!${wrapCall}) {
7996 $*{failureCode}
7998 $*{successCode}
7999 """,
8000 wrapCall=wrapCall,
8001 failureCode=failureCode,
8002 successCode=successCode,
8005 if type is None or type.isVoid():
8006 return (setUndefined(), True)
8008 if (type.isSequence() or type.isRecord()) and type.nullable():
8009 # These are both wrapped in Nullable<>
8010 recTemplate, recInfall = getWrapTemplateForType(
8011 type.inner,
8012 descriptorProvider,
8013 "%s.Value()" % result,
8014 successCode,
8015 returnsNewObject,
8016 exceptionCode,
8017 spiderMonkeyInterfacesAreStructs,
8019 code = fill(
8022 if (${result}.IsNull()) {
8023 $*{setNull}
8025 $*{recTemplate}
8026 """,
8027 result=result,
8028 setNull=setNull(),
8029 recTemplate=recTemplate,
8031 return code, recInfall
8033 if type.isSequence():
8034 # Now do non-nullable sequences. Our success code is just to break to
8035 # where we set the element in the array. Note that we bump the
8036 # sequenceWrapLevel around this call so that nested sequence conversions
8037 # will use different iteration variables.
8038 global sequenceWrapLevel
8039 index = "sequenceIdx%d" % sequenceWrapLevel
8040 sequenceWrapLevel += 1
8041 innerTemplate = wrapForType(
8042 type.inner,
8043 descriptorProvider,
8045 "result": "%s[%s]" % (result, index),
8046 "successCode": "break;\n",
8047 "jsvalRef": "tmp",
8048 "jsvalHandle": "&tmp",
8049 "returnsNewObject": returnsNewObject,
8050 "exceptionCode": exceptionCode,
8051 "obj": "returnArray",
8052 "spiderMonkeyInterfacesAreStructs": spiderMonkeyInterfacesAreStructs,
8055 sequenceWrapLevel -= 1
8056 code = fill(
8059 uint32_t length = ${result}.Length();
8060 JS::Rooted<JSObject*> returnArray(cx, JS::NewArrayObject(cx, length));
8061 if (!returnArray) {
8062 $*{exceptionCode}
8064 // Scope for 'tmp'
8066 JS::Rooted<JS::Value> tmp(cx);
8067 for (uint32_t ${index} = 0; ${index} < length; ++${index}) {
8068 // Control block to let us common up the JS_DefineElement calls when there
8069 // are different ways to succeed at wrapping the object.
8070 do {
8071 $*{innerTemplate}
8072 } while (false);
8073 if (!JS_DefineElement(cx, returnArray, ${index}, tmp,
8074 JSPROP_ENUMERATE)) {
8075 $*{exceptionCode}
8079 $*{set}
8080 """,
8081 result=result,
8082 exceptionCode=exceptionCode,
8083 index=index,
8084 innerTemplate=innerTemplate,
8085 set=setObject("*returnArray"),
8088 return (code, False)
8090 if type.isRecord():
8091 # Now do non-nullable record. Our success code is just to break to
8092 # where we define the property on the object. Note that we bump the
8093 # recordWrapLevel around this call so that nested record conversions
8094 # will use different temp value names.
8095 global recordWrapLevel
8096 valueName = "recordValue%d" % recordWrapLevel
8097 recordWrapLevel += 1
8098 innerTemplate = wrapForType(
8099 type.inner,
8100 descriptorProvider,
8102 "result": valueName,
8103 "successCode": "break;\n",
8104 "jsvalRef": "tmp",
8105 "jsvalHandle": "&tmp",
8106 "returnsNewObject": returnsNewObject,
8107 "exceptionCode": exceptionCode,
8108 "obj": "returnObj",
8109 "spiderMonkeyInterfacesAreStructs": spiderMonkeyInterfacesAreStructs,
8112 recordWrapLevel -= 1
8113 if type.keyType.isByteString():
8114 # There is no length-taking JS_DefineProperty. So to keep
8115 # things sane with embedded nulls, we want to byte-inflate
8116 # to an nsAString. The only byte-inflation function we
8117 # have around is AppendASCIItoUTF16, which luckily doesn't
8118 # assert anything about the input being ASCII.
8119 expandedKeyDecl = "NS_ConvertASCIItoUTF16 expandedKey(entry.mKey);\n"
8120 keyName = "expandedKey"
8121 elif type.keyType.isUTF8String():
8122 # We do the same as above for utf8 strings. We could do better if
8123 # we had a DefineProperty API that takes utf-8 property names.
8124 expandedKeyDecl = "NS_ConvertUTF8toUTF16 expandedKey(entry.mKey);\n"
8125 keyName = "expandedKey"
8126 else:
8127 expandedKeyDecl = ""
8128 keyName = "entry.mKey"
8130 code = fill(
8133 JS::Rooted<JSObject*> returnObj(cx, JS_NewPlainObject(cx));
8134 if (!returnObj) {
8135 $*{exceptionCode}
8137 // Scope for 'tmp'
8139 JS::Rooted<JS::Value> tmp(cx);
8140 for (auto& entry : ${result}.Entries()) {
8141 auto& ${valueName} = entry.mValue;
8142 // Control block to let us common up the JS_DefineUCProperty calls when there
8143 // are different ways to succeed at wrapping the value.
8144 do {
8145 $*{innerTemplate}
8146 } while (false);
8147 $*{expandedKeyDecl}
8148 if (!JS_DefineUCProperty(cx, returnObj,
8149 ${keyName}.BeginReading(),
8150 ${keyName}.Length(), tmp,
8151 JSPROP_ENUMERATE)) {
8152 $*{exceptionCode}
8156 $*{set}
8157 """,
8158 result=result,
8159 exceptionCode=exceptionCode,
8160 valueName=valueName,
8161 innerTemplate=innerTemplate,
8162 expandedKeyDecl=expandedKeyDecl,
8163 keyName=keyName,
8164 set=setObject("*returnObj"),
8167 return (code, False)
8169 if type.isPromise():
8170 assert not type.nullable()
8171 # The use of ToJSValue here is a bit annoying because the Promise
8172 # version is not inlined. But we can't put an inline version in either
8173 # ToJSValue.h or BindingUtils.h, because Promise.h includes ToJSValue.h
8174 # and that includes BindingUtils.h, so we'd get an include loop if
8175 # either of those headers included Promise.h. And trying to write the
8176 # conversion by hand here is pretty annoying because we have to handle
8177 # the various RefPtr, rawptr, NonNull, etc cases, which ToJSValue will
8178 # handle for us. So just eat the cost of the function call.
8179 return (wrapAndSetPtr("ToJSValue(cx, %s, ${jsvalHandle})" % result), False)
8181 if type.isGeckoInterface() and not type.isCallbackInterface():
8182 descriptor = descriptorProvider.getDescriptor(
8183 type.unroll().inner.identifier.name
8185 if type.nullable():
8186 if descriptor.interface.identifier.name == "WindowProxy":
8187 template, infal = getWrapTemplateForType(
8188 type.inner,
8189 descriptorProvider,
8190 "%s.Value()" % result,
8191 successCode,
8192 returnsNewObject,
8193 exceptionCode,
8194 spiderMonkeyInterfacesAreStructs,
8196 return (
8197 "if (%s.IsNull()) {\n" % result
8198 + indent(setNull())
8199 + "}\n"
8200 + template,
8201 infal,
8204 wrappingCode = "if (!%s) {\n" % (result) + indent(setNull()) + "}\n"
8205 else:
8206 wrappingCode = ""
8208 if not descriptor.interface.isExternal():
8209 if descriptor.wrapperCache:
8210 wrapMethod = "GetOrCreateDOMReflector"
8211 wrapArgs = "cx, %s, ${jsvalHandle}" % result
8212 else:
8213 wrapMethod = "WrapNewBindingNonWrapperCachedObject"
8214 wrapArgs = "cx, ${obj}, %s, ${jsvalHandle}" % result
8215 if isConstructorRetval:
8216 wrapArgs += ", desiredProto"
8217 wrap = "%s(%s)" % (wrapMethod, wrapArgs)
8218 # Can only fail to wrap as a new-binding object if they already
8219 # threw an exception.
8220 failed = "MOZ_ASSERT(JS_IsExceptionPending(cx));\n" + exceptionCode
8221 else:
8222 if descriptor.notflattened:
8223 getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
8224 else:
8225 getIID = ""
8226 wrap = "WrapObject(cx, %s, %s${jsvalHandle})" % (result, getIID)
8227 failed = None
8229 wrappingCode += wrapAndSetPtr(wrap, failed)
8230 return (wrappingCode, False)
8232 if type.isJSString():
8233 return (setString(result), False)
8235 if type.isDOMString() or type.isUSVString():
8236 if type.nullable():
8237 return (
8238 wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result),
8239 False,
8241 else:
8242 return (
8243 wrapAndSetPtr(
8244 "xpc::NonVoidStringToJsval(cx, %s, ${jsvalHandle})" % result
8246 False,
8249 if type.isByteString():
8250 if type.nullable():
8251 return (
8252 wrapAndSetPtr("ByteStringToJsval(cx, %s, ${jsvalHandle})" % result),
8253 False,
8255 else:
8256 return (
8257 wrapAndSetPtr(
8258 "NonVoidByteStringToJsval(cx, %s, ${jsvalHandle})" % result
8260 False,
8263 if type.isUTF8String():
8264 if type.nullable():
8265 return (
8266 wrapAndSetPtr("UTF8StringToJsval(cx, %s, ${jsvalHandle})" % result),
8267 False,
8269 else:
8270 return (
8271 wrapAndSetPtr(
8272 "NonVoidUTF8StringToJsval(cx, %s, ${jsvalHandle})" % result
8274 False,
8277 if type.isEnum():
8278 if type.nullable():
8279 resultLoc = "%s.Value()" % result
8280 else:
8281 resultLoc = result
8282 conversion = fill(
8284 if (!ToJSValue(cx, ${result}, $${jsvalHandle})) {
8285 $*{exceptionCode}
8287 $*{successCode}
8288 """,
8289 result=resultLoc,
8290 exceptionCode=exceptionCode,
8291 successCode=successCode,
8294 if type.nullable():
8295 conversion = CGIfElseWrapper(
8296 "%s.IsNull()" % result, CGGeneric(setNull()), CGGeneric(conversion)
8297 ).define()
8298 return conversion, False
8300 if type.isCallback() or type.isCallbackInterface():
8301 # Callbacks can store null if we nuked the compartments their
8302 # objects lived in.
8303 wrapCode = setObjectOrNull(
8304 "GetCallbackFromCallbackObject(cx, %(result)s)", wrapAsType=type
8306 if type.nullable():
8307 wrapCode = (
8308 "if (%(result)s) {\n"
8309 + indent(wrapCode)
8310 + "} else {\n"
8311 + indent(setNull())
8312 + "}\n"
8314 wrapCode = wrapCode % {"result": result}
8315 return wrapCode, False
8317 if type.isAny():
8318 # See comments in GetOrCreateDOMReflector explaining why we need
8319 # to wrap here.
8320 # NB: _setValue(..., type-that-is-any) calls JS_WrapValue(), so is fallible
8321 head = "JS::ExposeValueToActiveJS(%s);\n" % result
8322 return (head + _setValue(result, wrapAsType=type), False)
8324 if type.isObject() or (
8325 type.isSpiderMonkeyInterface() and not spiderMonkeyInterfacesAreStructs
8327 # See comments in GetOrCreateDOMReflector explaining why we need
8328 # to wrap here.
8329 if type.nullable():
8330 toValue = "%s"
8331 setter = setObjectOrNull
8332 head = """if (%s) {
8333 JS::ExposeObjectToActiveJS(%s);
8335 """ % (
8336 result,
8337 result,
8339 else:
8340 toValue = "*%s"
8341 setter = setObject
8342 head = "JS::ExposeObjectToActiveJS(%s);\n" % result
8343 # NB: setObject{,OrNull}(..., some-object-type) calls JS_WrapValue(), so is fallible
8344 return (head + setter(toValue % result, wrapAsType=type), False)
8346 if not (
8347 type.isUnion()
8348 or type.isPrimitive()
8349 or type.isDictionary()
8350 or (type.isSpiderMonkeyInterface() and spiderMonkeyInterfacesAreStructs)
8352 raise TypeError("Need to learn to wrap %s" % type)
8354 if type.nullable():
8355 recTemplate, recInfal = getWrapTemplateForType(
8356 type.inner,
8357 descriptorProvider,
8358 "%s.Value()" % result,
8359 successCode,
8360 returnsNewObject,
8361 exceptionCode,
8362 spiderMonkeyInterfacesAreStructs,
8364 return (
8365 "if (%s.IsNull()) {\n" % result + indent(setNull()) + "}\n" + recTemplate,
8366 recInfal,
8369 if type.isSpiderMonkeyInterface():
8370 assert spiderMonkeyInterfacesAreStructs
8371 # See comments in GetOrCreateDOMReflector explaining why we need
8372 # to wrap here.
8373 # NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible
8374 return (setObject("*%s.Obj()" % result, wrapAsType=type), False)
8376 if type.isUnion():
8377 return (wrapAndSetPtr("%s.ToJSVal(cx, ${obj}, ${jsvalHandle})" % result), False)
8379 if type.isDictionary():
8380 return (
8381 wrapAndSetPtr("%s.ToObjectInternal(cx, ${jsvalHandle})" % result),
8382 False,
8385 tag = type.tag()
8387 if tag in [
8388 IDLType.Tags.int8,
8389 IDLType.Tags.uint8,
8390 IDLType.Tags.int16,
8391 IDLType.Tags.uint16,
8392 IDLType.Tags.int32,
8394 return (setInt32("int32_t(%s)" % result), True)
8396 elif tag in [
8397 IDLType.Tags.int64,
8398 IDLType.Tags.uint64,
8399 IDLType.Tags.unrestricted_float,
8400 IDLType.Tags.float,
8401 IDLType.Tags.unrestricted_double,
8402 IDLType.Tags.double,
8404 # XXXbz will cast to double do the "even significand" thing that webidl
8405 # calls for for 64-bit ints? Do we care?
8406 return (setDouble("double(%s)" % result), True)
8408 elif tag == IDLType.Tags.uint32:
8409 return (setUint32(result), True)
8411 elif tag == IDLType.Tags.bool:
8412 return (setBoolean(result), True)
8414 else:
8415 raise TypeError("Need to learn to wrap primitive: %s" % type)
8418 def wrapForType(type, descriptorProvider, templateValues):
8420 Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict
8421 that should contain:
8423 * 'jsvalRef': something that can have .address() called on it to get a
8424 JS::Value* and .set() called on it to set it to a JS::Value.
8425 This can be a JS::MutableHandle<JS::Value> or a
8426 JS::Rooted<JS::Value>.
8427 * 'jsvalHandle': something that can be passed to methods taking a
8428 JS::MutableHandle<JS::Value>. This can be a
8429 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
8430 * 'obj' (optional): the name of the variable that contains the JSObject to
8431 use as a scope when wrapping, if not supplied 'obj'
8432 will be used as the name
8433 * 'result' (optional): the name of the variable in which the C++ value is
8434 stored, if not supplied 'result' will be used as
8435 the name
8436 * 'successCode' (optional): the code to run once we have successfully
8437 done the conversion, if not supplied 'return
8438 true;' will be used as the code. The
8439 successCode must ensure that once it runs no
8440 more of the conversion template will be
8441 executed (e.g. by doing a 'return' or 'break'
8442 as appropriate).
8443 * 'returnsNewObject' (optional): If true, we're wrapping for the return
8444 value of a [NewObject] method. Assumed
8445 false if not set.
8446 * 'exceptionCode' (optional): Code to run when a JS exception is thrown.
8447 The default is "return false;". The code
8448 passed here must return.
8449 * 'isConstructorRetval' (optional): If true, we're wrapping a constructor
8450 return value.
8452 wrap = getWrapTemplateForType(
8453 type,
8454 descriptorProvider,
8455 templateValues.get("result", "result"),
8456 templateValues.get("successCode", None),
8457 templateValues.get("returnsNewObject", False),
8458 templateValues.get("exceptionCode", "return false;\n"),
8459 templateValues.get("spiderMonkeyInterfacesAreStructs", False),
8460 isConstructorRetval=templateValues.get("isConstructorRetval", False),
8461 )[0]
8463 defaultValues = {"obj": "obj"}
8464 return string.Template(wrap).substitute(defaultValues, **templateValues)
8467 def infallibleForMember(member, type, descriptorProvider):
8469 Determine the fallibility of changing a C++ value of IDL type "type" into
8470 JS for the given attribute. Apart from returnsNewObject, all the defaults
8471 are used, since the fallbility does not change based on the boolean values,
8472 and the template will be discarded.
8474 CURRENT ASSUMPTIONS:
8475 We assume that successCode for wrapping up return values cannot contain
8476 failure conditions.
8478 return getWrapTemplateForType(
8479 type,
8480 descriptorProvider,
8481 "result",
8482 None,
8483 memberReturnsNewObject(member),
8484 "return false;\n",
8485 False,
8486 )[1]
8489 def leafTypeNeedsCx(type, retVal):
8490 return (
8491 type.isAny()
8492 or type.isObject()
8493 or type.isJSString()
8494 or (retVal and type.isSpiderMonkeyInterface())
8498 def leafTypeNeedsScopeObject(type, retVal):
8499 return retVal and type.isSpiderMonkeyInterface()
8502 def leafTypeNeedsRooting(type):
8503 return leafTypeNeedsCx(type, False) or type.isSpiderMonkeyInterface()
8506 def typeNeedsRooting(type):
8507 return typeMatchesLambda(type, lambda t: leafTypeNeedsRooting(t))
8510 def typeNeedsCx(type, retVal=False):
8511 return typeMatchesLambda(type, lambda t: leafTypeNeedsCx(t, retVal))
8514 def typeNeedsScopeObject(type, retVal=False):
8515 return typeMatchesLambda(type, lambda t: leafTypeNeedsScopeObject(t, retVal))
8518 def typeMatchesLambda(type, func):
8519 if type is None:
8520 return False
8521 if type.nullable():
8522 return typeMatchesLambda(type.inner, func)
8523 if type.isSequence() or type.isRecord():
8524 return typeMatchesLambda(type.inner, func)
8525 if type.isUnion():
8526 return any(typeMatchesLambda(t, func) for t in type.unroll().flatMemberTypes)
8527 if type.isDictionary():
8528 return dictionaryMatchesLambda(type.inner, func)
8529 return func(type)
8532 def dictionaryMatchesLambda(dictionary, func):
8533 return any(typeMatchesLambda(m.type, func) for m in dictionary.members) or (
8534 dictionary.parent and dictionaryMatchesLambda(dictionary.parent, func)
8538 # Whenever this is modified, please update CGNativeMember.getRetvalInfo as
8539 # needed to keep the types compatible.
8540 def getRetvalDeclarationForType(returnType, descriptorProvider, isMember=False):
8542 Returns a tuple containing five things:
8544 1) A CGThing for the type of the return value, or None if there is no need
8545 for a return value.
8547 2) A value indicating the kind of ourparam to pass the value as. Valid
8548 options are None to not pass as an out param at all, "ref" (to pass a
8549 reference as an out param), and "ptr" (to pass a pointer as an out
8550 param).
8552 3) A CGThing for a tracer for the return value, or None if no tracing is
8553 needed.
8555 4) An argument string to pass to the retval declaration
8556 constructor or None if there are no arguments.
8558 5) The name of a function that needs to be called with the return value
8559 before using it, or None if no function needs to be called.
8561 if returnType is None or returnType.isVoid():
8562 # Nothing to declare
8563 return None, None, None, None, None
8564 if returnType.isPrimitive() and returnType.tag() in builtinNames:
8565 result = CGGeneric(builtinNames[returnType.tag()])
8566 if returnType.nullable():
8567 result = CGTemplatedType("Nullable", result)
8568 return result, None, None, None, None
8569 if returnType.isJSString():
8570 if isMember:
8571 raise TypeError("JSString not supported as return type member")
8572 return CGGeneric("JS::Rooted<JSString*>"), "ptr", None, "cx", None
8573 if returnType.isDOMString() or returnType.isUSVString():
8574 if isMember:
8575 return CGGeneric("nsString"), "ref", None, None, None
8576 return CGGeneric("DOMString"), "ref", None, None, None
8577 if returnType.isByteString() or returnType.isUTF8String():
8578 if isMember:
8579 return CGGeneric("nsCString"), "ref", None, None, None
8580 return CGGeneric("nsAutoCString"), "ref", None, None, None
8581 if returnType.isEnum():
8582 result = CGGeneric(returnType.unroll().inner.identifier.name)
8583 if returnType.nullable():
8584 result = CGTemplatedType("Nullable", result)
8585 return result, None, None, None, None
8586 if returnType.isGeckoInterface() or returnType.isPromise():
8587 if returnType.isGeckoInterface():
8588 typeName = returnType.unroll().inner.identifier.name
8589 if typeName == "WindowProxy":
8590 result = CGGeneric("WindowProxyHolder")
8591 if returnType.nullable():
8592 result = CGTemplatedType("Nullable", result)
8593 return result, None, None, None, None
8595 typeName = descriptorProvider.getDescriptor(typeName).nativeType
8596 else:
8597 typeName = "Promise"
8598 if isMember:
8599 conversion = None
8600 result = CGGeneric("StrongPtrForMember<%s>" % typeName)
8601 else:
8602 conversion = CGGeneric("StrongOrRawPtr<%s>" % typeName)
8603 result = CGGeneric("auto")
8604 return result, None, None, None, conversion
8605 if returnType.isCallback():
8606 name = returnType.unroll().callback.identifier.name
8607 return CGGeneric("RefPtr<%s>" % name), None, None, None, None
8608 if returnType.isAny():
8609 if isMember:
8610 return CGGeneric("JS::Value"), None, None, None, None
8611 return CGGeneric("JS::Rooted<JS::Value>"), "ptr", None, "cx", None
8612 if returnType.isObject() or returnType.isSpiderMonkeyInterface():
8613 if isMember:
8614 return CGGeneric("JSObject*"), None, None, None, None
8615 return CGGeneric("JS::Rooted<JSObject*>"), "ptr", None, "cx", None
8616 if returnType.isSequence():
8617 nullable = returnType.nullable()
8618 if nullable:
8619 returnType = returnType.inner
8620 result, _, _, _, _ = getRetvalDeclarationForType(
8621 returnType.inner, descriptorProvider, isMember="Sequence"
8623 # While we have our inner type, set up our rooter, if needed
8624 if not isMember and typeNeedsRooting(returnType):
8625 rooter = CGGeneric(
8626 "SequenceRooter<%s > resultRooter(cx, &result);\n" % result.define()
8628 else:
8629 rooter = None
8630 result = CGTemplatedType("nsTArray", result)
8631 if nullable:
8632 result = CGTemplatedType("Nullable", result)
8633 return result, "ref", rooter, None, None
8634 if returnType.isRecord():
8635 nullable = returnType.nullable()
8636 if nullable:
8637 returnType = returnType.inner
8638 result, _, _, _, _ = getRetvalDeclarationForType(
8639 returnType.inner, descriptorProvider, isMember="Record"
8641 # While we have our inner type, set up our rooter, if needed
8642 if not isMember and typeNeedsRooting(returnType):
8643 rooter = CGGeneric(
8644 "RecordRooter<%s> resultRooter(cx, &result);\n"
8645 % ("nsString, " + result.define())
8647 else:
8648 rooter = None
8649 result = CGTemplatedType("Record", [recordKeyDeclType(returnType), result])
8650 if nullable:
8651 result = CGTemplatedType("Nullable", result)
8652 return result, "ref", rooter, None, None
8653 if returnType.isDictionary():
8654 nullable = returnType.nullable()
8655 dictName = CGDictionary.makeDictionaryName(returnType.unroll().inner)
8656 result = CGGeneric(dictName)
8657 if not isMember and typeNeedsRooting(returnType):
8658 if nullable:
8659 result = CGTemplatedType("NullableRootedDictionary", result)
8660 else:
8661 result = CGTemplatedType("RootedDictionary", result)
8662 resultArgs = "cx"
8663 else:
8664 if nullable:
8665 result = CGTemplatedType("Nullable", result)
8666 resultArgs = None
8667 return result, "ref", None, resultArgs, None
8668 if returnType.isUnion():
8669 result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True))
8670 if not isMember and typeNeedsRooting(returnType):
8671 if returnType.nullable():
8672 result = CGTemplatedType("NullableRootedUnion", result)
8673 else:
8674 result = CGTemplatedType("RootedUnion", result)
8675 resultArgs = "cx"
8676 else:
8677 if returnType.nullable():
8678 result = CGTemplatedType("Nullable", result)
8679 resultArgs = None
8680 return result, "ref", None, resultArgs, None
8681 raise TypeError("Don't know how to declare return value for %s" % returnType)
8684 def needCx(returnType, arguments, extendedAttributes, considerTypes, static=False):
8685 return (
8686 not static
8687 and considerTypes
8688 and (
8689 typeNeedsCx(returnType, True) or any(typeNeedsCx(a.type) for a in arguments)
8691 or "implicitJSContext" in extendedAttributes
8695 def needScopeObject(
8696 returnType, arguments, extendedAttributes, isWrapperCached, considerTypes, isMember
8699 isMember should be true if we're dealing with an attribute
8700 annotated as [StoreInSlot].
8702 return (
8703 considerTypes
8704 and not isWrapperCached
8705 and (
8706 (not isMember and typeNeedsScopeObject(returnType, True))
8707 or any(typeNeedsScopeObject(a.type) for a in arguments)
8712 def callerTypeGetterForDescriptor(descriptor):
8713 if descriptor.interface.isExposedInAnyWorker():
8714 systemCallerGetter = "nsContentUtils::ThreadsafeIsSystemCaller"
8715 else:
8716 systemCallerGetter = "nsContentUtils::IsSystemCaller"
8717 return "%s(cx) ? CallerType::System : CallerType::NonSystem" % systemCallerGetter
8720 class CGCallGenerator(CGThing):
8722 A class to generate an actual call to a C++ object. Assumes that the C++
8723 object is stored in a variable whose name is given by the |object| argument.
8725 needsCallerType is a boolean indicating whether the call should receive
8726 a PrincipalType for the caller.
8728 needsErrorResult is a boolean indicating whether the call should be
8729 fallible and thus needs ErrorResult parameter.
8731 resultVar: If the returnType is not void, then the result of the call is
8732 stored in a C++ variable named by resultVar. The caller is responsible for
8733 declaring the result variable. If the caller doesn't care about the result
8734 value, resultVar can be omitted.
8736 context: The context string to pass to MaybeSetPendingException.
8739 def __init__(
8740 self,
8741 needsErrorResult,
8742 needsCallerType,
8743 isChromeOnly,
8744 arguments,
8745 argsPre,
8746 returnType,
8747 extendedAttributes,
8748 descriptor,
8749 nativeMethodName,
8750 static,
8751 object="self",
8752 argsPost=[],
8753 resultVar=None,
8754 context="nullptr",
8756 CGThing.__init__(self)
8759 result,
8760 resultOutParam,
8761 resultRooter,
8762 resultArgs,
8763 resultConversion,
8764 ) = getRetvalDeclarationForType(returnType, descriptor)
8766 args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
8767 for a, name in arguments:
8768 arg = CGGeneric(name)
8770 # Now constify the things that need it
8771 def needsConst(a):
8772 if a.type.isDictionary():
8773 return True
8774 if a.type.isSequence():
8775 return True
8776 if a.type.isRecord():
8777 return True
8778 # isObject() types are always a JS::Rooted, whether
8779 # nullable or not, and it turns out a const JS::Rooted
8780 # is not very helpful at all (in particular, it won't
8781 # even convert to a JS::Handle).
8782 # XXX bz Well, why not???
8783 if a.type.nullable() and not a.type.isObject():
8784 return True
8785 if a.type.isString():
8786 return True
8787 if a.canHaveMissingValue():
8788 # This will need an Optional or it's a variadic;
8789 # in both cases it should be const.
8790 return True
8791 if a.type.isUnion():
8792 return True
8793 if a.type.isSpiderMonkeyInterface():
8794 return True
8795 return False
8797 if needsConst(a):
8798 arg = CGWrapper(arg, pre="Constify(", post=")")
8799 # And convert NonNull<T> to T&
8800 if (
8801 (a.type.isGeckoInterface() or a.type.isCallback() or a.type.isPromise())
8802 and not a.type.nullable()
8803 ) or a.type.isDOMString():
8804 arg = CGWrapper(arg, pre="NonNullHelper(", post=")")
8806 # If it's a refcounted object, let the static analysis know it's
8807 # alive for the duration of the call.
8808 if a.type.isGeckoInterface() or a.type.isCallback():
8809 arg = CGWrapper(arg, pre="MOZ_KnownLive(", post=")")
8811 args.append(arg)
8813 needResultDecl = False
8815 # Build up our actual call
8816 self.cgRoot = CGList([])
8818 # Return values that go in outparams go here
8819 if resultOutParam is not None:
8820 if resultVar is None:
8821 needResultDecl = True
8822 resultVar = "result"
8823 if resultOutParam == "ref":
8824 args.append(CGGeneric(resultVar))
8825 else:
8826 assert resultOutParam == "ptr"
8827 args.append(CGGeneric("&" + resultVar))
8829 needsSubjectPrincipal = "needsSubjectPrincipal" in extendedAttributes
8830 if needsSubjectPrincipal:
8831 needsNonSystemPrincipal = (
8832 "needsNonSystemSubjectPrincipal" in extendedAttributes
8834 if needsNonSystemPrincipal:
8835 checkPrincipal = dedent(
8837 if (principal->IsSystemPrincipal()) {
8838 principal = nullptr;
8842 else:
8843 checkPrincipal = ""
8845 getPrincipal = fill(
8847 JS::Realm* realm = js::GetContextRealm(cx);
8848 MOZ_ASSERT(realm);
8849 JSPrincipals* principals = JS::GetRealmPrincipals(realm);
8850 nsIPrincipal* principal = nsJSPrincipals::get(principals);
8851 ${checkPrincipal}
8852 """,
8853 checkPrincipal=checkPrincipal,
8856 if descriptor.interface.isExposedInAnyWorker():
8857 self.cgRoot.append(
8858 CGGeneric(
8859 fill(
8861 Maybe<nsIPrincipal*> subjectPrincipal;
8862 if (NS_IsMainThread()) {
8863 $*{getPrincipal}
8864 subjectPrincipal.emplace(principal);
8866 """,
8867 getPrincipal=getPrincipal,
8871 subjectPrincipalArg = "subjectPrincipal"
8872 else:
8873 if needsNonSystemPrincipal:
8874 principalType = "nsIPrincipal*"
8875 subjectPrincipalArg = "subjectPrincipal"
8876 else:
8877 principalType = "NonNull<nsIPrincipal>"
8878 subjectPrincipalArg = "NonNullHelper(subjectPrincipal)"
8880 self.cgRoot.append(
8881 CGGeneric(
8882 fill(
8884 ${principalType} subjectPrincipal;
8886 $*{getPrincipal}
8887 subjectPrincipal = principal;
8889 """,
8890 principalType=principalType,
8891 getPrincipal=getPrincipal,
8896 args.append(CGGeneric("MOZ_KnownLive(%s)" % subjectPrincipalArg))
8898 if needsCallerType:
8899 if isChromeOnly:
8900 args.append(CGGeneric("SystemCallerGuarantee()"))
8901 else:
8902 args.append(CGGeneric(callerTypeGetterForDescriptor(descriptor)))
8904 canOOM = "canOOM" in extendedAttributes
8905 if needsErrorResult:
8906 args.append(CGGeneric("rv"))
8907 elif canOOM:
8908 args.append(CGGeneric("OOMReporter::From(rv)"))
8909 args.extend(CGGeneric(arg) for arg in argsPost)
8911 call = CGGeneric(nativeMethodName)
8912 if not static:
8913 call = CGWrapper(call, pre="%s->" % object)
8914 call = CGList([call, CGWrapper(args, pre="(", post=")")])
8915 if returnType is None or returnType.isVoid() or resultOutParam is not None:
8916 assert resultConversion is None
8917 call = CGList(
8919 CGWrapper(
8920 call,
8921 pre=(
8922 "// NOTE: This assert does NOT call the function.\n"
8923 "static_assert(std::is_void_v<decltype("
8925 post=')>, "Should be returning void here");',
8927 call,
8929 "\n",
8931 elif resultConversion is not None:
8932 call = CGList([resultConversion, CGWrapper(call, pre="(", post=")")])
8933 if resultVar is None and result is not None:
8934 needResultDecl = True
8935 resultVar = "result"
8937 if needResultDecl:
8938 if resultArgs is not None:
8939 resultArgsStr = "(%s)" % resultArgs
8940 else:
8941 resultArgsStr = ""
8942 result = CGWrapper(result, post=(" %s%s" % (resultVar, resultArgsStr)))
8943 if resultOutParam is None and resultArgs is None:
8944 call = CGList([result, CGWrapper(call, pre="(", post=")")])
8945 else:
8946 self.cgRoot.append(CGWrapper(result, post=";\n"))
8947 if resultOutParam is None:
8948 call = CGWrapper(call, pre=resultVar + " = ")
8949 if resultRooter is not None:
8950 self.cgRoot.append(resultRooter)
8951 elif result is not None:
8952 assert resultOutParam is None
8953 call = CGWrapper(call, pre=resultVar + " = ")
8955 call = CGWrapper(call, post=";\n")
8956 self.cgRoot.append(call)
8958 if needsErrorResult or canOOM:
8959 self.cgRoot.prepend(CGGeneric("FastErrorResult rv;\n"))
8960 self.cgRoot.append(
8961 CGGeneric(
8962 fill(
8964 if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx, ${context}))) {
8965 return false;
8967 """,
8968 context=context,
8973 self.cgRoot.append(CGGeneric("MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"))
8975 def define(self):
8976 return self.cgRoot.define()
8979 def getUnionMemberName(type):
8980 # Promises can't be in unions, because they're not distinguishable
8981 # from anything else.
8982 assert not type.isPromise()
8983 if type.isGeckoInterface():
8984 return type.inner.identifier.name
8985 if type.isEnum():
8986 return type.inner.identifier.name
8987 return type.name
8990 # A counter for making sure that when we're wrapping up things in
8991 # nested sequences we don't use the same variable name to iterate over
8992 # different sequences.
8993 sequenceWrapLevel = 0
8994 recordWrapLevel = 0
8997 def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
8999 Take the thing named by "value" and if it contains "any",
9000 "object", or spidermonkey-interface types inside return a CGThing
9001 that will wrap them into the current compartment.
9003 if type.isAny():
9004 assert not type.nullable()
9005 if isMember:
9006 value = "JS::MutableHandle<JS::Value>::fromMarkedLocation(&%s)" % value
9007 else:
9008 value = "&" + value
9009 return CGGeneric(
9010 "if (!JS_WrapValue(cx, %s)) {\n" " return false;\n" "}\n" % value
9013 if type.isObject():
9014 if isMember:
9015 value = "JS::MutableHandle<JSObject*>::fromMarkedLocation(&%s)" % value
9016 else:
9017 value = "&" + value
9018 return CGGeneric(
9019 "if (!JS_WrapObject(cx, %s)) {\n" " return false;\n" "}\n" % value
9022 if type.isSpiderMonkeyInterface():
9023 origValue = value
9024 if type.nullable():
9025 value = "%s.Value()" % value
9026 wrapCode = CGGeneric(
9027 "if (!%s.WrapIntoNewCompartment(cx)) {\n" " return false;\n" "}\n" % value
9029 if type.nullable():
9030 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
9031 return wrapCode
9033 if type.isSequence():
9034 origValue = value
9035 origType = type
9036 if type.nullable():
9037 type = type.inner
9038 value = "%s.Value()" % value
9039 global sequenceWrapLevel
9040 index = "indexName%d" % sequenceWrapLevel
9041 sequenceWrapLevel += 1
9042 wrapElement = wrapTypeIntoCurrentCompartment(
9043 type.inner, "%s[%s]" % (value, index)
9045 sequenceWrapLevel -= 1
9046 if not wrapElement:
9047 return None
9048 wrapCode = CGWrapper(
9049 CGIndenter(wrapElement),
9050 pre=(
9051 "for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n"
9052 % (index, index, value, index)
9054 post="}\n",
9056 if origType.nullable():
9057 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
9058 return wrapCode
9060 if type.isRecord():
9061 origType = type
9062 if type.nullable():
9063 type = type.inner
9064 recordRef = "%s.Value()" % value
9065 else:
9066 recordRef = value
9067 global recordWrapLevel
9068 entryRef = "mapEntry%d" % recordWrapLevel
9069 recordWrapLevel += 1
9070 wrapElement = wrapTypeIntoCurrentCompartment(type.inner, "%s.mValue" % entryRef)
9071 recordWrapLevel -= 1
9072 if not wrapElement:
9073 return None
9074 wrapCode = CGWrapper(
9075 CGIndenter(wrapElement),
9076 pre=("for (auto& %s : %s.Entries()) {\n" % (entryRef, recordRef)),
9077 post="}\n",
9079 if origType.nullable():
9080 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % value)
9081 return wrapCode
9083 if type.isDictionary():
9084 assert not type.nullable()
9085 myDict = type.inner
9086 memberWraps = []
9087 while myDict:
9088 for member in myDict.members:
9089 memberWrap = wrapArgIntoCurrentCompartment(
9090 member,
9091 "%s.%s"
9092 % (value, CGDictionary.makeMemberName(member.identifier.name)),
9094 if memberWrap:
9095 memberWraps.append(memberWrap)
9096 myDict = myDict.parent
9097 return CGList(memberWraps) if len(memberWraps) != 0 else None
9099 if type.isUnion():
9100 memberWraps = []
9101 if type.nullable():
9102 type = type.inner
9103 value = "%s.Value()" % value
9104 for member in type.flatMemberTypes:
9105 memberName = getUnionMemberName(member)
9106 memberWrap = wrapTypeIntoCurrentCompartment(
9107 member, "%s.GetAs%s()" % (value, memberName)
9109 if memberWrap:
9110 memberWrap = CGIfWrapper(memberWrap, "%s.Is%s()" % (value, memberName))
9111 memberWraps.append(memberWrap)
9112 return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None
9114 if (
9115 type.isString()
9116 or type.isPrimitive()
9117 or type.isEnum()
9118 or type.isGeckoInterface()
9119 or type.isCallback()
9120 or type.isPromise()
9122 # All of these don't need wrapping.
9123 return None
9125 raise TypeError(
9126 "Unknown type; we don't know how to wrap it in constructor "
9127 "arguments: %s" % type
9131 def wrapArgIntoCurrentCompartment(arg, value, isMember=True):
9133 As wrapTypeIntoCurrentCompartment but handles things being optional
9135 origValue = value
9136 isOptional = arg.canHaveMissingValue()
9137 if isOptional:
9138 value = value + ".Value()"
9139 wrap = wrapTypeIntoCurrentCompartment(arg.type, value, isMember)
9140 if wrap and isOptional:
9141 wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue)
9142 return wrap
9145 def needsContainsHack(m):
9146 return m.getExtendedAttribute("ReturnValueNeedsContainsHack")
9149 def needsCallerType(m):
9150 return m.getExtendedAttribute("NeedsCallerType")
9153 class CGPerSignatureCall(CGThing):
9155 This class handles the guts of generating code for a particular
9156 call signature. A call signature consists of four things:
9158 1) A return type, which can be None to indicate that there is no
9159 actual return value (e.g. this is an attribute setter) or an
9160 IDLType if there's an IDL type involved (including |void|).
9161 2) An argument list, which is allowed to be empty.
9162 3) A name of a native method to call.
9163 4) Whether or not this method is static. Note that this only controls how
9164 the method is called (|self->nativeMethodName(...)| vs
9165 |nativeMethodName(...)|).
9167 We also need to know whether this is a method or a getter/setter
9168 to do error reporting correctly.
9170 The idlNode parameter can be either a method or an attr. We can query
9171 |idlNode.identifier| in both cases, so we can be agnostic between the two.
9173 dontSetSlot should be set to True if the value should not be cached in a
9174 slot (even if the attribute is marked as StoreInSlot or Cached in the
9175 WebIDL).
9178 # XXXbz For now each entry in the argument list is either an
9179 # IDLArgument or a FakeArgument, but longer-term we may want to
9180 # have ways of flagging things like JSContext* or optional_argc in
9181 # there.
9183 def __init__(
9184 self,
9185 returnType,
9186 arguments,
9187 nativeMethodName,
9188 static,
9189 descriptor,
9190 idlNode,
9191 argConversionStartsAt=0,
9192 getter=False,
9193 setter=False,
9194 isConstructor=False,
9195 useCounterName=None,
9196 resultVar=None,
9197 objectName="obj",
9198 dontSetSlot=False,
9199 extendedAttributes=None,
9201 assert idlNode.isMethod() == (not getter and not setter)
9202 assert idlNode.isAttr() == (getter or setter)
9203 # Constructors are always static
9204 assert not isConstructor or static
9206 CGThing.__init__(self)
9207 self.returnType = returnType
9208 self.descriptor = descriptor
9209 self.idlNode = idlNode
9210 if extendedAttributes is None:
9211 extendedAttributes = descriptor.getExtendedAttributes(
9212 idlNode, getter=getter, setter=setter
9214 self.extendedAttributes = extendedAttributes
9215 self.arguments = arguments
9216 self.argCount = len(arguments)
9217 self.isConstructor = isConstructor
9218 self.setSlot = (
9219 not dontSetSlot and idlNode.isAttr() and idlNode.slotIndices is not None
9221 cgThings = []
9223 deprecated = idlNode.getExtendedAttribute("Deprecated") or (
9224 idlNode.isStatic()
9225 and descriptor.interface.getExtendedAttribute("Deprecated")
9227 if deprecated:
9228 cgThings.append(
9229 CGGeneric(
9230 dedent(
9232 DeprecationWarning(cx, obj, DeprecatedOperations::e%s);
9234 % deprecated[0]
9239 lenientFloatCode = None
9240 if idlNode.getExtendedAttribute("LenientFloat") is not None and (
9241 setter or idlNode.isMethod()
9243 cgThings.append(
9244 CGGeneric(
9245 dedent(
9247 bool foundNonFiniteFloat = false;
9252 lenientFloatCode = "foundNonFiniteFloat = true;\n"
9254 argsPre = []
9255 if idlNode.isStatic():
9256 # If we're a constructor, "obj" may not be a function, so calling
9257 # XrayAwareCalleeGlobal() on it is not safe. Of course in the
9258 # constructor case either "obj" is an Xray or we're already in the
9259 # content compartment, not the Xray compartment, so just
9260 # constructing the GlobalObject from "obj" is fine.
9261 if isConstructor:
9262 objForGlobalObject = "obj"
9263 else:
9264 objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)"
9265 cgThings.append(
9266 CGGeneric(
9267 fill(
9269 GlobalObject global(cx, ${obj});
9270 if (global.Failed()) {
9271 return false;
9274 """,
9275 obj=objForGlobalObject,
9279 argsPre.append("global")
9281 # For JS-implemented interfaces we do not want to base the
9282 # needsCx decision on the types involved, just on our extended
9283 # attributes. Also, JSContext is not needed for the static case
9284 # since GlobalObject already contains the context.
9285 needsCx = needCx(
9286 returnType,
9287 arguments,
9288 self.extendedAttributes,
9289 not descriptor.interface.isJSImplemented(),
9290 static,
9292 if needsCx:
9293 argsPre.append("cx")
9295 needsUnwrap = False
9296 argsPost = []
9297 runConstructorInCallerCompartment = descriptor.interface.getExtendedAttribute(
9298 "RunConstructorInCallerCompartment"
9300 if isConstructor and not runConstructorInCallerCompartment:
9301 needsUnwrap = True
9302 needsUnwrappedVar = False
9303 unwrappedVar = "obj"
9304 if descriptor.interface.isJSImplemented():
9305 # We need the desired proto in our constructor, because the
9306 # constructor will actually construct our reflector.
9307 argsPost.append("desiredProto")
9308 elif descriptor.interface.isJSImplemented():
9309 if not idlNode.isStatic():
9310 needsUnwrap = True
9311 needsUnwrappedVar = True
9312 argsPost.append(
9313 "(unwrappedObj ? js::GetNonCCWObjectRealm(*unwrappedObj) : js::GetContextRealm(cx))"
9315 elif needScopeObject(
9316 returnType,
9317 arguments,
9318 self.extendedAttributes,
9319 descriptor.wrapperCache,
9320 True,
9321 idlNode.getExtendedAttribute("StoreInSlot"),
9323 # If we ever end up with APIs like this on cross-origin objects,
9324 # figure out how the CheckedUnwrapDynamic bits should work. Chances
9325 # are, just calling it with "cx" is fine... For now, though, just
9326 # assert that it does not matter.
9327 assert not descriptor.isMaybeCrossOriginObject()
9328 # The scope object should always be from the relevant
9329 # global. Make sure to unwrap it as needed.
9330 cgThings.append(
9331 CGGeneric(
9332 dedent(
9334 JS::Rooted<JSObject*> unwrappedObj(cx, js::CheckedUnwrapStatic(obj));
9335 // Caller should have ensured that "obj" can be unwrapped already.
9336 MOZ_DIAGNOSTIC_ASSERT(unwrappedObj);
9341 argsPre.append("unwrappedObj")
9343 if needsUnwrap and needsUnwrappedVar:
9344 # We cannot assign into obj because it's a Handle, not a
9345 # MutableHandle, so we need a separate Rooted.
9346 cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;\n"))
9347 unwrappedVar = "unwrappedObj.ref()"
9349 if idlNode.isMethod() and idlNode.isLegacycaller():
9350 # If we can have legacycaller with identifier, we can't
9351 # just use the idlNode to determine whether we're
9352 # generating code for the legacycaller or not.
9353 assert idlNode.isIdentifierLess()
9354 # Pass in our thisVal
9355 argsPre.append("args.thisv()")
9357 if idlNode.isMethod():
9358 argDescription = "argument %(index)d"
9359 elif setter:
9360 argDescription = "value being assigned"
9361 else:
9362 assert self.argCount == 0
9364 if needsUnwrap:
9365 # It's very important that we construct our unwrappedObj, if we need
9366 # to do it, before we might start setting up Rooted things for our
9367 # arguments, so that we don't violate the stack discipline Rooted
9368 # depends on.
9369 cgThings.append(
9370 CGGeneric("bool objIsXray = xpc::WrapperFactory::IsXrayWrapper(obj);\n")
9372 if needsUnwrappedVar:
9373 cgThings.append(
9374 CGIfWrapper(
9375 CGGeneric("unwrappedObj.emplace(cx, obj);\n"), "objIsXray"
9379 for i in range(argConversionStartsAt, self.argCount):
9380 cgThings.append(
9381 CGArgumentConverter(
9382 arguments[i],
9384 self.descriptor,
9385 argDescription % {"index": i + 1},
9386 idlNode,
9387 invalidEnumValueFatal=not setter,
9388 lenientFloatCode=lenientFloatCode,
9392 # Now that argument processing is done, enforce the LenientFloat stuff
9393 if lenientFloatCode:
9394 if setter:
9395 foundNonFiniteFloatBehavior = "return true;\n"
9396 else:
9397 assert idlNode.isMethod()
9398 foundNonFiniteFloatBehavior = dedent(
9400 args.rval().setUndefined();
9401 return true;
9404 cgThings.append(
9405 CGGeneric(
9406 fill(
9408 if (foundNonFiniteFloat) {
9409 $*{returnSteps}
9411 """,
9412 returnSteps=foundNonFiniteFloatBehavior,
9417 if needsUnwrap:
9418 # Something depends on having the unwrapped object, so unwrap it now.
9419 xraySteps = []
9420 # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is
9421 # not null.
9422 xraySteps.append(
9423 CGGeneric(
9424 fill(
9426 // Since our object is an Xray, we can just CheckedUnwrapStatic:
9427 // we know Xrays have no dynamic unwrap behavior.
9428 ${obj} = js::CheckedUnwrapStatic(${obj});
9429 if (!${obj}) {
9430 return false;
9432 """,
9433 obj=unwrappedVar,
9437 if isConstructor:
9438 # If we're called via an xray, we need to enter the underlying
9439 # object's compartment and then wrap up all of our arguments into
9440 # that compartment as needed. This is all happening after we've
9441 # already done the conversions from JS values to WebIDL (C++)
9442 # values, so we only need to worry about cases where there are 'any'
9443 # or 'object' types, or other things that we represent as actual
9444 # JSAPI types, present. Effectively, we're emulating a
9445 # CrossCompartmentWrapper, but working with the C++ types, not the
9446 # original list of JS::Values.
9447 cgThings.append(CGGeneric("Maybe<JSAutoRealm> ar;\n"))
9448 xraySteps.append(CGGeneric("ar.emplace(cx, obj);\n"))
9449 xraySteps.append(
9450 CGGeneric(
9451 dedent(
9453 if (!JS_WrapObject(cx, &desiredProto)) {
9454 return false;
9460 xraySteps.extend(
9461 wrapArgIntoCurrentCompartment(arg, argname, isMember=False)
9462 for arg, argname in self.getArguments()
9465 cgThings.append(CGIfWrapper(CGList(xraySteps), "objIsXray"))
9467 if idlNode.getExtendedAttribute("CEReactions") is not None and not getter:
9468 cgThings.append(
9469 CGGeneric(
9470 dedent(
9472 Maybe<AutoCEReaction> ceReaction;
9473 DocGroup* docGroup = self->GetDocGroup();
9474 if (docGroup) {
9475 ceReaction.emplace(docGroup->CustomElementReactionsStack(), cx);
9482 # If this is a method that was generated by a maplike/setlike
9483 # interface, use the maplike/setlike generator to fill in the body.
9484 # Otherwise, use CGCallGenerator to call the native method.
9485 if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod():
9486 if (
9487 idlNode.maplikeOrSetlikeOrIterable.isMaplike()
9488 or idlNode.maplikeOrSetlikeOrIterable.isSetlike()
9490 cgThings.append(
9491 CGMaplikeOrSetlikeMethodGenerator(
9492 descriptor,
9493 idlNode.maplikeOrSetlikeOrIterable,
9494 idlNode.identifier.name,
9497 else:
9498 cgThings.append(
9499 CGIterableMethodGenerator(
9500 descriptor,
9501 idlNode.maplikeOrSetlikeOrIterable,
9502 idlNode.identifier.name,
9505 else:
9506 context = GetLabelForErrorReporting(descriptor, idlNode, isConstructor)
9507 if getter:
9508 context = context + " getter"
9509 elif setter:
9510 context = context + " setter"
9511 # Callee expects a quoted string for the context if
9512 # there's a context.
9513 context = '"%s"' % context
9514 cgThings.append(
9515 CGCallGenerator(
9516 self.needsErrorResult(),
9517 needsCallerType(idlNode),
9518 isChromeOnly(idlNode),
9519 self.getArguments(),
9520 argsPre,
9521 returnType,
9522 self.extendedAttributes,
9523 descriptor,
9524 nativeMethodName,
9525 static,
9526 # We know our "self" must be being kept alive; otherwise we have
9527 # a serious problem. In common cases it's just an argument and
9528 # we're MOZ_CAN_RUN_SCRIPT, but in some cases it's on the stack
9529 # and being kept alive via references from JS.
9530 object="MOZ_KnownLive(self)",
9531 argsPost=argsPost,
9532 resultVar=resultVar,
9533 context=context,
9537 if useCounterName:
9538 # Generate a telemetry call for when [UseCounter] is used.
9539 windowCode = fill(
9541 SetUseCounter(obj, eUseCounter_${useCounterName});
9542 """,
9543 useCounterName=useCounterName,
9545 workerCode = fill(
9547 SetUseCounter(UseCounterWorker::${useCounterName});
9548 """,
9549 useCounterName=useCounterName,
9551 code = ""
9552 if idlNode.isExposedInWindow() and idlNode.isExposedInAnyWorker():
9553 code += fill(
9555 if (NS_IsMainThread()) {
9556 ${windowCode}
9557 } else {
9558 ${workerCode}
9560 """,
9561 windowCode=windowCode,
9562 workerCode=workerCode,
9564 elif idlNode.isExposedInWindow():
9565 code += windowCode
9566 elif idlNode.isExposedInAnyWorker():
9567 code += workerCode
9569 cgThings.append(CGGeneric(code))
9571 self.cgRoot = CGList(cgThings)
9573 def getArguments(self):
9574 return [(a, "arg" + str(i)) for i, a in enumerate(self.arguments)]
9576 def needsErrorResult(self):
9577 return "needsErrorResult" in self.extendedAttributes
9579 def wrap_return_value(self):
9580 wrapCode = ""
9582 returnsNewObject = memberReturnsNewObject(self.idlNode)
9583 if returnsNewObject and (
9584 self.returnType.isGeckoInterface() or self.returnType.isPromise()
9586 wrapCode += dedent(
9588 static_assert(!std::is_pointer_v<decltype(result)>,
9589 "NewObject implies that we need to keep the object alive with a strong reference.");
9593 if self.setSlot:
9594 # For attributes in slots, we want to do some
9595 # post-processing once we've wrapped them.
9596 successCode = "break;\n"
9597 else:
9598 successCode = None
9600 resultTemplateValues = {
9601 "jsvalRef": "args.rval()",
9602 "jsvalHandle": "args.rval()",
9603 "returnsNewObject": returnsNewObject,
9604 "isConstructorRetval": self.isConstructor,
9605 "successCode": successCode,
9606 # 'obj' in this dictionary is the thing whose compartment we are
9607 # trying to do the to-JS conversion in. We're going to put that
9608 # thing in a variable named "conversionScope" if setSlot is true.
9609 # Otherwise, just use "obj" for lack of anything better.
9610 "obj": "conversionScope" if self.setSlot else "obj",
9613 wrapCode += wrapForType(self.returnType, self.descriptor, resultTemplateValues)
9615 if self.setSlot:
9616 if self.idlNode.isStatic():
9617 raise TypeError(
9618 "Attribute %s.%s is static, so we don't have a useful slot "
9619 "to cache it in, because we don't have support for that on "
9620 "interface objects. See "
9621 "https://bugzilla.mozilla.org/show_bug.cgi?id=1363870"
9623 self.descriptor.interface.identifier.name,
9624 self.idlNode.identifier.name,
9628 # When using a slot on the Xray expando, we need to make sure that
9629 # our initial conversion to a JS::Value is done in the caller
9630 # compartment. When using a slot on our reflector, we want to do
9631 # the conversion in the compartment of that reflector (that is,
9632 # slotStorage). In both cases we want to make sure that we finally
9633 # set up args.rval() to be in the caller compartment. We also need
9634 # to make sure that the conversion steps happen inside a do/while
9635 # that they can break out of on success.
9637 # Of course we always have to wrap the value into the slotStorage
9638 # compartment before we store it in slotStorage.
9640 # postConversionSteps are the steps that run while we're still in
9641 # the compartment we do our conversion in but after we've finished
9642 # the initial conversion into args.rval().
9643 postConversionSteps = ""
9644 if needsContainsHack(self.idlNode):
9645 # Define a .contains on the object that has the same value as
9646 # .includes; needed for backwards compat in extensions as we
9647 # migrate some DOMStringLists to FrozenArray.
9648 postConversionSteps += dedent(
9650 if (args.rval().isObject() && nsContentUtils::ThreadsafeIsSystemCaller(cx)) {
9651 JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());
9652 JS::Rooted<JS::Value> includesVal(cx);
9653 if (!JS_GetProperty(cx, rvalObj, "includes", &includesVal) ||
9654 !JS_DefineProperty(cx, rvalObj, "contains", includesVal, JSPROP_ENUMERATE)) {
9655 return false;
9661 if self.idlNode.getExtendedAttribute("Frozen"):
9662 assert (
9663 self.idlNode.type.isSequence() or self.idlNode.type.isDictionary()
9665 freezeValue = CGGeneric(
9666 "JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());\n"
9667 "if (!JS_FreezeObject(cx, rvalObj)) {\n"
9668 " return false;\n"
9669 "}\n"
9671 if self.idlNode.type.nullable():
9672 freezeValue = CGIfWrapper(freezeValue, "args.rval().isObject()")
9673 postConversionSteps += freezeValue.define()
9675 # slotStorageSteps are steps that run once we have entered the
9676 # slotStorage compartment.
9677 slotStorageSteps = fill(
9679 // Make a copy so that we don't do unnecessary wrapping on args.rval().
9680 JS::Rooted<JS::Value> storedVal(cx, args.rval());
9681 if (!${maybeWrap}(cx, &storedVal)) {
9682 return false;
9684 JS::SetReservedSlot(slotStorage, slotIndex, storedVal);
9685 """,
9686 maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type),
9689 checkForXray = mayUseXrayExpandoSlots(self.descriptor, self.idlNode)
9691 # For the case of Cached attributes, go ahead and preserve our
9692 # wrapper if needed. We need to do this because otherwise the
9693 # wrapper could get garbage-collected and the cached value would
9694 # suddenly disappear, but the whole premise of cached values is that
9695 # they never change without explicit action on someone's part. We
9696 # don't do this for StoreInSlot, since those get dealt with during
9697 # wrapper setup, and failure would involve us trying to clear an
9698 # already-preserved wrapper.
9699 if (
9700 self.idlNode.getExtendedAttribute("Cached")
9701 and self.descriptor.wrapperCache
9703 preserveWrapper = dedent(
9705 PreserveWrapper(self);
9708 if checkForXray:
9709 preserveWrapper = fill(
9711 if (!isXray) {
9712 // In the Xray case we don't need to do this, because getting the
9713 // expando object already preserved our wrapper.
9714 $*{preserveWrapper}
9716 """,
9717 preserveWrapper=preserveWrapper,
9719 slotStorageSteps += preserveWrapper
9721 if checkForXray:
9722 # In the Xray case we use the current global as conversion
9723 # scope, as explained in the big compartment/conversion comment
9724 # above.
9725 conversionScope = "isXray ? JS::CurrentGlobalOrNull(cx) : slotStorage"
9726 else:
9727 conversionScope = "slotStorage"
9729 wrapCode = fill(
9732 JS::Rooted<JSObject*> conversionScope(cx, ${conversionScope});
9733 JSAutoRealm ar(cx, conversionScope);
9734 do { // block we break out of when done wrapping
9735 $*{wrapCode}
9736 } while (false);
9737 $*{postConversionSteps}
9739 { // And now store things in the realm of our slotStorage.
9740 JSAutoRealm ar(cx, slotStorage);
9741 $*{slotStorageSteps}
9743 // And now make sure args.rval() is in the caller realm.
9744 return ${maybeWrap}(cx, args.rval());
9745 """,
9746 conversionScope=conversionScope,
9747 wrapCode=wrapCode,
9748 postConversionSteps=postConversionSteps,
9749 slotStorageSteps=slotStorageSteps,
9750 maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type),
9752 return wrapCode
9754 def define(self):
9755 return self.cgRoot.define() + self.wrap_return_value()
9758 class CGSwitch(CGList):
9760 A class to generate code for a switch statement.
9762 Takes three constructor arguments: an expression, a list of cases,
9763 and an optional default.
9765 Each case is a CGCase. The default is a CGThing for the body of
9766 the default case, if any.
9769 def __init__(self, expression, cases, default=None):
9770 CGList.__init__(self, [CGIndenter(c) for c in cases])
9771 self.prepend(CGGeneric("switch (" + expression + ") {\n"))
9772 if default is not None:
9773 self.append(
9774 CGIndenter(
9775 CGWrapper(
9776 CGIndenter(default), pre="default: {\n", post=" break;\n}\n"
9781 self.append(CGGeneric("}\n"))
9784 class CGCase(CGList):
9786 A class to generate code for a case statement.
9788 Takes three constructor arguments: an expression, a CGThing for
9789 the body (allowed to be None if there is no body), and an optional
9790 argument (defaulting to False) for whether to fall through.
9793 def __init__(self, expression, body, fallThrough=False):
9794 CGList.__init__(self, [])
9795 self.append(CGGeneric("case " + expression + ": {\n"))
9796 bodyList = CGList([body])
9797 if fallThrough:
9798 bodyList.append(CGGeneric("[[fallthrough]];\n"))
9799 else:
9800 bodyList.append(CGGeneric("break;\n"))
9801 self.append(CGIndenter(bodyList))
9802 self.append(CGGeneric("}\n"))
9805 class CGMethodCall(CGThing):
9807 A class to generate selection of a method signature from a set of
9808 signatures and generation of a call to that signature.
9811 def __init__(
9812 self, nativeMethodName, static, descriptor, method, isConstructor=False
9814 CGThing.__init__(self)
9816 methodName = GetLabelForErrorReporting(descriptor, method, isConstructor)
9817 argDesc = "argument %d"
9819 if method.getExtendedAttribute("UseCounter"):
9820 useCounterName = methodName.replace(".", "_")
9821 else:
9822 useCounterName = None
9824 if method.isStatic():
9825 nativeType = descriptor.nativeType
9826 staticTypeOverride = PropertyDefiner.getStringAttr(
9827 method, "StaticClassOverride"
9829 if staticTypeOverride:
9830 nativeType = staticTypeOverride
9831 nativeMethodName = "%s::%s" % (nativeType, nativeMethodName)
9833 def requiredArgCount(signature):
9834 arguments = signature[1]
9835 if len(arguments) == 0:
9836 return 0
9837 requiredArgs = len(arguments)
9838 while requiredArgs and arguments[requiredArgs - 1].optional:
9839 requiredArgs -= 1
9840 return requiredArgs
9842 def getPerSignatureCall(signature, argConversionStartsAt=0):
9843 return CGPerSignatureCall(
9844 signature[0],
9845 signature[1],
9846 nativeMethodName,
9847 static,
9848 descriptor,
9849 method,
9850 argConversionStartsAt=argConversionStartsAt,
9851 isConstructor=isConstructor,
9852 useCounterName=useCounterName,
9855 signatures = method.signatures()
9856 if len(signatures) == 1:
9857 # Special case: we can just do a per-signature method call
9858 # here for our one signature and not worry about switching
9859 # on anything.
9860 signature = signatures[0]
9861 self.cgRoot = CGList([getPerSignatureCall(signature)])
9862 requiredArgs = requiredArgCount(signature)
9864 # Skip required arguments check for maplike/setlike interfaces, as
9865 # they can have arguments which are not passed, and are treated as
9866 # if undefined had been explicitly passed.
9867 if requiredArgs > 0 and not method.isMaplikeOrSetlikeOrIterableMethod():
9868 code = fill(
9870 if (!args.requireAtLeast(cx, "${methodName}", ${requiredArgs})) {
9871 return false;
9873 """,
9874 requiredArgs=requiredArgs,
9875 methodName=methodName,
9877 self.cgRoot.prepend(CGGeneric(code))
9878 return
9880 # Need to find the right overload
9881 maxArgCount = method.maxArgCount
9882 allowedArgCounts = method.allowedArgCounts
9884 argCountCases = []
9885 for argCountIdx, argCount in enumerate(allowedArgCounts):
9886 possibleSignatures = method.signaturesForArgCount(argCount)
9888 # Try to optimize away cases when the next argCount in the list
9889 # will have the same code as us; if it does, we can fall through to
9890 # that case.
9891 if argCountIdx + 1 < len(allowedArgCounts):
9892 nextPossibleSignatures = method.signaturesForArgCount(
9893 allowedArgCounts[argCountIdx + 1]
9895 else:
9896 nextPossibleSignatures = None
9897 if possibleSignatures == nextPossibleSignatures:
9898 # Same set of signatures means we better have the same
9899 # distinguishing index. So we can in fact just fall through to
9900 # the next case here.
9901 assert len(possibleSignatures) == 1 or (
9902 method.distinguishingIndexForArgCount(argCount)
9903 == method.distinguishingIndexForArgCount(
9904 allowedArgCounts[argCountIdx + 1]
9907 argCountCases.append(CGCase(str(argCount), None, True))
9908 continue
9910 if len(possibleSignatures) == 1:
9911 # easy case!
9912 signature = possibleSignatures[0]
9913 argCountCases.append(
9914 CGCase(str(argCount), getPerSignatureCall(signature))
9916 continue
9918 distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
9920 def distinguishingArgument(signature):
9921 args = signature[1]
9922 if distinguishingIndex < len(args):
9923 return args[distinguishingIndex]
9924 assert args[-1].variadic
9925 return args[-1]
9927 def distinguishingType(signature):
9928 return distinguishingArgument(signature).type
9930 for sig in possibleSignatures:
9931 # We should not have "any" args at distinguishingIndex,
9932 # since we have multiple possible signatures remaining,
9933 # but "any" is never distinguishable from anything else.
9934 assert not distinguishingType(sig).isAny()
9935 # We can't handle unions at the distinguishing index.
9936 if distinguishingType(sig).isUnion():
9937 raise TypeError(
9938 "No support for unions as distinguishing "
9939 "arguments yet: %s" % distinguishingArgument(sig).location
9941 # We don't support variadics as the distinguishingArgument yet.
9942 # If you want to add support, consider this case:
9944 # void(long... foo);
9945 # void(long bar, Int32Array baz);
9947 # in which we have to convert argument 0 to long before picking
9948 # an overload... but all the variadic stuff needs to go into a
9949 # single array in case we pick that overload, so we have to have
9950 # machinery for converting argument 0 to long and then either
9951 # placing it in the variadic bit or not. Or something. We may
9952 # be able to loosen this restriction if the variadic arg is in
9953 # fact at distinguishingIndex, perhaps. Would need to
9954 # double-check.
9955 if distinguishingArgument(sig).variadic:
9956 raise TypeError(
9957 "No support for variadics as distinguishing "
9958 "arguments yet: %s" % distinguishingArgument(sig).location
9961 # Convert all our arguments up to the distinguishing index.
9962 # Doesn't matter which of the possible signatures we use, since
9963 # they all have the same types up to that point; just use
9964 # possibleSignatures[0]
9965 caseBody = [
9966 CGArgumentConverter(
9967 possibleSignatures[0][1][i],
9969 descriptor,
9970 argDesc % (i + 1),
9971 method,
9973 for i in range(0, distinguishingIndex)
9976 # Select the right overload from our set.
9977 distinguishingArg = "args[%d]" % distinguishingIndex
9979 def tryCall(
9980 signature, indent, isDefinitelyObject=False, isNullOrUndefined=False
9982 assert not isDefinitelyObject or not isNullOrUndefined
9983 assert isDefinitelyObject or isNullOrUndefined
9984 if isDefinitelyObject:
9985 failureCode = "break;\n"
9986 else:
9987 failureCode = None
9988 type = distinguishingType(signature)
9989 # The argument at index distinguishingIndex can't possibly be
9990 # unset here, because we've already checked that argc is large
9991 # enough that we can examine this argument. But note that we
9992 # still want to claim that optional arguments are optional, in
9993 # case undefined was passed in.
9994 argIsOptional = distinguishingArgument(signature).canHaveMissingValue()
9995 testCode = instantiateJSToNativeConversion(
9996 getJSToNativeConversionInfo(
9997 type,
9998 descriptor,
9999 failureCode=failureCode,
10000 isDefinitelyObject=isDefinitelyObject,
10001 isNullOrUndefined=isNullOrUndefined,
10002 isOptional=argIsOptional,
10003 sourceDescription=(argDesc % (distinguishingIndex + 1)),
10006 "declName": "arg%d" % distinguishingIndex,
10007 "holderName": ("arg%d" % distinguishingIndex) + "_holder",
10008 "val": distinguishingArg,
10009 "obj": "obj",
10010 "haveValue": "args.hasDefined(%d)" % distinguishingIndex,
10011 "passedToJSImpl": toStringBool(
10012 isJSImplementedDescriptor(descriptor)
10015 checkForValue=argIsOptional,
10017 caseBody.append(CGIndenter(testCode, indent))
10019 # If we got this far, we know we unwrapped to the right
10020 # C++ type, so just do the call. Start conversion with
10021 # distinguishingIndex + 1, since we already converted
10022 # distinguishingIndex.
10023 caseBody.append(
10024 CGIndenter(
10025 getPerSignatureCall(signature, distinguishingIndex + 1), indent
10029 def hasConditionalConversion(type):
10031 Return whether the argument conversion for this type will be
10032 conditional on the type of incoming JS value. For example, for
10033 interface types the conversion is conditional on the incoming
10034 value being isObject().
10036 For the types for which this returns false, we do not have to
10037 output extra isUndefined() or isNullOrUndefined() cases, because
10038 null/undefined values will just fall through into our
10039 unconditional conversion.
10041 if type.isString() or type.isEnum():
10042 return False
10043 if type.isBoolean():
10044 distinguishingTypes = (
10045 distinguishingType(s) for s in possibleSignatures
10047 return any(
10048 t.isString() or t.isEnum() or t.isNumeric()
10049 for t in distinguishingTypes
10051 if type.isNumeric():
10052 distinguishingTypes = (
10053 distinguishingType(s) for s in possibleSignatures
10055 return any(t.isString() or t.isEnum() for t in distinguishingTypes)
10056 return True
10058 def needsNullOrUndefinedCase(type):
10060 Return true if the type needs a special isNullOrUndefined() case
10062 return (
10063 type.nullable() and hasConditionalConversion(type)
10064 ) or type.isDictionary()
10066 # First check for undefined and optional distinguishing arguments
10067 # and output a special branch for that case. Note that we don't
10068 # use distinguishingArgument here because we actualy want to
10069 # exclude variadic arguments. Also note that we skip this check if
10070 # we plan to output a isNullOrUndefined() special case for this
10071 # argument anyway, since that will subsume our isUndefined() check.
10072 # This is safe, because there can be at most one nullable
10073 # distinguishing argument, so if we're it we'll definitely get
10074 # picked up by the nullable handling. Also, we can skip this check
10075 # if the argument has an unconditional conversion later on.
10076 undefSigs = [
10078 for s in possibleSignatures
10079 if distinguishingIndex < len(s[1])
10080 and s[1][distinguishingIndex].optional
10081 and hasConditionalConversion(s[1][distinguishingIndex].type)
10082 and not needsNullOrUndefinedCase(s[1][distinguishingIndex].type)
10084 # Can't have multiple signatures with an optional argument at the
10085 # same index.
10086 assert len(undefSigs) < 2
10087 if len(undefSigs) > 0:
10088 caseBody.append(
10089 CGGeneric("if (%s.isUndefined()) {\n" % distinguishingArg)
10091 tryCall(undefSigs[0], 2, isNullOrUndefined=True)
10092 caseBody.append(CGGeneric("}\n"))
10094 # Next, check for null or undefined. That means looking for
10095 # nullable arguments at the distinguishing index and outputting a
10096 # separate branch for them. But if the nullable argument has an
10097 # unconditional conversion, we don't need to do that. The reason
10098 # for that is that at most one argument at the distinguishing index
10099 # is nullable (since two nullable arguments are not
10100 # distinguishable), and null/undefined values will always fall
10101 # through to the unconditional conversion we have, if any, since
10102 # they will fail whatever the conditions on the input value are for
10103 # our other conversions.
10104 nullOrUndefSigs = [
10106 for s in possibleSignatures
10107 if needsNullOrUndefinedCase(distinguishingType(s))
10109 # Can't have multiple nullable types here
10110 assert len(nullOrUndefSigs) < 2
10111 if len(nullOrUndefSigs) > 0:
10112 caseBody.append(
10113 CGGeneric("if (%s.isNullOrUndefined()) {\n" % distinguishingArg)
10115 tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True)
10116 caseBody.append(CGGeneric("}\n"))
10118 # Now check for distinguishingArg being various kinds of objects.
10119 # The spec says to check for the following things in order:
10120 # 1) A platform object that's not a platform array object, being
10121 # passed to an interface or "object" arg.
10122 # 2) A callable object being passed to a callback or "object" arg.
10123 # 3) An iterable object being passed to a sequence arg.
10124 # 4) Any object being passed to a array or callback interface or
10125 # dictionary or "object" arg.
10127 # First grab all the overloads that have a non-callback interface
10128 # (which includes SpiderMonkey interfaces) at the distinguishing
10129 # index. We can also include the ones that have an "object" here,
10130 # since if those are present no other object-typed argument will
10131 # be.
10132 objectSigs = [
10134 for s in possibleSignatures
10135 if (
10136 distinguishingType(s).isObject()
10137 or distinguishingType(s).isNonCallbackInterface()
10141 # And all the overloads that take callbacks
10142 objectSigs.extend(
10143 s for s in possibleSignatures if distinguishingType(s).isCallback()
10146 # And all the overloads that take sequences
10147 objectSigs.extend(
10148 s for s in possibleSignatures if distinguishingType(s).isSequence()
10151 # Now append all the overloads that take a dictionary or callback
10152 # interface or record. There should be only one of these!
10153 genericObjectSigs = [
10155 for s in possibleSignatures
10156 if (
10157 distinguishingType(s).isDictionary()
10158 or distinguishingType(s).isRecord()
10159 or distinguishingType(s).isCallbackInterface()
10162 assert len(genericObjectSigs) <= 1
10163 objectSigs.extend(genericObjectSigs)
10165 # There might be more than one thing in objectSigs; we need to check
10166 # which ones we unwrap to.
10167 if len(objectSigs) > 0:
10168 # Here it's enough to guard on our argument being an object.
10169 # The code for unwrapping non-callback interfaces, spiderMonkey
10170 # interfaces, and sequences will just bail out and move
10171 # on to the next overload if the object fails to unwrap
10172 # correctly, while "object" accepts any object anyway. We
10173 # could even not do the isObject() check up front here, but in
10174 # cases where we have multiple object overloads it makes sense
10175 # to do it only once instead of for each overload. That will
10176 # also allow the unwrapping test to skip having to do codegen
10177 # for the null-or-undefined case, which we already handled
10178 # above.
10179 caseBody.append(CGGeneric("if (%s.isObject()) {\n" % distinguishingArg))
10180 for sig in objectSigs:
10181 caseBody.append(CGIndenter(CGGeneric("do {\n")))
10182 # Indent by 4, since we need to indent further
10183 # than our "do" statement
10184 tryCall(sig, 4, isDefinitelyObject=True)
10185 caseBody.append(CGIndenter(CGGeneric("} while (false);\n")))
10187 caseBody.append(CGGeneric("}\n"))
10189 # Now we only have to consider booleans, numerics, and strings. If
10190 # we only have one of them, then we can just output it. But if not,
10191 # then we need to output some of the cases conditionally: if we have
10192 # a string overload, then boolean and numeric are conditional, and
10193 # if not then boolean is conditional if we have a numeric overload.
10194 def findUniqueSignature(filterLambda):
10195 sigs = [s for s in possibleSignatures if filterLambda(s)]
10196 assert len(sigs) < 2
10197 if len(sigs) > 0:
10198 return sigs[0]
10199 return None
10201 stringSignature = findUniqueSignature(
10202 lambda s: (
10203 distinguishingType(s).isString() or distinguishingType(s).isEnum()
10206 numericSignature = findUniqueSignature(
10207 lambda s: distinguishingType(s).isNumeric()
10209 booleanSignature = findUniqueSignature(
10210 lambda s: distinguishingType(s).isBoolean()
10213 if stringSignature or numericSignature:
10214 booleanCondition = "%s.isBoolean()"
10215 else:
10216 booleanCondition = None
10218 if stringSignature:
10219 numericCondition = "%s.isNumber()"
10220 else:
10221 numericCondition = None
10223 def addCase(sig, condition):
10224 sigCode = getPerSignatureCall(sig, distinguishingIndex)
10225 if condition:
10226 sigCode = CGIfWrapper(sigCode, condition % distinguishingArg)
10227 caseBody.append(sigCode)
10229 if booleanSignature:
10230 addCase(booleanSignature, booleanCondition)
10231 if numericSignature:
10232 addCase(numericSignature, numericCondition)
10233 if stringSignature:
10234 addCase(stringSignature, None)
10236 if not booleanSignature and not numericSignature and not stringSignature:
10237 # Just throw; we have no idea what we're supposed to
10238 # do with this.
10239 caseBody.append(
10240 CGGeneric(
10241 'return cx.ThrowErrorMessage<MSG_OVERLOAD_RESOLUTION_FAILED>("%d", "%d");\n'
10242 % (distinguishingIndex + 1, argCount)
10246 argCountCases.append(CGCase(str(argCount), CGList(caseBody)))
10248 overloadCGThings = []
10249 overloadCGThings.append(
10250 CGGeneric(
10251 "unsigned argcount = std::min(args.length(), %du);\n" % maxArgCount
10254 overloadCGThings.append(
10255 CGSwitch(
10256 "argcount",
10257 argCountCases,
10258 CGGeneric(
10259 dedent(
10261 // Using nsPrintfCString here would require including that
10262 // header. Let's not worry about it.
10263 nsAutoCString argCountStr;
10264 argCountStr.AppendPrintf("%u", args.length());
10265 return cx.ThrowErrorMessage<MSG_INVALID_OVERLOAD_ARGCOUNT>(argCountStr.get());
10271 overloadCGThings.append(
10272 CGGeneric(
10273 'MOZ_CRASH("We have an always-returning default case");\n'
10274 "return false;\n"
10277 self.cgRoot = CGList(overloadCGThings)
10279 def define(self):
10280 return self.cgRoot.define()
10283 class CGGetterCall(CGPerSignatureCall):
10285 A class to generate a native object getter call for a particular IDL
10286 getter.
10289 def __init__(
10290 self,
10291 returnType,
10292 nativeMethodName,
10293 descriptor,
10294 attr,
10295 dontSetSlot=False,
10296 extendedAttributes=None,
10298 if attr.getExtendedAttribute("UseCounter"):
10299 useCounterName = "%s_%s_getter" % (
10300 descriptor.interface.identifier.name,
10301 attr.identifier.name,
10303 else:
10304 useCounterName = None
10305 if attr.isStatic():
10306 nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName)
10307 CGPerSignatureCall.__init__(
10308 self,
10309 returnType,
10311 nativeMethodName,
10312 attr.isStatic(),
10313 descriptor,
10314 attr,
10315 getter=True,
10316 useCounterName=useCounterName,
10317 dontSetSlot=dontSetSlot,
10318 extendedAttributes=extendedAttributes,
10322 class FakeIdentifier:
10323 def __init__(self, name):
10324 self.name = name
10327 class FakeArgument:
10329 A class that quacks like an IDLArgument. This is used to make
10330 setters look like method calls or for special operations.
10333 def __init__(
10334 self, type, interfaceMember, name="arg", allowTreatNonCallableAsNull=False
10336 self.type = type
10337 self.optional = False
10338 self.variadic = False
10339 self.defaultValue = None
10340 self._allowTreatNonCallableAsNull = allowTreatNonCallableAsNull
10342 self.identifier = FakeIdentifier(name)
10344 def allowTreatNonCallableAsNull(self):
10345 return self._allowTreatNonCallableAsNull
10347 def canHaveMissingValue(self):
10348 return False
10351 class CGSetterCall(CGPerSignatureCall):
10353 A class to generate a native object setter call for a particular IDL
10354 setter.
10357 def __init__(self, argType, nativeMethodName, descriptor, attr):
10358 if attr.getExtendedAttribute("UseCounter"):
10359 useCounterName = "%s_%s_setter" % (
10360 descriptor.interface.identifier.name,
10361 attr.identifier.name,
10363 else:
10364 useCounterName = None
10365 if attr.isStatic():
10366 nativeMethodName = "%s::%s" % (descriptor.nativeType, nativeMethodName)
10367 CGPerSignatureCall.__init__(
10368 self,
10369 None,
10370 [FakeArgument(argType, attr, allowTreatNonCallableAsNull=True)],
10371 nativeMethodName,
10372 attr.isStatic(),
10373 descriptor,
10374 attr,
10375 setter=True,
10376 useCounterName=useCounterName,
10379 def wrap_return_value(self):
10380 attr = self.idlNode
10381 if self.descriptor.wrapperCache and attr.slotIndices is not None:
10382 if attr.getExtendedAttribute("StoreInSlot"):
10383 args = "cx, self"
10384 else:
10385 args = "self"
10386 clearSlot = "%s(%s);\n" % (
10387 MakeClearCachedValueNativeName(self.idlNode),
10388 args,
10390 else:
10391 clearSlot = ""
10393 # We have no return value
10394 return "\n" "%s" "return true;\n" % clearSlot
10397 class CGAbstractBindingMethod(CGAbstractStaticMethod):
10399 Common class to generate some of our class hooks. This will generate the
10400 function declaration, get a reference to the JS object for our binding
10401 object (which might be an argument of the class hook or something we get
10402 from a JS::CallArgs), and unwrap into the right C++ type. Subclasses are
10403 expected to override the generate_code function to do the rest of the work.
10404 This function should return a CGThing which is already properly indented.
10406 getThisObj should be code for getting a JSObject* for the binding
10407 object. "" can be passed in if the binding object is already stored in
10408 'obj'.
10410 callArgs should be code for getting a JS::CallArgs into a variable
10411 called 'args'. This can be "" if there is already such a variable
10412 around or if the body does not need a JS::CallArgs.
10416 def __init__(
10417 self,
10418 descriptor,
10419 name,
10420 args,
10421 getThisObj,
10422 callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n",
10424 CGAbstractStaticMethod.__init__(
10425 self, descriptor, name, "bool", args, canRunScript=True
10428 # This can't ever happen, because we only use this for class hooks.
10429 self.unwrapFailureCode = fill(
10431 MOZ_CRASH("Unexpected object in '${name}' hook");
10432 return false;
10433 """,
10434 name=name,
10437 if getThisObj == "":
10438 self.getThisObj = None
10439 else:
10440 self.getThisObj = CGGeneric(
10441 "JS::Rooted<JSObject*> obj(cx, %s);\n" % getThisObj
10443 self.callArgs = callArgs
10445 def definition_body(self):
10446 body = self.callArgs
10447 if self.getThisObj is not None:
10448 body += self.getThisObj.define() + "\n"
10449 body += "%s* self;\n" % self.descriptor.nativeType
10450 body += dedent(
10452 JS::Rooted<JS::Value> rootSelf(cx, JS::ObjectValue(*obj));
10456 body += str(
10457 CastableObjectUnwrapper(
10458 self.descriptor, "rootSelf", "&rootSelf", "self", self.unwrapFailureCode
10462 return body + self.generate_code().define()
10464 def generate_code(self):
10465 assert False # Override me
10468 class CGAbstractStaticBindingMethod(CGAbstractStaticMethod):
10470 Common class to generate the JSNatives for all our static methods, getters
10471 and setters. This will generate the function declaration and unwrap the
10472 global object. Subclasses are expected to override the generate_code
10473 function to do the rest of the work. This function should return a
10474 CGThing which is already properly indented.
10477 def __init__(self, descriptor, name):
10478 CGAbstractStaticMethod.__init__(
10479 self, descriptor, name, "bool", JSNativeArguments(), canRunScript=True
10482 def definition_body(self):
10483 # Make sure that "obj" is in the same compartment as "cx", since we'll
10484 # later use it to wrap return values.
10485 unwrap = dedent(
10487 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
10488 JS::Rooted<JSObject*> obj(cx, &args.callee());
10492 return unwrap + self.generate_code().define()
10494 def generate_code(self):
10495 assert False # Override me
10498 def MakeNativeName(name):
10499 return name[0].upper() + IDLToCIdentifier(name[1:])
10502 def GetWebExposedName(idlObject, descriptor):
10503 if idlObject == descriptor.operations["Stringifier"]:
10504 return "toString"
10505 name = idlObject.identifier.name
10506 if name == "__namedsetter":
10507 return "named setter"
10508 if name == "__namedgetter":
10509 return "named getter"
10510 if name == "__indexedsetter":
10511 return "indexed setter"
10512 if name == "__indexedgetter":
10513 return "indexed getter"
10514 if name == "__legacycaller":
10515 return "legacy caller"
10516 return name
10519 def GetConstructorNameForReporting(descriptor, ctor):
10520 # Figure out the name of our constructor for reporting purposes.
10521 # For unnamed webidl constructors, identifier.name is "constructor" but
10522 # the name JS sees is the interface name; for legacy factory functions
10523 # identifier.name is the actual name.
10524 ctorName = ctor.identifier.name
10525 if ctorName == "constructor":
10526 return descriptor.interface.identifier.name
10527 return ctorName
10530 def GetLabelForErrorReporting(descriptor, idlObject, isConstructor):
10532 descriptor is the descriptor for the interface involved
10534 idlObject is the method (regular or static), attribute (regular or
10535 static), or constructor (named or not) involved.
10537 isConstructor is true if idlObject is a constructor and false otherwise.
10539 if isConstructor:
10540 return "%s constructor" % GetConstructorNameForReporting(descriptor, idlObject)
10542 namePrefix = descriptor.interface.identifier.name
10543 name = GetWebExposedName(idlObject, descriptor)
10544 if " " in name:
10545 # It's got a space already, so just space-separate.
10546 return "%s %s" % (namePrefix, name)
10548 return "%s.%s" % (namePrefix, name)
10551 class CGSpecializedMethod(CGAbstractStaticMethod):
10553 A class for generating the C++ code for a specialized method that the JIT
10554 can call with lower overhead.
10557 def __init__(self, descriptor, method):
10558 self.method = method
10559 name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name))
10560 args = [
10561 Argument("JSContext*", "cx"),
10562 Argument("JS::Handle<JSObject*>", "obj"),
10563 Argument("void*", "void_self"),
10564 Argument("const JSJitMethodCallArgs&", "args"),
10566 CGAbstractStaticMethod.__init__(
10567 self, descriptor, name, "bool", args, canRunScript=True
10570 def definition_body(self):
10571 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method)
10572 call = CGMethodCall(
10573 nativeName, self.method.isStatic(), self.descriptor, self.method
10574 ).define()
10575 prefix = ""
10576 if self.method.getExtendedAttribute("CrossOriginCallable"):
10577 for signature in self.method.signatures():
10578 # non-void signatures would require us to deal with remote proxies for the
10579 # return value here.
10580 if not signature[0].isVoid():
10581 raise TypeError(
10582 "We don't support a method marked as CrossOriginCallable "
10583 "with non-void return type"
10585 prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
10586 prefix = fill(
10588 // CrossOriginThisPolicy::UnwrapThisObject stores a ${nativeType}::RemoteProxy in void_self
10589 // if obj is a proxy with a RemoteObjectProxy handler for the right type, or else it stores
10590 // a ${nativeType}. If we get here from the JIT (without going through UnwrapThisObject) we
10591 // know void_self contains a ${nativeType}; we don't have special cases in the JIT to deal
10592 // with remote object proxies.
10593 if (IsRemoteObjectProxy(obj, ${prototypeID})) {
10594 auto* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
10595 $*{call}
10597 """,
10598 prototypeID=prototypeID,
10599 nativeType=self.descriptor.nativeType,
10600 call=call,
10602 return prefix + fill(
10604 auto* self = static_cast<${nativeType}*>(void_self);
10605 $*{call}
10606 """,
10607 nativeType=self.descriptor.nativeType,
10608 call=call,
10611 def auto_profiler_label(self):
10612 interface_name = self.descriptor.interface.identifier.name
10613 method_name = self.method.identifier.name
10614 return fill(
10616 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
10617 "${interface_name}", "${method_name}", DOM, cx,
10618 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_METHOD) |
10619 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
10620 """,
10621 interface_name=interface_name,
10622 method_name=method_name,
10625 @staticmethod
10626 def should_have_method_description(descriptor, idlMethod):
10628 Returns whether the given IDL method (static, non-static, constructor)
10629 should have a method description declaration, for use in error
10630 reporting.
10632 # If a method has overloads, it needs a method description, because it
10633 # can throw MSG_INVALID_OVERLOAD_ARGCOUNT at the very least.
10634 if len(idlMethod.signatures()) != 1:
10635 return True
10637 # Methods with only one signature need a method description if one of
10638 # their args needs it.
10639 sig = idlMethod.signatures()[0]
10640 args = sig[1]
10641 return any(
10642 idlTypeNeedsCallContext(
10643 arg.type,
10644 descriptor,
10645 allowTreatNonCallableAsNull=arg.allowTreatNonCallableAsNull(),
10647 for arg in args
10650 @staticmethod
10651 def error_reporting_label_helper(descriptor, idlMethod, isConstructor):
10653 Returns the method description to use for error reporting for the given
10654 IDL method. Used to implement common error_reporting_label() functions
10655 across different classes.
10657 if not CGSpecializedMethod.should_have_method_description(
10658 descriptor, idlMethod
10660 return None
10661 return GetLabelForErrorReporting(descriptor, idlMethod, isConstructor)
10663 def error_reporting_label(self):
10664 return CGSpecializedMethod.error_reporting_label_helper(
10665 self.descriptor, self.method, isConstructor=False
10668 @staticmethod
10669 def makeNativeName(descriptor, method):
10670 if method.underlyingAttr:
10671 return CGSpecializedGetter.makeNativeName(descriptor, method.underlyingAttr)
10672 name = method.identifier.name
10673 return MakeNativeName(descriptor.binaryNameFor(name))
10676 class CGMethodPromiseWrapper(CGAbstractStaticMethod):
10678 A class for generating a wrapper around another method that will
10679 convert exceptions to promises.
10682 def __init__(self, descriptor, methodToWrap):
10683 self.method = methodToWrap
10684 name = self.makeName(methodToWrap.name)
10685 args = list(methodToWrap.args)
10686 CGAbstractStaticMethod.__init__(
10687 self, descriptor, name, "bool", args, canRunScript=True
10690 def definition_body(self):
10691 return fill(
10693 bool ok = ${methodName}(${args});
10694 if (ok) {
10695 return true;
10697 return ConvertExceptionToPromise(cx, args.rval());
10698 """,
10699 methodName=self.method.name,
10700 args=", ".join(arg.name for arg in self.args),
10703 @staticmethod
10704 def makeName(methodName):
10705 return methodName + "_promiseWrapper"
10708 class CGDefaultToJSONMethod(CGSpecializedMethod):
10709 def __init__(self, descriptor, method):
10710 assert method.isDefaultToJSON()
10711 CGSpecializedMethod.__init__(self, descriptor, method)
10713 def definition_body(self):
10714 ret = fill(
10716 auto* self = static_cast<${nativeType}*>(void_self);
10717 JS::Rooted<JSObject*> result(cx, JS_NewPlainObject(cx));
10718 if (!result) {
10719 return false;
10721 """,
10722 nativeType=self.descriptor.nativeType,
10725 jsonDescriptors = [self.descriptor]
10726 interface = self.descriptor.interface.parent
10727 while interface:
10728 descriptor = self.descriptor.getDescriptor(interface.identifier.name)
10729 if descriptor.hasDefaultToJSON:
10730 jsonDescriptors.append(descriptor)
10731 interface = interface.parent
10733 # Iterate the array in reverse: oldest ancestor first
10734 for descriptor in jsonDescriptors[::-1]:
10735 ret += fill(
10737 if (!${parentclass}::CollectJSONAttributes(cx, obj, MOZ_KnownLive(self), result)) {
10738 return false;
10740 """,
10741 parentclass=toBindingNamespace(descriptor.name),
10743 ret += "args.rval().setObject(*result);\n" "return true;\n"
10744 return ret
10747 class CGLegacyCallHook(CGAbstractBindingMethod):
10749 Call hook for our object
10752 def __init__(self, descriptor):
10753 self._legacycaller = descriptor.operations["LegacyCaller"]
10754 # Our "self" is actually the callee in this case, not the thisval.
10755 CGAbstractBindingMethod.__init__(
10756 self,
10757 descriptor,
10758 LEGACYCALLER_HOOK_NAME,
10759 JSNativeArguments(),
10760 getThisObj="&args.callee()",
10763 def define(self):
10764 if not self._legacycaller:
10765 return ""
10766 return CGAbstractBindingMethod.define(self)
10768 def generate_code(self):
10769 name = self._legacycaller.identifier.name
10770 nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
10771 return CGMethodCall(nativeName, False, self.descriptor, self._legacycaller)
10773 def error_reporting_label(self):
10774 # Should act like methods.
10775 return CGSpecializedMethod.error_reporting_label_helper(
10776 self.descriptor, self._legacycaller, isConstructor=False
10780 class CGResolveHook(CGAbstractClassHook):
10782 Resolve hook for objects that have the NeedResolve extended attribute.
10785 def __init__(self, descriptor):
10786 assert descriptor.interface.getExtendedAttribute("NeedResolve")
10788 args = [
10789 Argument("JSContext*", "cx"),
10790 Argument("JS::Handle<JSObject*>", "obj"),
10791 Argument("JS::Handle<jsid>", "id"),
10792 Argument("bool*", "resolvedp"),
10794 CGAbstractClassHook.__init__(self, descriptor, RESOLVE_HOOK_NAME, "bool", args)
10796 def generate_code(self):
10797 return dedent(
10799 JS::Rooted<JS::PropertyDescriptor> desc(cx);
10800 if (!self->DoResolve(cx, obj, id, &desc)) {
10801 return false;
10803 if (!desc.object()) {
10804 return true;
10806 // If desc.value() is undefined, then the DoResolve call
10807 // has already defined it on the object. Don't try to also
10808 // define it.
10809 if (!desc.value().isUndefined()) {
10810 desc.attributesRef() |= JSPROP_RESOLVING;
10811 if (!JS_DefinePropertyById(cx, obj, id, desc)) {
10812 return false;
10815 *resolvedp = true;
10816 return true;
10820 def definition_body(self):
10821 if self.descriptor.isGlobal():
10822 # Resolve standard classes
10823 prefix = dedent(
10825 if (!ResolveGlobal(cx, obj, id, resolvedp)) {
10826 return false;
10828 if (*resolvedp) {
10829 return true;
10834 else:
10835 prefix = ""
10836 return prefix + CGAbstractClassHook.definition_body(self)
10839 class CGMayResolveHook(CGAbstractStaticMethod):
10841 Resolve hook for objects that have the NeedResolve extended attribute.
10844 def __init__(self, descriptor):
10845 assert descriptor.interface.getExtendedAttribute("NeedResolve")
10847 args = [
10848 Argument("const JSAtomState&", "names"),
10849 Argument("jsid", "id"),
10850 Argument("JSObject*", "maybeObj"),
10852 CGAbstractStaticMethod.__init__(
10853 self, descriptor, MAY_RESOLVE_HOOK_NAME, "bool", args
10856 def definition_body(self):
10857 if self.descriptor.isGlobal():
10858 # Check whether this would resolve as a standard class.
10859 prefix = dedent(
10861 if (MayResolveGlobal(names, id, maybeObj)) {
10862 return true;
10867 else:
10868 prefix = ""
10869 return prefix + "return %s::MayResolve(id);\n" % self.descriptor.nativeType
10872 class CGEnumerateHook(CGAbstractBindingMethod):
10874 Enumerate hook for objects with custom hooks.
10877 def __init__(self, descriptor):
10878 assert descriptor.interface.getExtendedAttribute("NeedResolve")
10880 args = [
10881 Argument("JSContext*", "cx"),
10882 Argument("JS::Handle<JSObject*>", "obj"),
10883 Argument("JS::MutableHandleVector<jsid>", "properties"),
10884 Argument("bool", "enumerableOnly"),
10886 # Our "self" is actually the "obj" argument in this case, not the thisval.
10887 CGAbstractBindingMethod.__init__(
10888 self, descriptor, NEW_ENUMERATE_HOOK_NAME, args, getThisObj="", callArgs=""
10891 def generate_code(self):
10892 return CGGeneric(
10893 dedent(
10895 FastErrorResult rv;
10896 self->GetOwnPropertyNames(cx, properties, enumerableOnly, rv);
10897 if (rv.MaybeSetPendingException(cx)) {
10898 return false;
10900 return true;
10905 def definition_body(self):
10906 if self.descriptor.isGlobal():
10907 # Enumerate standard classes
10908 prefix = dedent(
10910 if (!EnumerateGlobal(cx, obj, properties, enumerableOnly)) {
10911 return false;
10916 else:
10917 prefix = ""
10918 return prefix + CGAbstractBindingMethod.definition_body(self)
10921 class CppKeywords:
10923 A class for checking if method names declared in webidl
10924 are not in conflict with C++ keywords.
10927 keywords = frozenset(
10929 "alignas",
10930 "alignof",
10931 "and",
10932 "and_eq",
10933 "asm",
10934 "assert",
10935 "auto",
10936 "bitand",
10937 "bitor",
10938 "bool",
10939 "break",
10940 "case",
10941 "catch",
10942 "char",
10943 "char16_t",
10944 "char32_t",
10945 "class",
10946 "compl",
10947 "const",
10948 "constexpr",
10949 "const_cast",
10950 "continue",
10951 "decltype",
10952 "default",
10953 "delete",
10954 "do",
10955 "double",
10956 "dynamic_cast",
10957 "else",
10958 "enum",
10959 "explicit",
10960 "export",
10961 "extern",
10962 "false",
10963 "final",
10964 "float",
10965 "for",
10966 "friend",
10967 "goto",
10968 "if",
10969 "inline",
10970 "int",
10971 "long",
10972 "mutable",
10973 "namespace",
10974 "new",
10975 "noexcept",
10976 "not",
10977 "not_eq",
10978 "nullptr",
10979 "operator",
10980 "or",
10981 "or_eq",
10982 "override",
10983 "private",
10984 "protected",
10985 "public",
10986 "register",
10987 "reinterpret_cast",
10988 "return",
10989 "short",
10990 "signed",
10991 "sizeof",
10992 "static",
10993 "static_assert",
10994 "static_cast",
10995 "struct",
10996 "switch",
10997 "template",
10998 "this",
10999 "thread_local",
11000 "throw",
11001 "true",
11002 "try",
11003 "typedef",
11004 "typeid",
11005 "typename",
11006 "union",
11007 "unsigned",
11008 "using",
11009 "virtual",
11010 "void",
11011 "volatile",
11012 "wchar_t",
11013 "while",
11014 "xor",
11015 "xor_eq",
11019 @staticmethod
11020 def checkMethodName(name):
11021 # Double '_' because 'assert' and '_assert' cannot be used in MS2013 compiler.
11022 # Bug 964892 and bug 963560.
11023 if name in CppKeywords.keywords:
11024 name = "_" + name + "_"
11025 return name
11028 class CGStaticMethod(CGAbstractStaticBindingMethod):
11030 A class for generating the C++ code for an IDL static method.
11033 def __init__(self, descriptor, method):
11034 self.method = method
11035 name = CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name))
11036 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
11038 def generate_code(self):
11039 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method)
11040 return CGMethodCall(nativeName, True, self.descriptor, self.method)
11042 def auto_profiler_label(self):
11043 interface_name = self.descriptor.interface.identifier.name
11044 method_name = self.method.identifier.name
11045 return fill(
11047 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11048 "${interface_name}", "${method_name}", DOM, cx,
11049 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_METHOD) |
11050 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11051 """,
11052 interface_name=interface_name,
11053 method_name=method_name,
11056 def error_reporting_label(self):
11057 return CGSpecializedMethod.error_reporting_label_helper(
11058 self.descriptor, self.method, isConstructor=False
11062 class CGSpecializedGetter(CGAbstractStaticMethod):
11064 A class for generating the code for a specialized attribute getter
11065 that the JIT can call with lower overhead.
11068 def __init__(self, descriptor, attr):
11069 self.attr = attr
11070 name = "get_" + IDLToCIdentifier(attr.identifier.name)
11071 args = [
11072 Argument("JSContext*", "cx"),
11073 Argument("JS::Handle<JSObject*>", "obj"),
11074 Argument("void*", "void_self"),
11075 Argument("JSJitGetterCallArgs", "args"),
11077 # StoreInSlot attributes have their getters called from Wrap(). We
11078 # really hope they can't run script, and don't want to annotate Wrap()
11079 # methods as doing that anyway, so let's not annotate them as
11080 # MOZ_CAN_RUN_SCRIPT.
11081 CGAbstractStaticMethod.__init__(
11082 self,
11083 descriptor,
11084 name,
11085 "bool",
11086 args,
11087 canRunScript=not attr.getExtendedAttribute("StoreInSlot"),
11090 def definition_body(self):
11091 prefix = fill(
11093 auto* self = static_cast<${nativeType}*>(void_self);
11094 """,
11095 nativeType=self.descriptor.nativeType,
11098 if self.attr.isMaplikeOrSetlikeAttr():
11099 assert not self.attr.getExtendedAttribute("CrossOriginReadable")
11100 # If the interface is maplike/setlike, there will be one getter
11101 # method for the size property of the backing object. Due to having
11102 # to unpack the backing object from the slot, this requires its own
11103 # generator.
11104 return prefix + getMaplikeOrSetlikeSizeGetterBody(
11105 self.descriptor, self.attr
11107 nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, self.attr)
11108 type = self.attr.type
11109 if self.attr.getExtendedAttribute("CrossOriginReadable"):
11110 remoteType = type
11111 extendedAttributes = self.descriptor.getExtendedAttributes(
11112 self.attr, getter=True
11114 if (
11115 remoteType.isGeckoInterface()
11116 and not remoteType.unroll().inner.isExternal()
11117 and remoteType.unroll().inner.getExtendedAttribute("ChromeOnly") is None
11119 # We'll use a JSObject. It might make more sense to use remoteType's
11120 # RemoteProxy, but it's not easy to construct a type for that from here.
11121 remoteType = BuiltinTypes[IDLBuiltinType.Types.object]
11122 if "needsErrorResult" not in extendedAttributes:
11123 extendedAttributes.append("needsErrorResult")
11124 prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
11125 prefix = (
11126 fill(
11128 if (IsRemoteObjectProxy(obj, ${prototypeID})) {
11129 ${nativeType}::RemoteProxy* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
11130 $*{call}
11132 """,
11133 prototypeID=prototypeID,
11134 nativeType=self.descriptor.nativeType,
11135 call=CGGetterCall(
11136 remoteType,
11137 nativeName,
11138 self.descriptor,
11139 self.attr,
11140 dontSetSlot=True,
11141 extendedAttributes=extendedAttributes,
11142 ).define(),
11144 + prefix
11147 if self.attr.slotIndices is not None:
11148 # We're going to store this return value in a slot on some object,
11149 # to cache it. The question is, which object? For dictionary and
11150 # sequence return values, we want to use a slot on the Xray expando
11151 # if we're called via Xrays, and a slot on our reflector otherwise.
11152 # On the other hand, when dealing with some interfacce types
11153 # (e.g. window.document) we want to avoid calling the getter more
11154 # than once. In the case of window.document, it's because the
11155 # getter can start returning null, which would get hidden in the
11156 # non-Xray case by the fact that it's [StoreOnSlot], so the cached
11157 # version is always around.
11159 # The upshot is that we use the reflector slot for any getter whose
11160 # type is a gecko interface, whether we're called via Xrays or not.
11161 # Since [Cached] and [StoreInSlot] cannot be used with "NewObject",
11162 # we know that in the interface type case the returned object is
11163 # wrappercached. So creating Xrays to it is reasonable.
11164 if mayUseXrayExpandoSlots(self.descriptor, self.attr):
11165 prefix += fill(
11167 // Have to either root across the getter call or reget after.
11168 bool isXray;
11169 JS::Rooted<JSObject*> slotStorage(cx, GetCachedSlotStorageObject(cx, obj, &isXray));
11170 if (!slotStorage) {
11171 return false;
11173 const size_t slotIndex = isXray ? ${xraySlotIndex} : ${slotIndex};
11174 """,
11175 xraySlotIndex=memberXrayExpandoReservedSlot(
11176 self.attr, self.descriptor
11178 slotIndex=memberReservedSlot(self.attr, self.descriptor),
11180 else:
11181 prefix += fill(
11183 // Have to either root across the getter call or reget after.
11184 JS::Rooted<JSObject*> slotStorage(cx, js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false));
11185 MOZ_ASSERT(IsDOMObject(slotStorage));
11186 const size_t slotIndex = ${slotIndex};
11187 """,
11188 slotIndex=memberReservedSlot(self.attr, self.descriptor),
11191 prefix += fill(
11193 MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(JS::GetClass(slotStorage)) > slotIndex);
11195 // Scope for cachedVal
11196 JS::Value cachedVal = JS::GetReservedSlot(slotStorage, slotIndex);
11197 if (!cachedVal.isUndefined()) {
11198 args.rval().set(cachedVal);
11199 // The cached value is in the compartment of slotStorage,
11200 // so wrap into the caller compartment as needed.
11201 return ${maybeWrap}(cx, args.rval());
11205 """,
11206 maybeWrap=getMaybeWrapValueFuncForType(self.attr.type),
11209 return (
11210 prefix + CGGetterCall(type, nativeName, self.descriptor, self.attr).define()
11213 def auto_profiler_label(self):
11214 interface_name = self.descriptor.interface.identifier.name
11215 attr_name = self.attr.identifier.name
11216 return fill(
11218 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11219 "${interface_name}", "${attr_name}", DOM, cx,
11220 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER) |
11221 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11222 """,
11223 interface_name=interface_name,
11224 attr_name=attr_name,
11227 def error_reporting_label(self):
11228 # Getters never need a BindingCallContext.
11229 return None
11231 @staticmethod
11232 def makeNativeName(descriptor, attr):
11233 name = attr.identifier.name
11234 nativeName = MakeNativeName(descriptor.binaryNameFor(name))
11235 _, resultOutParam, _, _, _ = getRetvalDeclarationForType(attr.type, descriptor)
11236 extendedAttrs = descriptor.getExtendedAttributes(attr, getter=True)
11237 canFail = "needsErrorResult" in extendedAttrs or "canOOM" in extendedAttrs
11238 if resultOutParam or attr.type.nullable() or canFail:
11239 nativeName = "Get" + nativeName
11240 return nativeName
11243 class CGGetterPromiseWrapper(CGAbstractStaticMethod):
11245 A class for generating a wrapper around another getter that will
11246 convert exceptions to promises.
11249 def __init__(self, descriptor, getterToWrap):
11250 self.getter = getterToWrap
11251 name = self.makeName(getterToWrap.name)
11252 args = list(getterToWrap.args)
11253 CGAbstractStaticMethod.__init__(
11254 self, descriptor, name, "bool", args, canRunScript=True
11257 def definition_body(self):
11258 return fill(
11260 bool ok = ${getterName}(${args});
11261 if (ok) {
11262 return true;
11264 return ConvertExceptionToPromise(cx, args.rval());
11265 """,
11266 getterName=self.getter.name,
11267 args=", ".join(arg.name for arg in self.args),
11270 @staticmethod
11271 def makeName(getterName):
11272 return getterName + "_promiseWrapper"
11275 class CGStaticGetter(CGAbstractStaticBindingMethod):
11277 A class for generating the C++ code for an IDL static attribute getter.
11280 def __init__(self, descriptor, attr):
11281 self.attr = attr
11282 name = "get_" + IDLToCIdentifier(attr.identifier.name)
11283 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
11285 def generate_code(self):
11286 nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, self.attr)
11287 return CGGetterCall(self.attr.type, nativeName, self.descriptor, self.attr)
11289 def auto_profiler_label(self):
11290 interface_name = self.descriptor.interface.identifier.name
11291 attr_name = self.attr.identifier.name
11292 return fill(
11294 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11295 "${interface_name}", "${attr_name}", DOM, cx,
11296 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_GETTER) |
11297 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11298 """,
11299 interface_name=interface_name,
11300 attr_name=attr_name,
11303 def error_reporting_label(self):
11304 # Getters never need a BindingCallContext.
11305 return None
11308 class CGSpecializedSetter(CGAbstractStaticMethod):
11310 A class for generating the code for a specialized attribute setter
11311 that the JIT can call with lower overhead.
11314 def __init__(self, descriptor, attr):
11315 self.attr = attr
11316 name = "set_" + IDLToCIdentifier(attr.identifier.name)
11317 args = [
11318 Argument("JSContext*", "cx"),
11319 Argument("JS::Handle<JSObject*>", "obj"),
11320 Argument("void*", "void_self"),
11321 Argument("JSJitSetterCallArgs", "args"),
11323 CGAbstractStaticMethod.__init__(
11324 self, descriptor, name, "bool", args, canRunScript=True
11327 def definition_body(self):
11328 nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, self.attr)
11329 type = self.attr.type
11330 call = CGSetterCall(type, nativeName, self.descriptor, self.attr).define()
11331 prefix = ""
11332 if self.attr.getExtendedAttribute("CrossOriginWritable"):
11333 if type.isGeckoInterface() and not type.unroll().inner.isExternal():
11334 # a setter taking a Gecko interface would require us to deal with remote
11335 # proxies for the value here.
11336 raise TypeError(
11337 "We don't support the setter of %s marked as "
11338 "CrossOriginWritable because it takes a Gecko interface "
11339 "as the value",
11340 attr.identifier.name,
11342 prototypeID, _ = PrototypeIDAndDepth(self.descriptor)
11343 prefix = fill(
11345 if (IsRemoteObjectProxy(obj, ${prototypeID})) {
11346 auto* self = static_cast<${nativeType}::RemoteProxy*>(void_self);
11347 $*{call}
11349 """,
11350 prototypeID=prototypeID,
11351 nativeType=self.descriptor.nativeType,
11352 call=call,
11354 return prefix + fill(
11356 auto* self = static_cast<${nativeType}*>(void_self);
11357 $*{call}
11358 """,
11359 nativeType=self.descriptor.nativeType,
11360 call=call,
11363 def auto_profiler_label(self):
11364 interface_name = self.descriptor.interface.identifier.name
11365 attr_name = self.attr.identifier.name
11366 return fill(
11368 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11369 "${interface_name}", "${attr_name}", DOM, cx,
11370 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER) |
11371 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11372 """,
11373 interface_name=interface_name,
11374 attr_name=attr_name,
11377 @staticmethod
11378 def error_reporting_label_helper(descriptor, attr):
11379 # Setters need a BindingCallContext if the type of the attribute needs
11380 # one.
11381 if not idlTypeNeedsCallContext(
11382 attr.type, descriptor, allowTreatNonCallableAsNull=True
11384 return None
11385 return (
11386 GetLabelForErrorReporting(descriptor, attr, isConstructor=False) + " setter"
11389 def error_reporting_label(self):
11390 return CGSpecializedSetter.error_reporting_label_helper(
11391 self.descriptor, self.attr
11394 @staticmethod
11395 def makeNativeName(descriptor, attr):
11396 name = attr.identifier.name
11397 return "Set" + MakeNativeName(descriptor.binaryNameFor(name))
11400 class CGStaticSetter(CGAbstractStaticBindingMethod):
11402 A class for generating the C++ code for an IDL static attribute setter.
11405 def __init__(self, descriptor, attr):
11406 self.attr = attr
11407 name = "set_" + IDLToCIdentifier(attr.identifier.name)
11408 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
11410 def generate_code(self):
11411 nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, self.attr)
11412 checkForArg = CGGeneric(
11413 fill(
11415 if (!args.requireAtLeast(cx, "${name} setter", 1)) {
11416 return false;
11418 """,
11419 name=self.attr.identifier.name,
11422 call = CGSetterCall(self.attr.type, nativeName, self.descriptor, self.attr)
11423 return CGList([checkForArg, call])
11425 def auto_profiler_label(self):
11426 interface_name = self.descriptor.interface.identifier.name
11427 attr_name = self.attr.identifier.name
11428 return fill(
11430 AUTO_PROFILER_LABEL_DYNAMIC_FAST(
11431 "${interface_name}", "${attr_name}", DOM, cx,
11432 uint32_t(js::ProfilingStackFrame::Flags::STRING_TEMPLATE_SETTER) |
11433 uint32_t(js::ProfilingStackFrame::Flags::RELEVANT_FOR_JS));
11434 """,
11435 interface_name=interface_name,
11436 attr_name=attr_name,
11439 def error_reporting_label(self):
11440 return CGSpecializedSetter.error_reporting_label_helper(
11441 self.descriptor, self.attr
11445 class CGSpecializedForwardingSetter(CGSpecializedSetter):
11447 A class for generating the code for a specialized attribute setter with
11448 PutForwards that the JIT can call with lower overhead.
11451 def __init__(self, descriptor, attr):
11452 CGSpecializedSetter.__init__(self, descriptor, attr)
11454 def definition_body(self):
11455 attrName = self.attr.identifier.name
11456 forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0]
11457 # JS_GetProperty and JS_SetProperty can only deal with ASCII
11458 assert all(ord(c) < 128 for c in attrName)
11459 assert all(ord(c) < 128 for c in forwardToAttrName)
11460 return fill(
11462 JS::Rooted<JS::Value> v(cx);
11463 if (!JS_GetProperty(cx, obj, "${attr}", &v)) {
11464 return false;
11467 if (!v.isObject()) {
11468 return cx.ThrowErrorMessage<MSG_NOT_OBJECT>("${interface}.${attr}");
11471 JS::Rooted<JSObject*> targetObj(cx, &v.toObject());
11472 return JS_SetProperty(cx, targetObj, "${forwardToAttrName}", args[0]);
11473 """,
11474 attr=attrName,
11475 interface=self.descriptor.interface.identifier.name,
11476 forwardToAttrName=forwardToAttrName,
11479 def error_reporting_label(self):
11480 # We always need to be able to throw.
11481 return (
11482 GetLabelForErrorReporting(self.descriptor, self.attr, isConstructor=False)
11483 + " setter"
11487 class CGSpecializedReplaceableSetter(CGSpecializedSetter):
11489 A class for generating the code for a specialized attribute setter with
11490 Replaceable that the JIT can call with lower overhead.
11493 def __init__(self, descriptor, attr):
11494 CGSpecializedSetter.__init__(self, descriptor, attr)
11496 def definition_body(self):
11497 attrName = self.attr.identifier.name
11498 # JS_DefineProperty can only deal with ASCII
11499 assert all(ord(c) < 128 for c in attrName)
11500 return (
11501 'return JS_DefineProperty(cx, obj, "%s", args[0], JSPROP_ENUMERATE);\n'
11502 % attrName
11505 def error_reporting_label(self):
11506 # We never throw directly.
11507 return None
11510 class CGSpecializedLenientSetter(CGSpecializedSetter):
11512 A class for generating the code for a specialized attribute setter with
11513 LenientSetter that the JIT can call with lower overhead.
11516 def __init__(self, descriptor, attr):
11517 CGSpecializedSetter.__init__(self, descriptor, attr)
11519 def definition_body(self):
11520 attrName = self.attr.identifier.name
11521 # JS_DefineProperty can only deal with ASCII
11522 assert all(ord(c) < 128 for c in attrName)
11523 return dedent(
11525 DeprecationWarning(cx, obj, DeprecatedOperations::eLenientSetter);
11526 return true;
11530 def error_reporting_label(self):
11531 # We never throw; that's the whole point.
11532 return None
11535 def memberReturnsNewObject(member):
11536 return member.getExtendedAttribute("NewObject") is not None
11539 class CGMemberJITInfo(CGThing):
11541 A class for generating the JITInfo for a property that points to
11542 our specialized getter and setter.
11545 def __init__(self, descriptor, member):
11546 self.member = member
11547 self.descriptor = descriptor
11549 def declare(self):
11550 return ""
11552 def defineJitInfo(
11553 self,
11554 infoName,
11555 opName,
11556 opType,
11557 infallible,
11558 movable,
11559 eliminatable,
11560 aliasSet,
11561 alwaysInSlot,
11562 lazilyInSlot,
11563 slotIndex,
11564 returnTypes,
11565 args,
11568 aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit.
11570 args is None if we don't want to output argTypes for some
11571 reason (e.g. we have overloads or we're not a method) and
11572 otherwise an iterable of the arguments for this method.
11574 assert (
11575 not movable or aliasSet != "AliasEverything"
11576 ) # Can't move write-aliasing things
11577 assert (
11578 not alwaysInSlot or movable
11579 ) # Things always in slots had better be movable
11580 assert (
11581 not eliminatable or aliasSet != "AliasEverything"
11582 ) # Can't eliminate write-aliasing things
11583 assert (
11584 not alwaysInSlot or eliminatable
11585 ) # Things always in slots had better be eliminatable
11587 def jitInfoInitializer(isTypedMethod):
11588 initializer = fill(
11591 { ${opName} },
11592 { prototypes::id::${name} },
11593 { PrototypeTraits<prototypes::id::${name}>::Depth },
11594 JSJitInfo::${opType},
11595 JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */
11596 ${returnType}, /* returnType. Not relevant for setters. */
11597 ${isInfallible}, /* isInfallible. False in setters. */
11598 ${isMovable}, /* isMovable. Not relevant for setters. */
11599 ${isEliminatable}, /* isEliminatable. Not relevant for setters. */
11600 ${isAlwaysInSlot}, /* isAlwaysInSlot. Only relevant for getters. */
11601 ${isLazilyCachedInSlot}, /* isLazilyCachedInSlot. Only relevant for getters. */
11602 ${isTypedMethod}, /* isTypedMethod. Only relevant for methods. */
11603 ${slotIndex} /* Reserved slot index, if we're stored in a slot, else 0. */
11605 """,
11606 opName=opName,
11607 name=self.descriptor.name,
11608 opType=opType,
11609 aliasSet=aliasSet,
11610 returnType=functools.reduce(
11611 CGMemberJITInfo.getSingleReturnType, returnTypes, ""
11613 isInfallible=toStringBool(infallible),
11614 isMovable=toStringBool(movable),
11615 isEliminatable=toStringBool(eliminatable),
11616 isAlwaysInSlot=toStringBool(alwaysInSlot),
11617 isLazilyCachedInSlot=toStringBool(lazilyInSlot),
11618 isTypedMethod=toStringBool(isTypedMethod),
11619 slotIndex=slotIndex,
11621 return initializer.rstrip()
11623 slotAssert = fill(
11625 static_assert(${slotIndex} <= JSJitInfo::maxSlotIndex, "We won't fit");
11626 static_assert(${slotIndex} < ${classReservedSlots}, "There is no slot for us");
11627 """,
11628 slotIndex=slotIndex,
11629 classReservedSlots=INSTANCE_RESERVED_SLOTS
11630 + self.descriptor.interface.totalMembersInSlots,
11632 if args is not None:
11633 argTypes = "%s_argTypes" % infoName
11634 args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args]
11635 args.append("JSJitInfo::ArgTypeListEnd")
11636 argTypesDecl = "static const JSJitInfo::ArgType %s[] = { %s };\n" % (
11637 argTypes,
11638 ", ".join(args),
11640 return fill(
11642 $*{argTypesDecl}
11643 static const JSTypedMethodJitInfo ${infoName} = {
11644 ${jitInfo},
11645 ${argTypes}
11647 $*{slotAssert}
11648 """,
11649 argTypesDecl=argTypesDecl,
11650 infoName=infoName,
11651 jitInfo=indent(jitInfoInitializer(True)),
11652 argTypes=argTypes,
11653 slotAssert=slotAssert,
11656 # Unexposed things are meant to be used from C++ directly, so we make
11657 # their jitinfo non-static. That way C++ can get at it.
11658 if self.member.getExtendedAttribute("Unexposed"):
11659 storageClass = "extern"
11660 else:
11661 storageClass = "static"
11663 return fill(
11665 ${storageClass} const JSJitInfo ${infoName} = ${jitInfo};
11666 $*{slotAssert}
11667 """,
11668 storageClass=storageClass,
11669 infoName=infoName,
11670 jitInfo=jitInfoInitializer(False),
11671 slotAssert=slotAssert,
11674 def define(self):
11675 if self.member.isAttr():
11676 getterinfo = "%s_getterinfo" % IDLToCIdentifier(self.member.identifier.name)
11677 name = IDLToCIdentifier(self.member.identifier.name)
11678 if self.member.type.isPromise():
11679 name = CGGetterPromiseWrapper.makeName(name)
11680 getter = "get_%s" % name
11681 extendedAttrs = self.descriptor.getExtendedAttributes(
11682 self.member, getter=True
11684 getterinfal = "needsErrorResult" not in extendedAttrs
11686 # At this point getterinfal is true if our getter either can't throw
11687 # at all, or can only throw OOM. In both cases, it's safe to move,
11688 # or dead-code-eliminate, the getter, because throwing OOM is not
11689 # semantically meaningful, so code can't rely on it happening. Note
11690 # that this makes the behavior consistent for OOM thrown from the
11691 # getter itself and OOM thrown from the to-JS conversion of the
11692 # return value (see the "canOOM" and "infallibleForMember" checks
11693 # below).
11694 movable = self.mayBeMovable() and getterinfal
11695 eliminatable = self.mayBeEliminatable() and getterinfal
11696 aliasSet = self.aliasSet()
11698 # Now we have to set getterinfal to whether we can _really_ ever
11699 # throw, from the point of view of the JS engine.
11700 getterinfal = (
11701 getterinfal
11702 and "canOOM" not in extendedAttrs
11703 and infallibleForMember(self.member, self.member.type, self.descriptor)
11705 isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot")
11706 if self.member.slotIndices is not None:
11707 assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached")
11708 isLazilyCachedInSlot = not isAlwaysInSlot
11709 slotIndex = memberReservedSlot(self.member, self.descriptor)
11710 # We'll statically assert that this is not too big in
11711 # CGUpdateMemberSlotsMethod, in the case when
11712 # isAlwaysInSlot is true.
11713 else:
11714 isLazilyCachedInSlot = False
11715 slotIndex = "0"
11717 result = self.defineJitInfo(
11718 getterinfo,
11719 getter,
11720 "Getter",
11721 getterinfal,
11722 movable,
11723 eliminatable,
11724 aliasSet,
11725 isAlwaysInSlot,
11726 isLazilyCachedInSlot,
11727 slotIndex,
11728 [self.member.type],
11729 None,
11731 if (
11732 not self.member.readonly
11733 or self.member.getExtendedAttribute("PutForwards") is not None
11734 or self.member.getExtendedAttribute("Replaceable") is not None
11735 or self.member.getExtendedAttribute("LegacyLenientSetter") is not None
11737 setterinfo = "%s_setterinfo" % IDLToCIdentifier(
11738 self.member.identifier.name
11740 # Actually a JSJitSetterOp, but JSJitGetterOp is first in the
11741 # union.
11742 setter = "(JSJitGetterOp)set_%s" % IDLToCIdentifier(
11743 self.member.identifier.name
11745 # Setters are always fallible, since they have to do a typed unwrap.
11746 result += self.defineJitInfo(
11747 setterinfo,
11748 setter,
11749 "Setter",
11750 False,
11751 False,
11752 False,
11753 "AliasEverything",
11754 False,
11755 False,
11756 "0",
11757 [BuiltinTypes[IDLBuiltinType.Types.void]],
11758 None,
11760 return result
11761 if self.member.isMethod():
11762 methodinfo = "%s_methodinfo" % IDLToCIdentifier(self.member.identifier.name)
11763 name = CppKeywords.checkMethodName(
11764 IDLToCIdentifier(self.member.identifier.name)
11766 if self.member.returnsPromise():
11767 name = CGMethodPromiseWrapper.makeName(name)
11768 # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union.
11769 method = "(JSJitGetterOp)%s" % name
11771 # Methods are infallible if they are infallible, have no arguments
11772 # to unwrap, and have a return type that's infallible to wrap up for
11773 # return.
11774 sigs = self.member.signatures()
11775 if len(sigs) != 1:
11776 # Don't handle overloading. If there's more than one signature,
11777 # one of them must take arguments.
11778 methodInfal = False
11779 args = None
11780 movable = False
11781 eliminatable = False
11782 else:
11783 sig = sigs[0]
11784 # For methods that affect nothing, it's OK to set movable to our
11785 # notion of infallible on the C++ side, without considering
11786 # argument conversions, since argument conversions that can
11787 # reliably throw would be effectful anyway and the jit doesn't
11788 # move effectful things.
11789 extendedAttrs = self.descriptor.getExtendedAttributes(self.member)
11790 hasInfallibleImpl = "needsErrorResult" not in extendedAttrs
11791 # At this point hasInfallibleImpl is true if our method either
11792 # can't throw at all, or can only throw OOM. In both cases, it
11793 # may be safe to move, or dead-code-eliminate, the method,
11794 # because throwing OOM is not semantically meaningful, so code
11795 # can't rely on it happening. Note that this makes the behavior
11796 # consistent for OOM thrown from the method itself and OOM
11797 # thrown from the to-JS conversion of the return value (see the
11798 # "canOOM" and "infallibleForMember" checks below).
11799 movable = self.mayBeMovable() and hasInfallibleImpl
11800 eliminatable = self.mayBeEliminatable() and hasInfallibleImpl
11801 # XXXbz can we move the smarts about fallibility due to arg
11802 # conversions into the JIT, using our new args stuff?
11803 if len(sig[1]) != 0 or not infallibleForMember(
11804 self.member, sig[0], self.descriptor
11806 # We have arguments or our return-value boxing can fail
11807 methodInfal = False
11808 else:
11809 methodInfal = hasInfallibleImpl and "canOOM" not in extendedAttrs
11810 # For now, only bother to output args if we're side-effect-free.
11811 if self.member.affects == "Nothing":
11812 args = sig[1]
11813 else:
11814 args = None
11816 aliasSet = self.aliasSet()
11817 result = self.defineJitInfo(
11818 methodinfo,
11819 method,
11820 "Method",
11821 methodInfal,
11822 movable,
11823 eliminatable,
11824 aliasSet,
11825 False,
11826 False,
11827 "0",
11828 [s[0] for s in sigs],
11829 args,
11831 return result
11832 raise TypeError("Illegal member type to CGPropertyJITInfo")
11834 def mayBeMovable(self):
11836 Returns whether this attribute or method may be movable, just
11837 based on Affects/DependsOn annotations.
11839 affects = self.member.affects
11840 dependsOn = self.member.dependsOn
11841 assert affects in IDLInterfaceMember.AffectsValues
11842 assert dependsOn in IDLInterfaceMember.DependsOnValues
11843 # Things that are DependsOn=DeviceState are not movable, because we
11844 # don't want them coalesced with each other or loop-hoisted, since
11845 # their return value can change even if nothing is going on from our
11846 # point of view.
11847 return affects == "Nothing" and (
11848 dependsOn != "Everything" and dependsOn != "DeviceState"
11851 def mayBeEliminatable(self):
11853 Returns whether this attribute or method may be eliminatable, just
11854 based on Affects/DependsOn annotations.
11856 # dependsOn shouldn't affect this decision at all, except in jitinfo we
11857 # have no way to express "Depends on everything, affects nothing",
11858 # because we only have three alias set values: AliasNone ("depends on
11859 # nothing, affects nothing"), AliasDOMSets ("depends on DOM sets,
11860 # affects nothing"), AliasEverything ("depends on everything, affects
11861 # everything"). So the [Affects=Nothing, DependsOn=Everything] case
11862 # gets encoded as AliasEverything and defineJitInfo asserts that if our
11863 # alias state is AliasEverything then we're not eliminatable (because it
11864 # thinks we might have side-effects at that point). Bug 1155796 is
11865 # tracking possible solutions for this.
11866 affects = self.member.affects
11867 dependsOn = self.member.dependsOn
11868 assert affects in IDLInterfaceMember.AffectsValues
11869 assert dependsOn in IDLInterfaceMember.DependsOnValues
11870 return affects == "Nothing" and dependsOn != "Everything"
11872 def aliasSet(self):
11874 Returns the alias set to store in the jitinfo. This may not be the
11875 effective alias set the JIT uses, depending on whether we have enough
11876 information about our args to allow the JIT to prove that effectful
11877 argument conversions won't happen.
11879 dependsOn = self.member.dependsOn
11880 assert dependsOn in IDLInterfaceMember.DependsOnValues
11882 if dependsOn == "Nothing" or dependsOn == "DeviceState":
11883 assert self.member.affects == "Nothing"
11884 return "AliasNone"
11886 if dependsOn == "DOMState":
11887 assert self.member.affects == "Nothing"
11888 return "AliasDOMSets"
11890 return "AliasEverything"
11892 @staticmethod
11893 def getJSReturnTypeTag(t):
11894 if t.nullable():
11895 # Sometimes it might return null, sometimes not
11896 return "JSVAL_TYPE_UNKNOWN"
11897 if t.isVoid():
11898 # No return, every time
11899 return "JSVAL_TYPE_UNDEFINED"
11900 if t.isSequence():
11901 return "JSVAL_TYPE_OBJECT"
11902 if t.isRecord():
11903 return "JSVAL_TYPE_OBJECT"
11904 if t.isPromise():
11905 return "JSVAL_TYPE_OBJECT"
11906 if t.isGeckoInterface():
11907 return "JSVAL_TYPE_OBJECT"
11908 if t.isString():
11909 return "JSVAL_TYPE_STRING"
11910 if t.isEnum():
11911 return "JSVAL_TYPE_STRING"
11912 if t.isCallback():
11913 return "JSVAL_TYPE_OBJECT"
11914 if t.isAny():
11915 # The whole point is to return various stuff
11916 return "JSVAL_TYPE_UNKNOWN"
11917 if t.isObject():
11918 return "JSVAL_TYPE_OBJECT"
11919 if t.isSpiderMonkeyInterface():
11920 return "JSVAL_TYPE_OBJECT"
11921 if t.isUnion():
11922 u = t.unroll()
11923 if u.hasNullableType:
11924 # Might be null or not
11925 return "JSVAL_TYPE_UNKNOWN"
11926 return functools.reduce(
11927 CGMemberJITInfo.getSingleReturnType, u.flatMemberTypes, ""
11929 if t.isDictionary():
11930 return "JSVAL_TYPE_OBJECT"
11931 if not t.isPrimitive():
11932 raise TypeError("No idea what type " + str(t) + " is.")
11933 tag = t.tag()
11934 if tag == IDLType.Tags.bool:
11935 return "JSVAL_TYPE_BOOLEAN"
11936 if tag in [
11937 IDLType.Tags.int8,
11938 IDLType.Tags.uint8,
11939 IDLType.Tags.int16,
11940 IDLType.Tags.uint16,
11941 IDLType.Tags.int32,
11943 return "JSVAL_TYPE_INT32"
11944 if tag in [
11945 IDLType.Tags.int64,
11946 IDLType.Tags.uint64,
11947 IDLType.Tags.unrestricted_float,
11948 IDLType.Tags.float,
11949 IDLType.Tags.unrestricted_double,
11950 IDLType.Tags.double,
11952 # These all use JS_NumberValue, which can return int or double.
11953 # But TI treats "double" as meaning "int or double", so we're
11954 # good to return JSVAL_TYPE_DOUBLE here.
11955 return "JSVAL_TYPE_DOUBLE"
11956 if tag != IDLType.Tags.uint32:
11957 raise TypeError("No idea what type " + str(t) + " is.")
11958 # uint32 is sometimes int and sometimes double.
11959 return "JSVAL_TYPE_DOUBLE"
11961 @staticmethod
11962 def getSingleReturnType(existingType, t):
11963 type = CGMemberJITInfo.getJSReturnTypeTag(t)
11964 if existingType == "":
11965 # First element of the list; just return its type
11966 return type
11968 if type == existingType:
11969 return existingType
11970 if (type == "JSVAL_TYPE_DOUBLE" and existingType == "JSVAL_TYPE_INT32") or (
11971 existingType == "JSVAL_TYPE_DOUBLE" and type == "JSVAL_TYPE_INT32"
11973 # Promote INT32 to DOUBLE as needed
11974 return "JSVAL_TYPE_DOUBLE"
11975 # Different types
11976 return "JSVAL_TYPE_UNKNOWN"
11978 @staticmethod
11979 def getJSArgType(t):
11980 assert not t.isVoid()
11981 if t.nullable():
11982 # Sometimes it might return null, sometimes not
11983 return (
11984 "JSJitInfo::ArgType(JSJitInfo::Null | %s)"
11985 % CGMemberJITInfo.getJSArgType(t.inner)
11987 if t.isSequence():
11988 return "JSJitInfo::Object"
11989 if t.isPromise():
11990 return "JSJitInfo::Object"
11991 if t.isGeckoInterface():
11992 return "JSJitInfo::Object"
11993 if t.isString():
11994 return "JSJitInfo::String"
11995 if t.isEnum():
11996 return "JSJitInfo::String"
11997 if t.isCallback():
11998 return "JSJitInfo::Object"
11999 if t.isAny():
12000 # The whole point is to return various stuff
12001 return "JSJitInfo::Any"
12002 if t.isObject():
12003 return "JSJitInfo::Object"
12004 if t.isSpiderMonkeyInterface():
12005 return "JSJitInfo::Object"
12006 if t.isUnion():
12007 u = t.unroll()
12008 type = "JSJitInfo::Null" if u.hasNullableType else ""
12009 return "JSJitInfo::ArgType(%s)" % functools.reduce(
12010 CGMemberJITInfo.getSingleArgType, u.flatMemberTypes, type
12012 if t.isDictionary():
12013 return "JSJitInfo::Object"
12014 if not t.isPrimitive():
12015 raise TypeError("No idea what type " + str(t) + " is.")
12016 tag = t.tag()
12017 if tag == IDLType.Tags.bool:
12018 return "JSJitInfo::Boolean"
12019 if tag in [
12020 IDLType.Tags.int8,
12021 IDLType.Tags.uint8,
12022 IDLType.Tags.int16,
12023 IDLType.Tags.uint16,
12024 IDLType.Tags.int32,
12026 return "JSJitInfo::Integer"
12027 if tag in [
12028 IDLType.Tags.int64,
12029 IDLType.Tags.uint64,
12030 IDLType.Tags.unrestricted_float,
12031 IDLType.Tags.float,
12032 IDLType.Tags.unrestricted_double,
12033 IDLType.Tags.double,
12035 # These all use JS_NumberValue, which can return int or double.
12036 # But TI treats "double" as meaning "int or double", so we're
12037 # good to return JSVAL_TYPE_DOUBLE here.
12038 return "JSJitInfo::Double"
12039 if tag != IDLType.Tags.uint32:
12040 raise TypeError("No idea what type " + str(t) + " is.")
12041 # uint32 is sometimes int and sometimes double.
12042 return "JSJitInfo::Double"
12044 @staticmethod
12045 def getSingleArgType(existingType, t):
12046 type = CGMemberJITInfo.getJSArgType(t)
12047 if existingType == "":
12048 # First element of the list; just return its type
12049 return type
12051 if type == existingType:
12052 return existingType
12053 return "%s | %s" % (existingType, type)
12056 class CGStaticMethodJitinfo(CGGeneric):
12058 A class for generating the JITInfo for a promise-returning static method.
12061 def __init__(self, method):
12062 CGGeneric.__init__(
12063 self,
12064 "\n"
12065 "static const JSJitInfo %s_methodinfo = {\n"
12066 " { (JSJitGetterOp)%s },\n"
12067 " { prototypes::id::_ID_Count }, { 0 }, JSJitInfo::StaticMethod,\n"
12068 " JSJitInfo::AliasEverything, JSVAL_TYPE_OBJECT, false, false,\n"
12069 " false, false, 0\n"
12070 "};\n"
12072 IDLToCIdentifier(method.identifier.name),
12073 CppKeywords.checkMethodName(IDLToCIdentifier(method.identifier.name)),
12078 def getEnumValueName(value):
12079 # Some enum values can be empty strings. Others might have weird
12080 # characters in them. Deal with the former by returning "_empty",
12081 # deal with possible name collisions from that by throwing if the
12082 # enum value is actually "_empty", and throw on any value
12083 # containing non-ASCII chars for now. Replace all chars other than
12084 # [0-9A-Za-z_] with '_'.
12085 if re.match("[^\x20-\x7E]", value):
12086 raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters')
12087 if re.match("^[0-9]", value):
12088 value = "_" + value
12089 value = re.sub(r"[^0-9A-Za-z_]", "_", value)
12090 if re.match("^_[A-Z]|__", value):
12091 raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec')
12092 if value == "_empty":
12093 raise SyntaxError('"_empty" is not an IDL enum value we support yet')
12094 if value == "":
12095 return "_empty"
12096 nativeName = MakeNativeName(value)
12097 if nativeName == "EndGuard_":
12098 raise SyntaxError(
12099 'Enum value "' + value + '" cannot be used because it'
12100 " collides with our internal EndGuard_ value. Please"
12101 " rename our internal EndGuard_ to something else"
12103 return nativeName
12106 class CGEnumToJSValue(CGAbstractMethod):
12107 def __init__(self, enum):
12108 enumType = enum.identifier.name
12109 self.stringsArray = enumType + "Values::" + ENUM_ENTRY_VARIABLE_NAME
12110 CGAbstractMethod.__init__(
12111 self,
12112 None,
12113 "ToJSValue",
12114 "bool",
12116 Argument("JSContext*", "aCx"),
12117 Argument(enumType, "aArgument"),
12118 Argument("JS::MutableHandle<JS::Value>", "aValue"),
12122 def definition_body(self):
12123 return fill(
12125 MOZ_ASSERT(uint32_t(aArgument) < ArrayLength(${strings}));
12126 JSString* resultStr =
12127 JS_NewStringCopyN(aCx, ${strings}[uint32_t(aArgument)].value,
12128 ${strings}[uint32_t(aArgument)].length);
12129 if (!resultStr) {
12130 return false;
12132 aValue.setString(resultStr);
12133 return true;
12134 """,
12135 strings=self.stringsArray,
12139 class CGEnum(CGThing):
12140 def __init__(self, enum):
12141 CGThing.__init__(self)
12142 self.enum = enum
12143 entryDecl = fill(
12145 extern const EnumEntry ${entry_array}[${entry_count}];
12147 static constexpr size_t Count = ${real_entry_count};
12149 // Our "${entry_array}" contains an extra entry with a null string.
12150 static_assert(mozilla::ArrayLength(${entry_array}) - 1 == Count,
12151 "Mismatch between enum strings and enum count");
12153 static_assert(static_cast<size_t>(${name}::EndGuard_) == Count,
12154 "Mismatch between enum value and enum count");
12156 inline auto GetString(${name} stringId) {
12157 MOZ_ASSERT(static_cast<${type}>(stringId) < Count);
12158 const EnumEntry& entry = ${entry_array}[static_cast<${type}>(stringId)];
12159 return Span<const char>{entry.value, entry.length};
12161 """,
12162 entry_array=ENUM_ENTRY_VARIABLE_NAME,
12163 entry_count=self.nEnumStrings(),
12164 # -1 because nEnumStrings() includes a string for EndGuard_
12165 real_entry_count=self.nEnumStrings() - 1,
12166 name=self.enum.identifier.name,
12167 type=self.underlyingType(),
12169 strings = CGNamespace(
12170 self.stringsNamespace(),
12171 CGGeneric(
12172 declare=entryDecl,
12173 define=fill(
12175 extern const EnumEntry ${name}[${count}] = {
12176 $*{entries}
12177 { nullptr, 0 }
12179 """,
12180 name=ENUM_ENTRY_VARIABLE_NAME,
12181 count=self.nEnumStrings(),
12182 entries="".join(
12183 '{"%s", %d},\n' % (val, len(val)) for val in self.enum.values()
12188 toJSValue = CGEnumToJSValue(enum)
12189 self.cgThings = CGList([strings, toJSValue], "\n")
12191 def stringsNamespace(self):
12192 return self.enum.identifier.name + "Values"
12194 def nEnumStrings(self):
12195 return len(self.enum.values()) + 1
12197 def underlyingType(self):
12198 count = self.nEnumStrings()
12199 if count <= 256:
12200 return "uint8_t"
12201 if count <= 65536:
12202 return "uint16_t"
12203 raise ValueError(
12204 "Enum " + self.enum.identifier.name + " has more than 65536 values"
12207 def declare(self):
12208 decl = fill(
12210 enum class ${name} : ${ty} {
12211 $*{enums}
12212 EndGuard_
12214 """,
12215 name=self.enum.identifier.name,
12216 ty=self.underlyingType(),
12217 enums=",\n".join(map(getEnumValueName, self.enum.values())) + ",\n",
12220 return decl + "\n" + self.cgThings.declare()
12222 def define(self):
12223 return self.cgThings.define()
12225 def deps(self):
12226 return self.enum.getDeps()
12229 def getUnionAccessorSignatureType(type, descriptorProvider):
12231 Returns the types that are used in the getter and setter signatures for
12232 union types
12234 # Flat member types have already unwrapped nullables.
12235 assert not type.nullable()
12237 # Promise types can never appear in unions, because Promise is not
12238 # distinguishable from anything.
12239 assert not type.isPromise()
12241 if type.isSequence() or type.isRecord():
12242 if type.isSequence():
12243 wrapperType = "Sequence"
12244 else:
12245 wrapperType = "Record"
12246 # We don't use the returned template here, so it's OK to just pass no
12247 # sourceDescription.
12248 elementInfo = getJSToNativeConversionInfo(
12249 type.inner, descriptorProvider, isMember=wrapperType
12251 if wrapperType == "Sequence":
12252 innerType = elementInfo.declType
12253 else:
12254 innerType = [recordKeyDeclType(type), elementInfo.declType]
12256 return CGTemplatedType(wrapperType, innerType, isConst=True, isReference=True)
12258 # Nested unions are unwrapped automatically into our flatMemberTypes.
12259 assert not type.isUnion()
12261 if type.isGeckoInterface():
12262 descriptor = descriptorProvider.getDescriptor(
12263 type.unroll().inner.identifier.name
12265 typeName = CGGeneric(descriptor.nativeType)
12266 if not type.unroll().inner.isExternal():
12267 typeName = CGWrapper(typeName, post="&")
12268 elif descriptor.interface.identifier.name == "WindowProxy":
12269 typeName = CGGeneric("WindowProxyHolder const&")
12270 else:
12271 # Allow null pointers for old-binding classes.
12272 typeName = CGWrapper(typeName, post="*")
12273 return typeName
12275 if type.isSpiderMonkeyInterface():
12276 typeName = CGGeneric(type.name)
12277 return CGWrapper(typeName, post=" const &")
12279 if type.isJSString():
12280 raise TypeError("JSString not supported in unions")
12282 if type.isDOMString() or type.isUSVString():
12283 return CGGeneric("const nsAString&")
12285 if type.isUTF8String():
12286 return CGGeneric("const nsACString&")
12288 if type.isByteString():
12289 return CGGeneric("const nsCString&")
12291 if type.isEnum():
12292 return CGGeneric(type.inner.identifier.name)
12294 if type.isCallback():
12295 return CGGeneric("%s&" % type.unroll().callback.identifier.name)
12297 if type.isAny():
12298 return CGGeneric("JS::Value")
12300 if type.isObject():
12301 return CGGeneric("JSObject*")
12303 if type.isDictionary():
12304 return CGGeneric("const %s&" % type.inner.identifier.name)
12306 if not type.isPrimitive():
12307 raise TypeError("Need native type for argument type '%s'" % str(type))
12309 return CGGeneric(builtinNames[type.tag()])
12312 def getUnionTypeTemplateVars(unionType, type, descriptorProvider, ownsMembers=False):
12313 name = getUnionMemberName(type)
12314 holderName = "m" + name + "Holder"
12316 # By the time tryNextCode is invoked, we're guaranteed the union has been
12317 # constructed as some type, since we've been trying to convert into the
12318 # corresponding member.
12319 prefix = "" if ownsMembers else "mUnion."
12320 tryNextCode = (
12321 "$*{destroyHolder}\n"
12322 "%sDestroy%s();\n"
12323 "tryNext = true;\n"
12324 "return true;\n" % (prefix, name)
12327 sourceDescription = "%s branch of %s" % (type.prettyName(), unionType.prettyName())
12329 conversionInfo = getJSToNativeConversionInfo(
12330 type,
12331 descriptorProvider,
12332 failureCode=tryNextCode,
12333 isDefinitelyObject=not type.isDictionary(),
12334 isMember=("OwningUnion" if ownsMembers else None),
12335 sourceDescription=sourceDescription,
12338 if conversionInfo.holderType is not None:
12339 assert not ownsMembers
12340 destroyHolder = "%s.reset();\n" % holderName
12341 else:
12342 destroyHolder = ""
12344 ctorNeedsCx = conversionInfo.declArgs == "cx"
12345 ctorArgs = "cx" if ctorNeedsCx else ""
12347 structType = conversionInfo.declType.define()
12348 externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
12350 if type.isObject():
12351 if ownsMembers:
12352 body = dedent(
12354 MOZ_ASSERT(mType == eUninitialized);
12355 mValue.mObject.SetValue(obj);
12356 mType = eObject;
12359 else:
12360 body = dedent(
12362 MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);
12363 mUnion.mValue.mObject.SetValue(cx, obj);
12364 mUnion.mType = mUnion.eObject;
12368 # It's a bit sketchy to do the security check after setting the value,
12369 # but it keeps the code cleaner and lets us avoid rooting |obj| over the
12370 # call to CallerSubsumes().
12371 body = body + fill(
12373 if (passedToJSImpl && !CallerSubsumes(obj)) {
12374 cx.ThrowErrorMessage<MSG_PERMISSION_DENIED_TO_PASS_ARG>("${sourceDescription}");
12375 return false;
12377 return true;
12378 """,
12379 sourceDescription=sourceDescription,
12382 setters = [
12383 ClassMethod(
12384 "SetToObject",
12385 "bool",
12387 Argument("BindingCallContext&", "cx"),
12388 Argument("JSObject*", "obj"),
12389 Argument("bool", "passedToJSImpl", default="false"),
12391 inline=True,
12392 bodyInHeader=True,
12393 body=body,
12396 elif type.isDictionary() and not type.inner.needsConversionFromJS:
12397 # In this case we are never initialized from JS to start with
12398 setters = None
12399 else:
12400 # Important: we need to not have our declName involve
12401 # maybe-GCing operations.
12402 if conversionInfo.holderType is not None:
12403 holderArgs = conversionInfo.holderArgs
12404 if holderArgs is None:
12405 holderArgs = ""
12406 initHolder = "%s.emplace(%s);\n" % (holderName, holderArgs)
12407 else:
12408 initHolder = ""
12410 jsConversion = fill(
12411 initHolder + conversionInfo.template,
12412 val="value",
12413 maybeMutableVal="value",
12414 declName="memberSlot",
12415 holderName=(holderName if ownsMembers else "%s.ref()" % holderName),
12416 destroyHolder=destroyHolder,
12417 passedToJSImpl="passedToJSImpl",
12420 jsConversion = fill(
12422 tryNext = false;
12423 { // scope for memberSlot
12424 ${structType}& memberSlot = RawSetAs${name}(${ctorArgs});
12425 $*{jsConversion}
12427 return true;
12428 """,
12429 structType=structType,
12430 name=name,
12431 ctorArgs=ctorArgs,
12432 jsConversion=jsConversion,
12435 if ownsMembers:
12436 handleType = "JS::Handle<JS::Value>"
12437 else:
12438 handleType = "JS::MutableHandle<JS::Value>"
12440 needCallContext = idlTypeNeedsCallContext(type)
12441 if needCallContext:
12442 cxType = "BindingCallContext&"
12443 else:
12444 cxType = "JSContext*"
12445 setters = [
12446 ClassMethod(
12447 "TrySetTo" + name,
12448 "bool",
12450 Argument(cxType, "cx"),
12451 Argument(handleType, "value"),
12452 Argument("bool&", "tryNext"),
12453 Argument("bool", "passedToJSImpl", default="false"),
12455 inline=not ownsMembers,
12456 bodyInHeader=not ownsMembers,
12457 body=jsConversion,
12460 if needCallContext:
12461 # Add a method for non-binding uses of unions to allow them to set
12462 # things in the union without providing a call context (though if
12463 # they want good error reporting they'll provide one anyway).
12464 shimBody = fill(
12466 BindingCallContext cx(cx_, nullptr);
12467 return TrySetTo${name}(cx, value, tryNext, passedToJSImpl);
12468 """,
12469 name=name,
12471 setters.append(
12472 ClassMethod(
12473 "TrySetTo" + name,
12474 "bool",
12476 Argument("JSContext*", "cx_"),
12477 Argument(handleType, "value"),
12478 Argument("bool&", "tryNext"),
12479 Argument("bool", "passedToJSImpl", default="false"),
12481 inline=not ownsMembers,
12482 bodyInHeader=not ownsMembers,
12483 body=shimBody,
12487 return {
12488 "name": name,
12489 "structType": structType,
12490 "externalType": externalType,
12491 "setters": setters,
12492 "holderType": conversionInfo.holderType.define()
12493 if conversionInfo.holderType
12494 else None,
12495 "ctorArgs": ctorArgs,
12496 "ctorArgList": [Argument("JSContext*", "cx")] if ctorNeedsCx else [],
12500 class CGUnionStruct(CGThing):
12501 def __init__(self, type, descriptorProvider, ownsMembers=False):
12502 CGThing.__init__(self)
12503 self.type = type.unroll()
12504 self.descriptorProvider = descriptorProvider
12505 self.ownsMembers = ownsMembers
12506 self.struct = self.getStruct()
12508 def declare(self):
12509 return self.struct.declare()
12511 def define(self):
12512 return self.struct.define()
12514 def deps(self):
12515 return self.type.getDeps()
12517 def getStruct(self):
12519 members = [
12520 ClassMember("mType", "Type", body="eUninitialized"),
12521 ClassMember("mValue", "Value"),
12523 ctor = ClassConstructor(
12524 [], bodyInHeader=True, visibility="public", explicit=True
12527 methods = []
12528 enumValues = ["eUninitialized"]
12529 toJSValCases = [CGCase("eUninitialized", CGGeneric("return false;\n"))]
12530 destructorCases = [CGCase("eUninitialized", None)]
12531 assignmentCases = [
12532 CGCase(
12533 "eUninitialized",
12534 CGGeneric(
12535 "MOZ_ASSERT(mType == eUninitialized,\n"
12536 ' "We need to destroy ourselves?");\n'
12540 traceCases = []
12541 unionValues = []
12542 if self.type.hasNullableType:
12543 enumValues.append("eNull")
12544 methods.append(
12545 ClassMethod(
12546 "IsNull",
12547 "bool",
12549 const=True,
12550 inline=True,
12551 body="return mType == eNull;\n",
12552 bodyInHeader=True,
12555 methods.append(
12556 ClassMethod(
12557 "SetNull",
12558 "void",
12560 inline=True,
12561 body=("Uninit();\n" "mType = eNull;\n"),
12562 bodyInHeader=True,
12565 destructorCases.append(CGCase("eNull", None))
12566 assignmentCases.append(
12567 CGCase(
12568 "eNull",
12569 CGGeneric(
12570 "MOZ_ASSERT(mType == eUninitialized);\n" "mType = eNull;\n"
12574 toJSValCases.append(
12575 CGCase("eNull", CGGeneric("rval.setNull();\n" "return true;\n"))
12578 hasObjectType = any(t.isObject() for t in self.type.flatMemberTypes)
12579 skipToJSVal = False
12580 for t in self.type.flatMemberTypes:
12581 vars = getUnionTypeTemplateVars(
12582 self.type, t, self.descriptorProvider, ownsMembers=self.ownsMembers
12584 if vars["name"] != "Object" or self.ownsMembers:
12585 body = fill(
12587 if (mType == e${name}) {
12588 return mValue.m${name}.Value();
12591 mType = e${name};
12592 return mValue.m${name}.SetValue(${ctorArgs});
12593 """,
12594 **vars
12597 # bodyInHeader must be false for return values because they own
12598 # their union members and we don't want include headers in
12599 # UnionTypes.h just to call Addref/Release
12600 methods.append(
12601 ClassMethod(
12602 "RawSetAs" + vars["name"],
12603 vars["structType"] + "&",
12604 vars["ctorArgList"],
12605 bodyInHeader=not self.ownsMembers,
12606 body=body % "MOZ_ASSERT(mType == eUninitialized);",
12609 uninit = "Uninit();"
12610 if hasObjectType and not self.ownsMembers:
12611 uninit = (
12612 'MOZ_ASSERT(mType != eObject, "This will not play well with Rooted");\n'
12613 + uninit
12615 methods.append(
12616 ClassMethod(
12617 "SetAs" + vars["name"],
12618 vars["structType"] + "&",
12619 vars["ctorArgList"],
12620 bodyInHeader=not self.ownsMembers,
12621 body=body % uninit,
12624 if self.ownsMembers:
12625 if vars["setters"]:
12626 methods.extend(vars["setters"])
12627 # Provide a SetStringLiteral() method to support string defaults.
12628 if t.isByteString() or t.isUTF8String():
12629 charType = "const nsCString::char_type"
12630 elif t.isString():
12631 charType = "const nsString::char_type"
12632 else:
12633 charType = None
12635 if charType:
12636 methods.append(
12637 ClassMethod(
12638 "SetStringLiteral",
12639 "void",
12640 # Hack, but it works...
12641 [Argument(charType, "(&aData)[N]")],
12642 inline=True,
12643 bodyInHeader=True,
12644 templateArgs=["int N"],
12645 body="RawSetAs%s().AssignLiteral(aData);\n" % t.name,
12649 body = fill(
12651 MOZ_ASSERT(Is${name}(), "Wrong type!");
12652 mValue.m${name}.Destroy();
12653 mType = eUninitialized;
12654 """,
12655 **vars
12657 methods.append(
12658 ClassMethod(
12659 "Destroy" + vars["name"],
12660 "void",
12662 visibility="private",
12663 bodyInHeader=not self.ownsMembers,
12664 body=body,
12668 body = fill("return mType == e${name};\n", **vars)
12669 methods.append(
12670 ClassMethod(
12671 "Is" + vars["name"],
12672 "bool",
12674 const=True,
12675 bodyInHeader=True,
12676 body=body,
12680 body = fill(
12682 MOZ_ASSERT(Is${name}(), "Wrong type!");
12683 return mValue.m${name}.Value();
12684 """,
12685 **vars
12687 # The non-const version of GetAs* returns our internal type
12688 getterReturnType = "%s&" % vars["structType"]
12689 methods.append(
12690 ClassMethod(
12691 "GetAs" + vars["name"],
12692 getterReturnType,
12694 bodyInHeader=True,
12695 body=body,
12698 # The const version of GetAs* returns our internal type
12699 # for owning unions, but our external type for non-owning
12700 # ones.
12701 if self.ownsMembers:
12702 getterReturnType = "%s const &" % vars["structType"]
12703 else:
12704 getterReturnType = vars["externalType"]
12705 methods.append(
12706 ClassMethod(
12707 "GetAs" + vars["name"],
12708 getterReturnType,
12710 const=True,
12711 bodyInHeader=True,
12712 body=body,
12716 unionValues.append(fill("UnionMember<${structType} > m${name}", **vars))
12717 enumValues.append("e" + vars["name"])
12719 conversionToJS = self.getConversionToJS(vars, t)
12720 if conversionToJS:
12721 toJSValCases.append(CGCase("e" + vars["name"], conversionToJS))
12722 else:
12723 skipToJSVal = True
12725 destructorCases.append(
12726 CGCase("e" + vars["name"], CGGeneric("Destroy%s();\n" % vars["name"]))
12728 assignmentCases.append(
12729 CGCase(
12730 "e" + vars["name"],
12731 CGGeneric(
12732 "SetAs%s() = aOther.GetAs%s();\n" % (vars["name"], vars["name"])
12736 if self.ownsMembers and typeNeedsRooting(t):
12737 if t.isObject():
12738 traceCases.append(
12739 CGCase(
12740 "e" + vars["name"],
12741 CGGeneric(
12742 'JS::UnsafeTraceRoot(trc, %s, "%s");\n'
12744 "&mValue.m" + vars["name"] + ".Value()",
12745 "mValue.m" + vars["name"],
12750 elif t.isDictionary():
12751 traceCases.append(
12752 CGCase(
12753 "e" + vars["name"],
12754 CGGeneric(
12755 "mValue.m%s.Value().TraceDictionary(trc);\n"
12756 % vars["name"]
12760 elif t.isSequence():
12761 traceCases.append(
12762 CGCase(
12763 "e" + vars["name"],
12764 CGGeneric(
12765 "DoTraceSequence(trc, mValue.m%s.Value());\n"
12766 % vars["name"]
12770 elif t.isRecord():
12771 traceCases.append(
12772 CGCase(
12773 "e" + vars["name"],
12774 CGGeneric(
12775 "TraceRecord(trc, mValue.m%s.Value());\n" % vars["name"]
12779 else:
12780 assert t.isSpiderMonkeyInterface()
12781 traceCases.append(
12782 CGCase(
12783 "e" + vars["name"],
12784 CGGeneric(
12785 "mValue.m%s.Value().TraceSelf(trc);\n" % vars["name"]
12790 dtor = CGSwitch("mType", destructorCases).define()
12792 methods.append(
12793 ClassMethod(
12794 "Uninit",
12795 "void",
12797 visibility="public",
12798 body=dtor,
12799 bodyInHeader=not self.ownsMembers,
12800 inline=not self.ownsMembers,
12804 if not skipToJSVal:
12805 methods.append(
12806 ClassMethod(
12807 "ToJSVal",
12808 "bool",
12810 Argument("JSContext*", "cx"),
12811 Argument("JS::Handle<JSObject*>", "scopeObj"),
12812 Argument("JS::MutableHandle<JS::Value>", "rval"),
12814 body=CGSwitch(
12815 "mType", toJSValCases, default=CGGeneric("return false;\n")
12816 ).define()
12817 + "\nreturn false;\n",
12818 const=True,
12822 constructors = [ctor]
12823 selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers)
12824 if self.ownsMembers:
12825 if traceCases:
12826 traceBody = CGSwitch(
12827 "mType", traceCases, default=CGGeneric("")
12828 ).define()
12829 else:
12830 traceBody = ""
12831 methods.append(
12832 ClassMethod(
12833 "TraceUnion", "void", [Argument("JSTracer*", "trc")], body=traceBody
12836 if CGUnionStruct.isUnionCopyConstructible(self.type):
12837 constructors.append(
12838 ClassConstructor(
12839 [Argument("const %s&" % selfName, "aOther")],
12840 bodyInHeader=True,
12841 visibility="public",
12842 explicit=True,
12843 body="*this = aOther;\n",
12846 op_body = CGList([])
12847 op_body.append(CGSwitch("aOther.mType", assignmentCases))
12848 op_body.append(CGGeneric("return *this;\n"))
12849 methods.append(
12850 ClassMethod(
12851 "operator=",
12852 "%s&" % selfName,
12853 [Argument("const %s&" % selfName, "aOther")],
12854 body=op_body.define(),
12857 disallowCopyConstruction = False
12858 else:
12859 disallowCopyConstruction = True
12860 else:
12861 disallowCopyConstruction = True
12863 if self.ownsMembers:
12864 friend = (
12865 " friend void ImplCycleCollectionUnlink(%s& aUnion);\n"
12866 % CGUnionStruct.unionTypeName(self.type, True)
12868 else:
12869 friend = " friend class %sArgument;\n" % str(self.type)
12871 bases = [ClassBase("AllOwningUnionBase")] if self.ownsMembers else []
12872 return CGClass(
12873 selfName,
12874 bases=bases,
12875 members=members,
12876 constructors=constructors,
12877 methods=methods,
12878 disallowCopyConstruction=disallowCopyConstruction,
12879 extradeclarations=friend,
12880 destructor=ClassDestructor(
12881 visibility="public", body="Uninit();\n", bodyInHeader=True
12883 enums=[ClassEnum("Type", enumValues, visibility="private")],
12884 unions=[ClassUnion("Value", unionValues, visibility="private")],
12887 def getConversionToJS(self, templateVars, type):
12888 if type.isDictionary() and not type.inner.needsConversionToJS:
12889 # We won't be able to convert this dictionary to a JS value, nor
12890 # will we need to, since we don't need a ToJSVal method at all.
12891 return None
12893 assert not type.nullable() # flatMemberTypes never has nullable types
12894 val = "mValue.m%(name)s.Value()" % templateVars
12895 wrapCode = wrapForType(
12896 type,
12897 self.descriptorProvider,
12899 "jsvalRef": "rval",
12900 "jsvalHandle": "rval",
12901 "obj": "scopeObj",
12902 "result": val,
12903 "spiderMonkeyInterfacesAreStructs": True,
12906 return CGGeneric(wrapCode)
12908 @staticmethod
12909 def isUnionCopyConstructible(type):
12910 return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes)
12912 @staticmethod
12913 def unionTypeName(type, ownsMembers):
12915 Returns a string name for this known union type.
12917 assert type.isUnion() and not type.nullable()
12918 return ("Owning" if ownsMembers else "") + type.name
12920 @staticmethod
12921 def unionTypeDecl(type, ownsMembers):
12923 Returns a string for declaring this possibly-nullable union type.
12925 assert type.isUnion()
12926 nullable = type.nullable()
12927 if nullable:
12928 type = type.inner
12929 decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers))
12930 if nullable:
12931 decl = CGTemplatedType("Nullable", decl)
12932 return decl.define()
12935 class CGUnionConversionStruct(CGThing):
12936 def __init__(self, type, descriptorProvider):
12937 CGThing.__init__(self)
12938 self.type = type.unroll()
12939 self.descriptorProvider = descriptorProvider
12941 def declare(self):
12943 structName = str(self.type)
12944 members = [
12945 ClassMember(
12946 "mUnion", structName + "&", body="const_cast<%s&>(aUnion)" % structName
12949 # Argument needs to be a const ref because that's all Maybe<> allows
12950 ctor = ClassConstructor(
12951 [Argument("const %s&" % structName, "aUnion")],
12952 bodyInHeader=True,
12953 visibility="public",
12954 explicit=True,
12956 methods = []
12958 if self.type.hasNullableType:
12959 methods.append(
12960 ClassMethod(
12961 "SetNull",
12962 "bool",
12964 body=(
12965 "MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);\n"
12966 "mUnion.mType = mUnion.eNull;\n"
12967 "return true;\n"
12969 inline=True,
12970 bodyInHeader=True,
12974 for t in self.type.flatMemberTypes:
12975 vars = getUnionTypeTemplateVars(self.type, t, self.descriptorProvider)
12976 if vars["setters"]:
12977 methods.extend(vars["setters"])
12978 if vars["name"] != "Object":
12979 body = fill(
12981 MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);
12982 mUnion.mType = mUnion.e${name};
12983 return mUnion.mValue.m${name}.SetValue(${ctorArgs});
12984 """,
12985 **vars
12987 methods.append(
12988 ClassMethod(
12989 "RawSetAs" + vars["name"],
12990 vars["structType"] + "&",
12991 vars["ctorArgList"],
12992 bodyInHeader=True,
12993 body=body,
12994 visibility="private",
12997 # Provide a SetStringLiteral() method to support string defaults.
12998 if t.isByteString() or t.isUTF8String():
12999 charType = "const nsCString::char_type"
13000 elif t.isString():
13001 charType = "const nsString::char_type"
13002 else:
13003 charType = None
13005 if charType:
13006 methods.append(
13007 ClassMethod(
13008 "SetStringLiteral",
13009 "void",
13010 # Hack, but it works...
13011 [Argument(charType, "(&aData)[N]")],
13012 inline=True,
13013 bodyInHeader=True,
13014 templateArgs=["int N"],
13015 body="RawSetAs%s().AssignLiteral(aData);\n" % t.name,
13019 if vars["holderType"] is not None:
13020 holderType = CGTemplatedType(
13021 "Maybe", CGGeneric(vars["holderType"])
13022 ).define()
13023 members.append(ClassMember("m%sHolder" % vars["name"], holderType))
13025 return CGClass(
13026 structName + "Argument",
13027 members=members,
13028 constructors=[ctor],
13029 methods=methods,
13030 disallowCopyConstruction=True,
13031 ).declare()
13033 def define(self):
13034 return ""
13036 def deps(self):
13037 return set()
13040 class ClassItem:
13041 """ Use with CGClass """
13043 def __init__(self, name, visibility):
13044 self.name = name
13045 self.visibility = visibility
13047 def declare(self, cgClass):
13048 assert False
13050 def define(self, cgClass):
13051 assert False
13054 class ClassBase(ClassItem):
13055 def __init__(self, name, visibility="public"):
13056 ClassItem.__init__(self, name, visibility)
13058 def declare(self, cgClass):
13059 return "%s %s" % (self.visibility, self.name)
13061 def define(self, cgClass):
13062 # Only in the header
13063 return ""
13066 class ClassMethod(ClassItem):
13067 def __init__(
13068 self,
13069 name,
13070 returnType,
13071 args,
13072 inline=False,
13073 static=False,
13074 virtual=False,
13075 const=False,
13076 bodyInHeader=False,
13077 templateArgs=None,
13078 visibility="public",
13079 body=None,
13080 breakAfterReturnDecl="\n",
13081 breakAfterSelf="\n",
13082 override=False,
13083 canRunScript=False,
13086 override indicates whether to flag the method as override
13088 assert not override or virtual
13089 assert not (override and static)
13090 self.returnType = returnType
13091 self.args = args
13092 self.inline = inline or bodyInHeader
13093 self.static = static
13094 self.virtual = virtual
13095 self.const = const
13096 self.bodyInHeader = bodyInHeader
13097 self.templateArgs = templateArgs
13098 self.body = body
13099 self.breakAfterReturnDecl = breakAfterReturnDecl
13100 self.breakAfterSelf = breakAfterSelf
13101 self.override = override
13102 self.canRunScript = canRunScript
13103 ClassItem.__init__(self, name, visibility)
13105 def getDecorators(self, declaring):
13106 decorators = []
13107 if self.canRunScript:
13108 decorators.append("MOZ_CAN_RUN_SCRIPT")
13109 if self.inline:
13110 decorators.append("inline")
13111 if declaring:
13112 if self.static:
13113 decorators.append("static")
13114 if self.virtual and not self.override:
13115 decorators.append("virtual")
13116 if decorators:
13117 return " ".join(decorators) + " "
13118 return ""
13120 def getBody(self):
13121 # Override me or pass a string to constructor
13122 assert self.body is not None
13123 return self.body
13125 def declare(self, cgClass):
13126 templateClause = (
13127 "template <%s>\n" % ", ".join(self.templateArgs)
13128 if self.bodyInHeader and self.templateArgs
13129 else ""
13131 args = ", ".join([a.declare() for a in self.args])
13132 if self.bodyInHeader:
13133 body = indent(self.getBody())
13134 body = "\n{\n" + body + "}\n"
13135 else:
13136 body = ";\n"
13138 return fill(
13139 "${templateClause}${decorators}${returnType}${breakAfterReturnDecl}"
13140 "${name}(${args})${const}${override}${body}"
13141 "${breakAfterSelf}",
13142 templateClause=templateClause,
13143 decorators=self.getDecorators(True),
13144 returnType=self.returnType,
13145 breakAfterReturnDecl=self.breakAfterReturnDecl,
13146 name=self.name,
13147 args=args,
13148 const=" const" if self.const else "",
13149 override=" override" if self.override else "",
13150 body=body,
13151 breakAfterSelf=self.breakAfterSelf,
13154 def define(self, cgClass):
13155 if self.bodyInHeader:
13156 return ""
13158 templateArgs = cgClass.templateArgs
13159 if templateArgs:
13160 if cgClass.templateSpecialization:
13161 templateArgs = templateArgs[len(cgClass.templateSpecialization) :]
13163 if templateArgs:
13164 templateClause = "template <%s>\n" % ", ".join(
13165 [str(a) for a in templateArgs]
13167 else:
13168 templateClause = ""
13170 return fill(
13172 ${templateClause}${decorators}${returnType}
13173 ${className}::${name}(${args})${const}
13175 $*{body}
13177 """,
13178 templateClause=templateClause,
13179 decorators=self.getDecorators(False),
13180 returnType=self.returnType,
13181 className=cgClass.getNameString(),
13182 name=self.name,
13183 args=", ".join([a.define() for a in self.args]),
13184 const=" const" if self.const else "",
13185 body=self.getBody(),
13189 class ClassUsingDeclaration(ClassItem):
13191 Used for importing a name from a base class into a CGClass
13193 baseClass is the name of the base class to import the name from
13195 name is the name to import
13197 visibility determines the visibility of the name (public,
13198 protected, private), defaults to public.
13201 def __init__(self, baseClass, name, visibility="public"):
13202 self.baseClass = baseClass
13203 ClassItem.__init__(self, name, visibility)
13205 def declare(self, cgClass):
13206 return "using %s::%s;\n\n" % (self.baseClass, self.name)
13208 def define(self, cgClass):
13209 return ""
13212 class ClassConstructor(ClassItem):
13214 Used for adding a constructor to a CGClass.
13216 args is a list of Argument objects that are the arguments taken by the
13217 constructor.
13219 inline should be True if the constructor should be marked inline.
13221 bodyInHeader should be True if the body should be placed in the class
13222 declaration in the header.
13224 visibility determines the visibility of the constructor (public,
13225 protected, private), defaults to private.
13227 explicit should be True if the constructor should be marked explicit.
13229 baseConstructors is a list of strings containing calls to base constructors,
13230 defaults to None.
13232 body contains a string with the code for the constructor, defaults to empty.
13235 def __init__(
13236 self,
13237 args,
13238 inline=False,
13239 bodyInHeader=False,
13240 visibility="private",
13241 explicit=False,
13242 constexpr=False,
13243 baseConstructors=None,
13244 body="",
13246 assert not (inline and constexpr)
13247 assert not (bodyInHeader and constexpr)
13248 self.args = args
13249 self.inline = inline or bodyInHeader
13250 self.bodyInHeader = bodyInHeader or constexpr
13251 self.explicit = explicit
13252 self.constexpr = constexpr
13253 self.baseConstructors = baseConstructors or []
13254 self.body = body
13255 ClassItem.__init__(self, None, visibility)
13257 def getDecorators(self, declaring):
13258 decorators = []
13259 if self.explicit:
13260 decorators.append("explicit")
13261 if self.inline and declaring:
13262 decorators.append("inline")
13263 if self.constexpr and declaring:
13264 decorators.append("constexpr")
13265 if decorators:
13266 return " ".join(decorators) + " "
13267 return ""
13269 def getInitializationList(self, cgClass):
13270 items = [str(c) for c in self.baseConstructors]
13271 for m in cgClass.members:
13272 if not m.static:
13273 initialize = m.body
13274 if initialize:
13275 items.append(m.name + "(" + initialize + ")")
13277 if len(items) > 0:
13278 return "\n : " + ",\n ".join(items)
13279 return ""
13281 def getBody(self):
13282 return self.body
13284 def declare(self, cgClass):
13285 args = ", ".join([a.declare() for a in self.args])
13286 if self.bodyInHeader:
13287 body = (
13288 self.getInitializationList(cgClass)
13289 + "\n{\n"
13290 + indent(self.getBody())
13291 + "}\n"
13293 else:
13294 body = ";\n"
13296 return fill(
13297 "${decorators}${className}(${args})${body}\n",
13298 decorators=self.getDecorators(True),
13299 className=cgClass.getNameString(),
13300 args=args,
13301 body=body,
13304 def define(self, cgClass):
13305 if self.bodyInHeader:
13306 return ""
13308 return fill(
13310 ${decorators}
13311 ${className}::${className}(${args})${initializationList}
13313 $*{body}
13315 """,
13316 decorators=self.getDecorators(False),
13317 className=cgClass.getNameString(),
13318 args=", ".join([a.define() for a in self.args]),
13319 initializationList=self.getInitializationList(cgClass),
13320 body=self.getBody(),
13324 class ClassDestructor(ClassItem):
13326 Used for adding a destructor to a CGClass.
13328 inline should be True if the destructor should be marked inline.
13330 bodyInHeader should be True if the body should be placed in the class
13331 declaration in the header.
13333 visibility determines the visibility of the destructor (public,
13334 protected, private), defaults to private.
13336 body contains a string with the code for the destructor, defaults to empty.
13338 virtual determines whether the destructor is virtual, defaults to False.
13341 def __init__(
13342 self,
13343 inline=False,
13344 bodyInHeader=False,
13345 visibility="private",
13346 body="",
13347 virtual=False,
13349 self.inline = inline or bodyInHeader
13350 self.bodyInHeader = bodyInHeader
13351 self.body = body
13352 self.virtual = virtual
13353 ClassItem.__init__(self, None, visibility)
13355 def getDecorators(self, declaring):
13356 decorators = []
13357 if self.virtual and declaring:
13358 decorators.append("virtual")
13359 if self.inline and declaring:
13360 decorators.append("inline")
13361 if decorators:
13362 return " ".join(decorators) + " "
13363 return ""
13365 def getBody(self):
13366 return self.body
13368 def declare(self, cgClass):
13369 if self.bodyInHeader:
13370 body = "\n{\n" + indent(self.getBody()) + "}\n"
13371 else:
13372 body = ";\n"
13374 return fill(
13375 "${decorators}~${className}()${body}\n",
13376 decorators=self.getDecorators(True),
13377 className=cgClass.getNameString(),
13378 body=body,
13381 def define(self, cgClass):
13382 if self.bodyInHeader:
13383 return ""
13384 return fill(
13386 ${decorators}
13387 ${className}::~${className}()
13389 $*{body}
13391 """,
13392 decorators=self.getDecorators(False),
13393 className=cgClass.getNameString(),
13394 body=self.getBody(),
13398 class ClassMember(ClassItem):
13399 def __init__(
13400 self,
13401 name,
13402 type,
13403 visibility="private",
13404 static=False,
13405 body=None,
13406 hasIgnoreInitCheckFlag=False,
13408 self.type = type
13409 self.static = static
13410 self.body = body
13411 self.hasIgnoreInitCheckFlag = hasIgnoreInitCheckFlag
13412 ClassItem.__init__(self, name, visibility)
13414 def declare(self, cgClass):
13415 return "%s%s%s %s;\n" % (
13416 "static " if self.static else "",
13417 "MOZ_INIT_OUTSIDE_CTOR " if self.hasIgnoreInitCheckFlag else "",
13418 self.type,
13419 self.name,
13422 def define(self, cgClass):
13423 if not self.static:
13424 return ""
13425 if self.body:
13426 body = " = " + self.body
13427 else:
13428 body = ""
13429 return "%s %s::%s%s;\n" % (self.type, cgClass.getNameString(), self.name, body)
13432 class ClassEnum(ClassItem):
13433 def __init__(self, name, entries, values=None, visibility="public"):
13434 self.entries = entries
13435 self.values = values
13436 ClassItem.__init__(self, name, visibility)
13438 def declare(self, cgClass):
13439 entries = []
13440 for i in range(0, len(self.entries)):
13441 if not self.values or i >= len(self.values):
13442 entry = "%s" % self.entries[i]
13443 else:
13444 entry = "%s = %s" % (self.entries[i], self.values[i])
13445 entries.append(entry)
13446 name = "" if not self.name else " " + self.name
13447 return "enum%s\n{\n%s\n};\n" % (name, indent(",\n".join(entries)))
13449 def define(self, cgClass):
13450 # Only goes in the header
13451 return ""
13454 class ClassUnion(ClassItem):
13455 def __init__(self, name, entries, visibility="public"):
13456 self.entries = [entry + ";\n" for entry in entries]
13457 ClassItem.__init__(self, name, visibility)
13459 def declare(self, cgClass):
13460 return "union %s\n{\n%s\n};\n" % (self.name, indent("".join(self.entries)))
13462 def define(self, cgClass):
13463 # Only goes in the header
13464 return ""
13467 class CGClass(CGThing):
13468 def __init__(
13469 self,
13470 name,
13471 bases=[],
13472 members=[],
13473 constructors=[],
13474 destructor=None,
13475 methods=[],
13476 enums=[],
13477 unions=[],
13478 templateArgs=[],
13479 templateSpecialization=[],
13480 isStruct=False,
13481 disallowCopyConstruction=False,
13482 indent="",
13483 decorators="",
13484 extradeclarations="",
13485 extradefinitions="",
13487 CGThing.__init__(self)
13488 self.name = name
13489 self.bases = bases
13490 self.members = members
13491 self.constructors = constructors
13492 # We store our single destructor in a list, since all of our
13493 # code wants lists of members.
13494 self.destructors = [destructor] if destructor else []
13495 self.methods = methods
13496 self.enums = enums
13497 self.unions = unions
13498 self.templateArgs = templateArgs
13499 self.templateSpecialization = templateSpecialization
13500 self.isStruct = isStruct
13501 self.disallowCopyConstruction = disallowCopyConstruction
13502 self.indent = indent
13503 self.defaultVisibility = "public" if isStruct else "private"
13504 self.decorators = decorators
13505 self.extradeclarations = extradeclarations
13506 self.extradefinitions = extradefinitions
13508 def getNameString(self):
13509 className = self.name
13510 if self.templateSpecialization:
13511 className += "<%s>" % ", ".join(
13512 [str(a) for a in self.templateSpecialization]
13514 return className
13516 def declare(self):
13517 result = ""
13518 if self.templateArgs:
13519 templateArgs = [a.declare() for a in self.templateArgs]
13520 templateArgs = templateArgs[len(self.templateSpecialization) :]
13521 result += "template <%s>\n" % ",".join([str(a) for a in templateArgs])
13523 type = "struct" if self.isStruct else "class"
13525 if self.templateSpecialization:
13526 specialization = "<%s>" % ", ".join(
13527 [str(a) for a in self.templateSpecialization]
13529 else:
13530 specialization = ""
13532 myself = "%s %s%s" % (type, self.name, specialization)
13533 if self.decorators != "":
13534 myself += " " + self.decorators
13535 result += myself
13537 if self.bases:
13538 inherit = " : "
13539 result += inherit
13540 # Grab our first base
13541 baseItems = [CGGeneric(b.declare(self)) for b in self.bases]
13542 bases = baseItems[:1]
13543 # Indent the rest
13544 bases.extend(
13545 CGIndenter(b, len(myself) + len(inherit)) for b in baseItems[1:]
13547 result += ",\n".join(b.define() for b in bases)
13549 result += "\n{\n"
13551 result += self.extradeclarations
13553 def declareMembers(cgClass, memberList, defaultVisibility):
13554 members = {"private": [], "protected": [], "public": []}
13556 for member in memberList:
13557 members[member.visibility].append(member)
13559 if defaultVisibility == "public":
13560 order = ["public", "protected", "private"]
13561 else:
13562 order = ["private", "protected", "public"]
13564 result = ""
13566 lastVisibility = defaultVisibility
13567 for visibility in order:
13568 list = members[visibility]
13569 if list:
13570 if visibility != lastVisibility:
13571 result += visibility + ":\n"
13572 for member in list:
13573 result += indent(member.declare(cgClass))
13574 lastVisibility = visibility
13575 return (result, lastVisibility)
13577 if self.disallowCopyConstruction:
13579 class DisallowedCopyConstructor(object):
13580 def __init__(self):
13581 self.visibility = "private"
13583 def declare(self, cgClass):
13584 name = cgClass.getNameString()
13585 return (
13586 "%s(const %s&) = delete;\n"
13587 "%s& operator=(const %s&) = delete;\n"
13588 % (name, name, name, name)
13591 disallowedCopyConstructors = [DisallowedCopyConstructor()]
13592 else:
13593 disallowedCopyConstructors = []
13595 order = [
13596 self.enums,
13597 self.unions,
13598 self.members,
13599 self.constructors + disallowedCopyConstructors,
13600 self.destructors,
13601 self.methods,
13604 lastVisibility = self.defaultVisibility
13605 pieces = []
13606 for memberList in order:
13607 code, lastVisibility = declareMembers(self, memberList, lastVisibility)
13609 if code:
13610 code = code.rstrip() + "\n" # remove extra blank lines at the end
13611 pieces.append(code)
13613 result += "\n".join(pieces)
13614 result += "};\n"
13615 result = indent(result, len(self.indent))
13616 return result
13618 def define(self):
13619 def defineMembers(cgClass, memberList, itemCount, separator=""):
13620 result = ""
13621 for member in memberList:
13622 if itemCount != 0:
13623 result = result + separator
13624 definition = member.define(cgClass)
13625 if definition:
13626 # Member variables would only produce empty lines here.
13627 result += definition
13628 itemCount += 1
13629 return (result, itemCount)
13631 order = [
13632 (self.members, ""),
13633 (self.constructors, "\n"),
13634 (self.destructors, "\n"),
13635 (self.methods, "\n"),
13638 result = self.extradefinitions
13639 itemCount = 0
13640 for memberList, separator in order:
13641 memberString, itemCount = defineMembers(
13642 self, memberList, itemCount, separator
13644 result = result + memberString
13645 return result
13648 class CGResolveOwnProperty(CGAbstractStaticMethod):
13649 def __init__(self, descriptor):
13650 args = [
13651 Argument("JSContext*", "cx"),
13652 Argument("JS::Handle<JSObject*>", "wrapper"),
13653 Argument("JS::Handle<JSObject*>", "obj"),
13654 Argument("JS::Handle<jsid>", "id"),
13655 Argument("JS::MutableHandle<JS::PropertyDescriptor>", "desc"),
13657 CGAbstractStaticMethod.__init__(
13658 self, descriptor, "ResolveOwnProperty", "bool", args
13661 def definition_body(self):
13662 return dedent(
13664 JS::Rooted<mozilla::Maybe<JS::PropertyDescriptor>> ownDesc(cx);
13665 if (!js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, &ownDesc)) {
13666 return false;
13669 if (ownDesc.isNothing()) {
13670 desc.object().set(nullptr);
13671 } else {
13672 desc.set(*ownDesc);
13675 return true;
13680 class CGResolveOwnPropertyViaResolve(CGAbstractBindingMethod):
13682 An implementation of Xray ResolveOwnProperty stuff for things that have a
13683 resolve hook.
13686 def __init__(self, descriptor):
13687 args = [
13688 Argument("JSContext*", "cx"),
13689 Argument("JS::Handle<JSObject*>", "wrapper"),
13690 Argument("JS::Handle<JSObject*>", "obj"),
13691 Argument("JS::Handle<jsid>", "id"),
13692 Argument("JS::MutableHandle<JS::PropertyDescriptor>", "desc"),
13694 CGAbstractBindingMethod.__init__(
13695 self,
13696 descriptor,
13697 "ResolveOwnPropertyViaResolve",
13698 args,
13699 getThisObj="",
13700 callArgs="",
13703 def generate_code(self):
13704 return CGGeneric(
13705 dedent(
13708 // Since we're dealing with an Xray, do the resolve on the
13709 // underlying object first. That gives it a chance to
13710 // define properties on the actual object as needed, and
13711 // then use the fact that it created the objects as a flag
13712 // to avoid re-resolving the properties if someone deletes
13713 // them.
13714 JSAutoRealm ar(cx, obj);
13715 JS_MarkCrossZoneId(cx, id);
13716 JS::Rooted<JS::PropertyDescriptor> objDesc(cx);
13717 if (!self->DoResolve(cx, obj, id, &objDesc)) {
13718 return false;
13720 // If desc.value() is undefined, then the DoResolve call
13721 // has already defined the property on the object. Don't
13722 // try to also define it.
13723 if (objDesc.object() &&
13724 !objDesc.value().isUndefined() &&
13725 !JS_DefinePropertyById(cx, obj, id, objDesc)) {
13726 return false;
13729 return self->DoResolve(cx, wrapper, id, desc);
13735 class CGEnumerateOwnProperties(CGAbstractStaticMethod):
13736 def __init__(self, descriptor):
13737 args = [
13738 Argument("JSContext*", "cx"),
13739 Argument("JS::Handle<JSObject*>", "wrapper"),
13740 Argument("JS::Handle<JSObject*>", "obj"),
13741 Argument("JS::MutableHandleVector<jsid>", "props"),
13743 CGAbstractStaticMethod.__init__(
13744 self, descriptor, "EnumerateOwnProperties", "bool", args
13747 def definition_body(self):
13748 return "return js::GetProxyHandler(obj)->ownPropertyKeys(cx, wrapper, props);\n"
13751 class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod):
13753 An implementation of Xray EnumerateOwnProperties stuff for things
13754 that have a resolve hook.
13757 def __init__(self, descriptor):
13758 args = [
13759 Argument("JSContext*", "cx"),
13760 Argument("JS::Handle<JSObject*>", "wrapper"),
13761 Argument("JS::Handle<JSObject*>", "obj"),
13762 Argument("JS::MutableHandleVector<jsid>", "props"),
13764 CGAbstractBindingMethod.__init__(
13765 self,
13766 descriptor,
13767 "EnumerateOwnPropertiesViaGetOwnPropertyNames",
13768 args,
13769 getThisObj="",
13770 callArgs="",
13773 def generate_code(self):
13774 return CGGeneric(
13775 dedent(
13777 FastErrorResult rv;
13778 // This wants all own props, not just enumerable ones.
13779 self->GetOwnPropertyNames(cx, props, false, rv);
13780 if (rv.MaybeSetPendingException(cx)) {
13781 return false;
13783 return true;
13789 class CGPrototypeTraitsClass(CGClass):
13790 def __init__(self, descriptor, indent=""):
13791 templateArgs = [Argument("prototypes::ID", "PrototypeID")]
13792 templateSpecialization = ["prototypes::id::" + descriptor.name]
13793 enums = [ClassEnum("", ["Depth"], [descriptor.interface.inheritanceDepth()])]
13794 CGClass.__init__(
13795 self,
13796 "PrototypeTraits",
13797 indent=indent,
13798 templateArgs=templateArgs,
13799 templateSpecialization=templateSpecialization,
13800 enums=enums,
13801 isStruct=True,
13804 def deps(self):
13805 return set()
13808 class CGClassForwardDeclare(CGThing):
13809 def __init__(self, name, isStruct=False):
13810 CGThing.__init__(self)
13811 self.name = name
13812 self.isStruct = isStruct
13814 def declare(self):
13815 type = "struct" if self.isStruct else "class"
13816 return "%s %s;\n" % (type, self.name)
13818 def define(self):
13819 # Header only
13820 return ""
13822 def deps(self):
13823 return set()
13826 class CGProxySpecialOperation(CGPerSignatureCall):
13828 Base class for classes for calling an indexed or named special operation
13829 (don't use this directly, use the derived classes below).
13831 If checkFound is False, will just assert that the prop is found instead of
13832 checking that it is before wrapping the value.
13834 resultVar: See the docstring for CGCallGenerator.
13836 foundVar: For getters and deleters, the generated code can also set a bool
13837 variable, declared by the caller, if the given indexed or named property
13838 already existed. If the caller wants this, it should pass the name of the
13839 bool variable as the foundVar keyword argument to the constructor. The
13840 caller is responsible for declaring the variable and initializing it to
13841 false.
13844 def __init__(
13845 self,
13846 descriptor,
13847 operation,
13848 checkFound=True,
13849 argumentHandleValue=None,
13850 resultVar=None,
13851 foundVar=None,
13853 self.checkFound = checkFound
13854 self.foundVar = foundVar or "found"
13856 nativeName = MakeNativeName(descriptor.binaryNameFor(operation))
13857 operation = descriptor.operations[operation]
13858 assert len(operation.signatures()) == 1
13859 signature = operation.signatures()[0]
13861 returnType, arguments = signature
13863 # We pass len(arguments) as the final argument so that the
13864 # CGPerSignatureCall won't do any argument conversion of its own.
13865 CGPerSignatureCall.__init__(
13866 self,
13867 returnType,
13868 arguments,
13869 nativeName,
13870 False,
13871 descriptor,
13872 operation,
13873 len(arguments),
13874 resultVar=resultVar,
13875 objectName="proxy",
13878 if operation.isSetter():
13879 # arguments[0] is the index or name of the item that we're setting.
13880 argument = arguments[1]
13881 info = getJSToNativeConversionInfo(
13882 argument.type,
13883 descriptor,
13884 sourceDescription=(
13885 "value being assigned to %s setter"
13886 % descriptor.interface.identifier.name
13889 if argumentHandleValue is None:
13890 argumentHandleValue = "desc.value()"
13891 rootedValue = fill(
13893 JS::Rooted<JS::Value> rootedValue(cx, ${argumentHandleValue});
13894 """,
13895 argumentHandleValue=argumentHandleValue,
13897 templateValues = {
13898 "declName": argument.identifier.name,
13899 "holderName": argument.identifier.name + "_holder",
13900 "val": argumentHandleValue,
13901 "maybeMutableVal": "&rootedValue",
13902 "obj": "obj",
13903 "passedToJSImpl": "false",
13905 self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues))
13906 # rootedValue needs to come before the conversion, so we
13907 # need to prepend it last.
13908 self.cgRoot.prepend(CGGeneric(rootedValue))
13909 elif operation.isGetter() or operation.isDeleter():
13910 if foundVar is None:
13911 self.cgRoot.prepend(CGGeneric("bool found = false;\n"))
13913 def getArguments(self):
13914 args = [(a, a.identifier.name) for a in self.arguments]
13915 if self.idlNode.isGetter() or self.idlNode.isDeleter():
13916 args.append(
13918 FakeArgument(
13919 BuiltinTypes[IDLBuiltinType.Types.boolean], self.idlNode
13921 self.foundVar,
13924 return args
13926 def wrap_return_value(self):
13927 if not self.idlNode.isGetter() or self.templateValues is None:
13928 return ""
13930 wrap = CGGeneric(
13931 wrapForType(self.returnType, self.descriptor, self.templateValues)
13933 if self.checkFound:
13934 wrap = CGIfWrapper(wrap, self.foundVar)
13935 else:
13936 wrap = CGList([CGGeneric("MOZ_ASSERT(" + self.foundVar + ");\n"), wrap])
13937 return "\n" + wrap.define()
13940 class CGProxyIndexedOperation(CGProxySpecialOperation):
13942 Class to generate a call to an indexed operation.
13944 If doUnwrap is False, the caller is responsible for making sure a variable
13945 named 'self' holds the C++ object somewhere where the code we generate
13946 will see it.
13948 If checkFound is False, will just assert that the prop is found instead of
13949 checking that it is before wrapping the value.
13951 resultVar: See the docstring for CGCallGenerator.
13953 foundVar: See the docstring for CGProxySpecialOperation.
13956 def __init__(
13957 self,
13958 descriptor,
13959 name,
13960 doUnwrap=True,
13961 checkFound=True,
13962 argumentHandleValue=None,
13963 resultVar=None,
13964 foundVar=None,
13966 self.doUnwrap = doUnwrap
13967 CGProxySpecialOperation.__init__(
13968 self,
13969 descriptor,
13970 name,
13971 checkFound,
13972 argumentHandleValue=argumentHandleValue,
13973 resultVar=resultVar,
13974 foundVar=foundVar,
13977 def define(self):
13978 # Our first argument is the id we're getting.
13979 argName = self.arguments[0].identifier.name
13980 if argName == "index":
13981 # We already have our index in a variable with that name
13982 setIndex = ""
13983 else:
13984 setIndex = "uint32_t %s = index;\n" % argName
13985 if self.doUnwrap:
13986 unwrap = "%s* self = UnwrapProxy(proxy);\n" % self.descriptor.nativeType
13987 else:
13988 unwrap = ""
13989 return setIndex + unwrap + CGProxySpecialOperation.define(self)
13992 class CGProxyIndexedGetter(CGProxyIndexedOperation):
13994 Class to generate a call to an indexed getter. If templateValues is not None
13995 the returned value will be wrapped with wrapForType using templateValues.
13997 If doUnwrap is False, the caller is responsible for making sure a variable
13998 named 'self' holds the C++ object somewhere where the code we generate
13999 will see it.
14001 If checkFound is False, will just assert that the prop is found instead of
14002 checking that it is before wrapping the value.
14004 foundVar: See the docstring for CGProxySpecialOperation.
14007 def __init__(
14008 self,
14009 descriptor,
14010 templateValues=None,
14011 doUnwrap=True,
14012 checkFound=True,
14013 foundVar=None,
14015 self.templateValues = templateValues
14016 CGProxyIndexedOperation.__init__(
14017 self, descriptor, "IndexedGetter", doUnwrap, checkFound, foundVar=foundVar
14021 class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter):
14023 Class to generate a call that checks whether an indexed property exists.
14025 For now, we just delegate to CGProxyIndexedGetter
14027 foundVar: See the docstring for CGProxySpecialOperation.
14030 def __init__(self, descriptor, foundVar):
14031 CGProxyIndexedGetter.__init__(self, descriptor, foundVar=foundVar)
14032 self.cgRoot.append(CGGeneric("(void)result;\n"))
14035 class CGProxyIndexedSetter(CGProxyIndexedOperation):
14037 Class to generate a call to an indexed setter.
14040 def __init__(self, descriptor, argumentHandleValue=None):
14041 CGProxyIndexedOperation.__init__(
14042 self, descriptor, "IndexedSetter", argumentHandleValue=argumentHandleValue
14046 class CGProxyNamedOperation(CGProxySpecialOperation):
14048 Class to generate a call to a named operation.
14050 'value' is the jsval to use for the name; None indicates that it should be
14051 gotten from the property id.
14053 resultVar: See the docstring for CGCallGenerator.
14055 foundVar: See the docstring for CGProxySpecialOperation.
14057 tailCode: if we end up with a non-symbol string id, run this code after
14058 we do all our other work.
14061 def __init__(
14062 self,
14063 descriptor,
14064 name,
14065 value=None,
14066 argumentHandleValue=None,
14067 resultVar=None,
14068 foundVar=None,
14069 tailCode="",
14071 CGProxySpecialOperation.__init__(
14072 self,
14073 descriptor,
14074 name,
14075 argumentHandleValue=argumentHandleValue,
14076 resultVar=resultVar,
14077 foundVar=foundVar,
14079 self.value = value
14080 self.tailCode = tailCode
14082 def define(self):
14083 # Our first argument is the id we're getting.
14084 argName = self.arguments[0].identifier.name
14085 if argName == "id":
14086 # deal with the name collision
14087 decls = "JS::Rooted<jsid> id_(cx, id);\n"
14088 idName = "id_"
14089 else:
14090 decls = ""
14091 idName = "id"
14093 decls += "FakeString<char16_t> %s;\n" % argName
14095 main = fill(
14097 ${nativeType}* self = UnwrapProxy(proxy);
14098 $*{op}
14099 $*{tailCode}
14100 """,
14101 nativeType=self.descriptor.nativeType,
14102 op=CGProxySpecialOperation.define(self),
14103 tailCode=self.tailCode,
14106 if self.value is None:
14107 return fill(
14109 $*{decls}
14110 bool isSymbol;
14111 if (!ConvertIdToString(cx, ${idName}, ${argName}, isSymbol)) {
14112 return false;
14114 if (!isSymbol) {
14115 $*{main}
14117 """,
14118 decls=decls,
14119 idName=idName,
14120 argName=argName,
14121 main=main,
14124 # Sadly, we have to set up nameVal even if we have an atom id,
14125 # because we don't know for sure, and we can end up needing it
14126 # so it needs to be higher up the stack. Using a Maybe here
14127 # seems like probable overkill.
14128 return fill(
14130 $*{decls}
14131 JS::Rooted<JS::Value> nameVal(cx, ${value});
14132 if (!nameVal.isSymbol()) {
14133 if (!ConvertJSValueToString(cx, nameVal, eStringify, eStringify,
14134 ${argName})) {
14135 return false;
14137 $*{main}
14139 """,
14140 decls=decls,
14141 value=self.value,
14142 argName=argName,
14143 main=main,
14147 class CGProxyNamedGetter(CGProxyNamedOperation):
14149 Class to generate a call to an named getter. If templateValues is not None
14150 the returned value will be wrapped with wrapForType using templateValues.
14151 'value' is the jsval to use for the name; None indicates that it should be
14152 gotten from the property id.
14154 foundVar: See the docstring for CGProxySpecialOperation.
14157 def __init__(self, descriptor, templateValues=None, value=None, foundVar=None):
14158 self.templateValues = templateValues
14159 CGProxyNamedOperation.__init__(
14160 self, descriptor, "NamedGetter", value, foundVar=foundVar
14164 class CGProxyNamedPresenceChecker(CGProxyNamedGetter):
14166 Class to generate a call that checks whether a named property exists.
14168 For now, we just delegate to CGProxyNamedGetter
14170 foundVar: See the docstring for CGProxySpecialOperation.
14173 def __init__(self, descriptor, foundVar=None):
14174 CGProxyNamedGetter.__init__(self, descriptor, foundVar=foundVar)
14175 self.cgRoot.append(CGGeneric("(void)result;\n"))
14178 class CGProxyNamedSetter(CGProxyNamedOperation):
14180 Class to generate a call to a named setter.
14183 def __init__(self, descriptor, tailCode, argumentHandleValue=None):
14184 CGProxyNamedOperation.__init__(
14185 self,
14186 descriptor,
14187 "NamedSetter",
14188 argumentHandleValue=argumentHandleValue,
14189 tailCode=tailCode,
14193 class CGProxyNamedDeleter(CGProxyNamedOperation):
14195 Class to generate a call to a named deleter.
14197 resultVar: See the docstring for CGCallGenerator.
14199 foundVar: See the docstring for CGProxySpecialOperation.
14202 def __init__(self, descriptor, resultVar=None, foundVar=None):
14203 CGProxyNamedOperation.__init__(
14204 self, descriptor, "NamedDeleter", resultVar=resultVar, foundVar=foundVar
14208 class CGProxyIsProxy(CGAbstractMethod):
14209 def __init__(self, descriptor):
14210 args = [Argument("JSObject*", "obj")]
14211 CGAbstractMethod.__init__(
14212 self, descriptor, "IsProxy", "bool", args, alwaysInline=True
14215 def declare(self):
14216 return ""
14218 def definition_body(self):
14219 return "return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();\n"
14222 class CGProxyUnwrap(CGAbstractMethod):
14223 def __init__(self, descriptor):
14224 args = [Argument("JSObject*", "obj")]
14225 CGAbstractMethod.__init__(
14226 self,
14227 descriptor,
14228 "UnwrapProxy",
14229 descriptor.nativeType + "*",
14230 args,
14231 alwaysInline=True,
14234 def declare(self):
14235 return ""
14237 def definition_body(self):
14238 return fill(
14240 MOZ_ASSERT(js::IsProxy(obj));
14241 if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) {
14242 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj));
14243 obj = js::UncheckedUnwrap(obj);
14245 MOZ_ASSERT(IsProxy(obj));
14246 return static_cast<${type}*>(js::GetProxyReservedSlot(obj, DOM_OBJECT_SLOT).toPrivate());
14247 """,
14248 type=self.descriptor.nativeType,
14252 MISSING_PROP_PREF = "dom.missing_prop_counters.enabled"
14255 def missingPropUseCountersForDescriptor(desc):
14256 if not desc.needsMissingPropUseCounters:
14257 return ""
14259 return fill(
14261 if (StaticPrefs::${pref}() && JSID_IS_ATOM(id)) {
14262 CountMaybeMissingProperty(proxy, id);
14265 """,
14266 pref=prefIdentifier(MISSING_PROP_PREF),
14270 def findAncestorWithInstrumentedProps(desc):
14272 Find an ancestor of desc.interface (not including desc.interface
14273 itself) that has instrumented properties on it. May return None
14274 if there is no such ancestor.
14276 ancestor = desc.interface.parent
14277 while ancestor:
14278 if ancestor.getExtendedAttribute("InstrumentedProps"):
14279 return ancestor
14280 ancestor = ancestor.parent
14281 return None
14284 class CGCountMaybeMissingProperty(CGAbstractMethod):
14285 def __init__(self, descriptor):
14287 Returns whether we counted the property involved.
14289 CGAbstractMethod.__init__(
14290 self,
14291 descriptor,
14292 "CountMaybeMissingProperty",
14293 "bool",
14295 Argument("JS::Handle<JSObject*>", "proxy"),
14296 Argument("JS::Handle<jsid>", "id"),
14300 def gen_switch(self, switchDecriptor):
14302 Generate a switch from the switch descriptor. The descriptor
14303 dictionary must have the following properties:
14305 1) A "precondition" property that contains code to run before the
14306 switch statement. Its value ie a string.
14307 2) A "condition" property for the condition. Its value is a string.
14308 3) A "cases" property. Its value is an object that has property names
14309 corresponding to the case labels. The values of those properties
14310 are either new switch descriptor dictionaries (which will then
14311 generate nested switches) or strings to use for case bodies.
14313 cases = []
14314 for label, body in sorted(six.iteritems(switchDecriptor["cases"])):
14315 if isinstance(body, dict):
14316 body = self.gen_switch(body)
14317 cases.append(
14318 fill(
14320 case ${label}: {
14321 $*{body}
14322 break;
14324 """,
14325 label=label,
14326 body=body,
14329 return fill(
14331 $*{precondition}
14332 switch (${condition}) {
14333 $*{cases}
14335 """,
14336 precondition=switchDecriptor["precondition"],
14337 condition=switchDecriptor["condition"],
14338 cases="".join(cases),
14341 def charSwitch(self, props, charIndex):
14343 Create a switch for the given props, based on the first char where
14344 they start to differ at index charIndex or more. Each prop is a tuple
14345 containing interface name and prop name.
14347 Incoming props should be a sorted list.
14349 if len(props) == 1:
14350 # We're down to one string: just check whether we match it.
14351 return fill(
14353 if (JS_LinearStringEqualsLiteral(str, "${name}")) {
14354 counter.emplace(eUseCounter_${iface}_${name});
14356 """,
14357 iface=self.descriptor.name,
14358 name=props[0],
14361 switch = dict()
14362 if charIndex == 0:
14363 switch["precondition"] = "StringIdChars chars(nogc, str);\n"
14364 else:
14365 switch["precondition"] = ""
14367 # Find the first place where we might actually have a difference.
14368 while all(prop[charIndex] == props[0][charIndex] for prop in props):
14369 charIndex += 1
14371 switch["condition"] = "chars[%d]" % charIndex
14372 switch["cases"] = dict()
14373 current_props = None
14374 curChar = None
14375 idx = 0
14376 while idx < len(props):
14377 nextChar = "'%s'" % props[idx][charIndex]
14378 if nextChar != curChar:
14379 if curChar:
14380 switch["cases"][curChar] = self.charSwitch(
14381 current_props, charIndex + 1
14383 current_props = []
14384 curChar = nextChar
14385 current_props.append(props[idx])
14386 idx += 1
14387 switch["cases"][curChar] = self.charSwitch(current_props, charIndex + 1)
14388 return switch
14390 def definition_body(self):
14391 ancestor = findAncestorWithInstrumentedProps(self.descriptor)
14393 if ancestor:
14394 body = fill(
14396 if (${ancestor}_Binding::CountMaybeMissingProperty(proxy, id)) {
14397 return true;
14400 """,
14401 ancestor=ancestor.identifier.name,
14403 else:
14404 body = ""
14406 instrumentedProps = self.descriptor.instrumentedProps
14407 if not instrumentedProps:
14408 return body + dedent(
14410 return false;
14414 lengths = set(len(prop) for prop in instrumentedProps)
14415 switchDesc = {"condition": "JS::GetLinearStringLength(str)", "precondition": ""}
14416 switchDesc["cases"] = dict()
14417 for length in sorted(lengths):
14418 switchDesc["cases"][str(length)] = self.charSwitch(
14419 list(sorted(prop for prop in instrumentedProps if len(prop) == length)),
14423 return body + fill(
14425 MOZ_ASSERT(StaticPrefs::${pref}() && JSID_IS_ATOM(id));
14426 Maybe<UseCounter> counter;
14428 // Scope for our no-GC section, so we don't need to rely on SetUseCounter not GCing.
14429 JS::AutoCheckCannotGC nogc;
14430 JSLinearString* str = JS::AtomToLinearString(JSID_TO_ATOM(id));
14431 // Don't waste time fetching the chars until we've done the length switch.
14432 $*{switch}
14434 if (counter) {
14435 SetUseCounter(proxy, *counter);
14436 return true;
14439 return false;
14440 """,
14441 pref=prefIdentifier(MISSING_PROP_PREF),
14442 switch=self.gen_switch(switchDesc),
14446 class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod):
14447 def __init__(self, descriptor):
14448 args = [
14449 Argument("JSContext*", "cx"),
14450 Argument("JS::Handle<JSObject*>", "proxy"),
14451 Argument("JS::Handle<jsid>", "id"),
14452 Argument("bool", "ignoreNamedProps"),
14453 Argument("JS::MutableHandle<JS::PropertyDescriptor>", "desc"),
14455 ClassMethod.__init__(
14456 self,
14457 "getOwnPropDescriptor",
14458 "bool",
14459 args,
14460 virtual=True,
14461 override=True,
14462 const=True,
14464 self.descriptor = descriptor
14466 def getBody(self):
14467 indexedGetter = self.descriptor.operations["IndexedGetter"]
14468 indexedSetter = self.descriptor.operations["IndexedSetter"]
14470 if self.descriptor.isMaybeCrossOriginObject():
14471 xrayDecl = dedent(
14473 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
14474 MOZ_ASSERT(IsPlatformObjectSameOrigin(cx, proxy),
14475 "getOwnPropertyDescriptor() and set() should have dealt");
14476 MOZ_ASSERT(js::IsObjectInContextCompartment(proxy, cx),
14477 "getOwnPropertyDescriptor() and set() should have dealt");
14481 xrayCheck = ""
14482 else:
14483 xrayDecl = "bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);\n"
14484 xrayCheck = "!isXray &&"
14486 if self.descriptor.supportsIndexedProperties():
14487 readonly = toStringBool(indexedSetter is None)
14488 fillDescriptor = (
14489 "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;\n" % readonly
14491 templateValues = {
14492 "jsvalRef": "desc.value()",
14493 "jsvalHandle": "desc.value()",
14494 "obj": "proxy",
14495 "successCode": fillDescriptor,
14497 getIndexed = fill(
14499 uint32_t index = GetArrayIndexFromId(id);
14500 if (IsArrayIndex(index)) {
14501 $*{callGetter}
14504 """,
14505 callGetter=CGProxyIndexedGetter(
14506 self.descriptor, templateValues
14507 ).define(),
14509 else:
14510 getIndexed = ""
14512 missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor)
14514 if self.descriptor.supportsNamedProperties():
14515 operations = self.descriptor.operations
14516 readonly = toStringBool(operations["NamedSetter"] is None)
14517 fillDescriptor = (
14518 "FillPropertyDescriptor(desc, proxy, %s, %s);\n"
14519 "return true;\n"
14520 % (readonly, toStringBool(self.descriptor.namedPropertiesEnumerable))
14522 templateValues = {
14523 "jsvalRef": "desc.value()",
14524 "jsvalHandle": "desc.value()",
14525 "obj": "proxy",
14526 "successCode": fillDescriptor,
14529 computeCondition = dedent(
14531 bool hasOnProto;
14532 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
14533 return false;
14535 callNamedGetter = !hasOnProto;
14538 if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
14539 computeCondition = fill(
14541 if (!isXray) {
14542 callNamedGetter = true;
14543 } else {
14544 $*{hasOnProto}
14546 """,
14547 hasOnProto=computeCondition,
14550 outerCondition = "!ignoreNamedProps"
14551 if self.descriptor.supportsIndexedProperties():
14552 outerCondition = "!IsArrayIndex(index) && " + outerCondition
14554 namedGetCode = CGProxyNamedGetter(self.descriptor, templateValues).define()
14555 namedGet = fill(
14557 bool callNamedGetter = false;
14558 if (${outerCondition}) {
14559 $*{computeCondition}
14561 if (callNamedGetter) {
14562 $*{namedGetCode}
14564 """,
14565 outerCondition=outerCondition,
14566 computeCondition=computeCondition,
14567 namedGetCode=namedGetCode,
14569 namedGet += "\n"
14570 else:
14571 namedGet = ""
14573 return fill(
14575 $*{xrayDecl}
14576 $*{getIndexed}
14577 $*{missingPropUseCounters}
14578 JS::Rooted<JSObject*> expando(cx);
14579 if (${xrayCheck}(expando = GetExpandoObject(proxy))) {
14580 if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc)) {
14581 return false;
14583 if (desc.object()) {
14584 // Pretend the property lives on the wrapper.
14585 desc.object().set(proxy);
14586 return true;
14590 $*{namedGet}
14591 desc.object().set(nullptr);
14592 return true;
14593 """,
14594 xrayDecl=xrayDecl,
14595 xrayCheck=xrayCheck,
14596 getIndexed=getIndexed,
14597 missingPropUseCounters=missingPropUseCounters,
14598 namedGet=namedGet,
14602 class CGDOMJSProxyHandler_defineProperty(ClassMethod):
14603 def __init__(self, descriptor):
14604 # The usual convention is to name the ObjectOpResult out-parameter
14605 # `result`, but that name is a bit overloaded around here.
14606 args = [
14607 Argument("JSContext*", "cx_"),
14608 Argument("JS::Handle<JSObject*>", "proxy"),
14609 Argument("JS::Handle<jsid>", "id"),
14610 Argument("JS::Handle<JS::PropertyDescriptor>", "desc"),
14611 Argument("JS::ObjectOpResult&", "opresult"),
14612 Argument("bool*", "done"),
14614 ClassMethod.__init__(
14615 self,
14616 "defineProperty",
14617 "bool",
14618 args,
14619 virtual=True,
14620 override=True,
14621 const=True,
14623 self.descriptor = descriptor
14625 def getBody(self):
14626 set = ""
14628 indexedSetter = self.descriptor.operations["IndexedSetter"]
14629 if indexedSetter:
14630 error_label = CGSpecializedMethod.error_reporting_label_helper(
14631 self.descriptor, indexedSetter, isConstructor=False
14633 if error_label:
14634 cxDecl = fill(
14636 BindingCallContext cx(cx_, "${error_label}");
14637 """,
14638 error_label=error_label,
14640 else:
14641 cxDecl = dedent(
14643 JSContext* cx = cx_;
14646 set += fill(
14648 uint32_t index = GetArrayIndexFromId(id);
14649 if (IsArrayIndex(index)) {
14650 $*{cxDecl}
14651 *done = true;
14652 // https://heycam.github.io/webidl/#legacy-platform-object-defineownproperty
14653 // Step 1.1. The no-indexed-setter case is handled by step 1.2.
14654 if (!desc.isDataDescriptor()) {
14655 return opresult.failNotDataDescriptor();
14658 $*{callSetter}
14659 return opresult.succeed();
14661 """,
14662 cxDecl=cxDecl,
14663 callSetter=CGProxyIndexedSetter(self.descriptor).define(),
14665 elif self.descriptor.supportsIndexedProperties():
14666 # We allow untrusted content to prevent Xrays from setting a
14667 # property if that property is an indexed property and we have no
14668 # indexed setter. That's how the object would normally behave if
14669 # you tried to set the property on it. That means we don't need to
14670 # do anything special for Xrays here.
14671 set += dedent(
14673 if (IsArrayIndex(GetArrayIndexFromId(id))) {
14674 *done = true;
14675 return opresult.failNoIndexedSetter();
14680 namedSetter = self.descriptor.operations["NamedSetter"]
14681 if namedSetter:
14682 error_label = CGSpecializedMethod.error_reporting_label_helper(
14683 self.descriptor, namedSetter, isConstructor=False
14685 if error_label:
14686 set += fill(
14688 BindingCallContext cx(cx_, "${error_label}");
14689 """,
14690 error_label=error_label,
14692 else:
14693 set += dedent(
14695 JSContext* cx = cx_;
14698 if self.descriptor.hasLegacyUnforgeableMembers:
14699 raise TypeError(
14700 "Can't handle a named setter on an interface "
14701 "that has unforgeables. Figure out how that "
14702 "should work!"
14704 tailCode = dedent(
14706 *done = true;
14707 return opresult.succeed();
14710 set += CGProxyNamedSetter(self.descriptor, tailCode).define()
14711 else:
14712 # We allow untrusted content to prevent Xrays from setting a
14713 # property if that property is already a named property on the
14714 # object and we have no named setter. That's how the object would
14715 # normally behave if you tried to set the property on it. That
14716 # means we don't need to do anything special for Xrays here.
14717 if self.descriptor.supportsNamedProperties():
14718 set += fill(
14720 JSContext* cx = cx_;
14721 bool found = false;
14722 $*{presenceChecker}
14724 if (found) {
14725 *done = true;
14726 return opresult.failNoNamedSetter();
14728 """,
14729 presenceChecker=CGProxyNamedPresenceChecker(
14730 self.descriptor, foundVar="found"
14731 ).define(),
14733 if self.descriptor.isMaybeCrossOriginObject():
14734 set += dedent(
14736 MOZ_ASSERT(IsPlatformObjectSameOrigin(cx_, proxy),
14737 "Why did the MaybeCrossOriginObject defineProperty override fail?");
14738 MOZ_ASSERT(js::IsObjectInContextCompartment(proxy, cx_),
14739 "Why did the MaybeCrossOriginObject defineProperty override fail?");
14743 # In all cases we want to tail-call to our base class; we can
14744 # always land here for symbols.
14745 set += (
14746 "return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n"
14747 % ", ".join(a.name for a in self.args)
14749 return set
14752 def getDeleterBody(descriptor, type, foundVar=None):
14754 type should be "Named" or "Indexed"
14756 The possible outcomes:
14757 - an error happened (the emitted code returns false)
14758 - own property not found (foundVar=false, deleteSucceeded=true)
14759 - own property found and deleted (foundVar=true, deleteSucceeded=true)
14760 - own property found but can't be deleted (foundVar=true, deleteSucceeded=false)
14762 assert type in ("Named", "Indexed")
14763 deleter = descriptor.operations[type + "Deleter"]
14764 if deleter:
14765 assert type == "Named"
14766 assert foundVar is not None
14767 if descriptor.hasLegacyUnforgeableMembers:
14768 raise TypeError(
14769 "Can't handle a deleter on an interface "
14770 "that has unforgeables. Figure out how "
14771 "that should work!"
14773 # See if the deleter method is fallible.
14774 t = deleter.signatures()[0][0]
14775 if t.isPrimitive() and not t.nullable() and t.tag() == IDLType.Tags.bool:
14776 # The deleter method has a boolean return value. When a
14777 # property is found, the return value indicates whether it
14778 # was successfully deleted.
14779 setDS = fill(
14781 if (!${foundVar}) {
14782 deleteSucceeded = true;
14784 """,
14785 foundVar=foundVar,
14787 else:
14788 # No boolean return value: if a property is found,
14789 # deleting it always succeeds.
14790 setDS = "deleteSucceeded = true;\n"
14792 body = (
14793 CGProxyNamedDeleter(
14794 descriptor, resultVar="deleteSucceeded", foundVar=foundVar
14795 ).define()
14796 + setDS
14798 elif getattr(descriptor, "supports%sProperties" % type)():
14799 presenceCheckerClass = globals()["CGProxy%sPresenceChecker" % type]
14800 foundDecl = ""
14801 if foundVar is None:
14802 foundVar = "found"
14803 foundDecl = "bool found = false;\n"
14804 body = fill(
14806 $*{foundDecl}
14807 $*{presenceChecker}
14808 deleteSucceeded = !${foundVar};
14809 """,
14810 foundDecl=foundDecl,
14811 presenceChecker=presenceCheckerClass(
14812 descriptor, foundVar=foundVar
14813 ).define(),
14814 foundVar=foundVar,
14816 else:
14817 body = None
14818 return body
14821 class CGDeleteNamedProperty(CGAbstractStaticMethod):
14822 def __init__(self, descriptor):
14823 args = [
14824 Argument("JSContext*", "cx"),
14825 Argument("JS::Handle<JSObject*>", "xray"),
14826 Argument("JS::Handle<JSObject*>", "proxy"),
14827 Argument("JS::Handle<jsid>", "id"),
14828 Argument("JS::ObjectOpResult&", "opresult"),
14830 CGAbstractStaticMethod.__init__(
14831 self, descriptor, "DeleteNamedProperty", "bool", args
14834 def definition_body(self):
14835 return fill(
14837 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(xray));
14838 MOZ_ASSERT(js::IsProxy(proxy));
14839 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
14840 JSAutoRealm ar(cx, proxy);
14841 bool deleteSucceeded = false;
14842 bool found = false;
14843 $*{namedBody}
14844 if (!found || deleteSucceeded) {
14845 return opresult.succeed();
14847 return opresult.failCantDelete();
14848 """,
14849 namedBody=getDeleterBody(self.descriptor, "Named", foundVar="found"),
14853 class CGDOMJSProxyHandler_delete(ClassMethod):
14854 def __init__(self, descriptor):
14855 args = [
14856 Argument("JSContext*", "cx"),
14857 Argument("JS::Handle<JSObject*>", "proxy"),
14858 Argument("JS::Handle<jsid>", "id"),
14859 Argument("JS::ObjectOpResult&", "opresult"),
14861 ClassMethod.__init__(
14862 self, "delete_", "bool", args, virtual=True, override=True, const=True
14864 self.descriptor = descriptor
14866 def getBody(self):
14867 delete = dedent(
14869 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
14870 "Should not have a XrayWrapper here");
14875 if self.descriptor.isMaybeCrossOriginObject():
14876 delete += dedent(
14878 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
14879 return ReportCrossOriginDenial(cx, id, "delete"_ns);
14882 // Safe to enter the Realm of proxy now.
14883 JSAutoRealm ar(cx, proxy);
14884 JS_MarkCrossZoneId(cx, id);
14888 indexedBody = getDeleterBody(self.descriptor, "Indexed")
14889 if indexedBody is not None:
14890 # Can't handle cross-origin objects here.
14891 assert not self.descriptor.isMaybeCrossOriginObject()
14892 delete += fill(
14894 uint32_t index = GetArrayIndexFromId(id);
14895 if (IsArrayIndex(index)) {
14896 bool deleteSucceeded;
14897 $*{indexedBody}
14898 return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
14900 """,
14901 indexedBody=indexedBody,
14904 namedBody = getDeleterBody(self.descriptor, "Named", foundVar="found")
14905 if namedBody is not None:
14906 delete += dedent(
14908 // Try named delete only if the named property visibility
14909 // algorithm says the property is visible.
14910 bool tryNamedDelete = true;
14911 { // Scope for expando
14912 JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
14913 if (expando) {
14914 bool hasProp;
14915 if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
14916 return false;
14918 tryNamedDelete = !hasProp;
14924 if not self.descriptor.interface.getExtendedAttribute(
14925 "LegacyOverrideBuiltIns"
14927 delete += dedent(
14929 if (tryNamedDelete) {
14930 bool hasOnProto;
14931 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
14932 return false;
14934 tryNamedDelete = !hasOnProto;
14939 # We always return above for an index id in the case when we support
14940 # indexed properties, so we can just treat the id as a name
14941 # unconditionally here.
14942 delete += fill(
14944 if (tryNamedDelete) {
14945 bool found = false;
14946 bool deleteSucceeded;
14947 $*{namedBody}
14948 if (found) {
14949 return deleteSucceeded ? opresult.succeed() : opresult.failCantDelete();
14952 """,
14953 namedBody=namedBody,
14956 delete += dedent(
14959 return dom::DOMProxyHandler::delete_(cx, proxy, id, opresult);
14963 return delete
14966 class CGDOMJSProxyHandler_ownPropNames(ClassMethod):
14967 def __init__(
14968 self,
14969 descriptor,
14971 args = [
14972 Argument("JSContext*", "cx"),
14973 Argument("JS::Handle<JSObject*>", "proxy"),
14974 Argument("unsigned", "flags"),
14975 Argument("JS::MutableHandleVector<jsid>", "props"),
14977 ClassMethod.__init__(
14978 self, "ownPropNames", "bool", args, virtual=True, override=True, const=True
14980 self.descriptor = descriptor
14982 def getBody(self):
14983 if self.descriptor.isMaybeCrossOriginObject():
14984 xrayDecl = dedent(
14986 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
14987 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
14988 if (!(flags & JSITER_HIDDEN)) {
14989 // There are no enumerable cross-origin props, so we're done.
14990 return true;
14993 JS::Rooted<JSObject*> holder(cx);
14994 if (!EnsureHolder(cx, proxy, &holder)) {
14995 return false;
14998 if (!js::GetPropertyKeys(cx, holder, flags, props)) {
14999 return false;
15002 return xpc::AppendCrossOriginWhitelistedPropNames(cx, props);
15007 xrayCheck = ""
15008 else:
15009 xrayDecl = "bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);\n"
15010 xrayCheck = "!isXray &&"
15012 # Per spec, we do indices, then named props, then everything else.
15013 if self.descriptor.supportsIndexedProperties():
15014 if self.descriptor.lengthNeedsCallerType():
15015 callerType = callerTypeGetterForDescriptor(self.descriptor)
15016 else:
15017 callerType = ""
15018 addIndices = fill(
15021 uint32_t length = UnwrapProxy(proxy)->Length(${callerType});
15022 MOZ_ASSERT(int32_t(length) >= 0);
15023 for (int32_t i = 0; i < int32_t(length); ++i) {
15024 if (!props.append(INT_TO_JSID(i))) {
15025 return false;
15028 """,
15029 callerType=callerType,
15031 else:
15032 addIndices = ""
15034 if self.descriptor.supportsNamedProperties():
15035 if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
15036 shadow = "!isXray"
15037 else:
15038 shadow = "false"
15040 if self.descriptor.supportedNamesNeedCallerType():
15041 callerType = ", " + callerTypeGetterForDescriptor(self.descriptor)
15042 else:
15043 callerType = ""
15045 addNames = fill(
15047 nsTArray<nsString> names;
15048 UnwrapProxy(proxy)->GetSupportedNames(names${callerType});
15049 if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) {
15050 return false;
15052 """,
15053 callerType=callerType,
15054 shadow=shadow,
15056 if not self.descriptor.namedPropertiesEnumerable:
15057 addNames = CGIfWrapper(
15058 CGGeneric(addNames), "flags & JSITER_HIDDEN"
15059 ).define()
15060 addNames = "\n" + addNames
15061 else:
15062 addNames = ""
15064 addExpandoProps = fill(
15066 JS::Rooted<JSObject*> expando(cx);
15067 if (${xrayCheck}(expando = DOMProxyHandler::GetExpandoObject(proxy)) &&
15068 !js::GetPropertyKeys(cx, expando, flags, props)) {
15069 return false;
15071 """,
15072 xrayCheck=xrayCheck,
15075 if self.descriptor.isMaybeCrossOriginObject():
15076 # We need to enter our compartment (which we might not be
15077 # in right now) to get the expando props.
15078 addExpandoProps = fill(
15080 { // Scope for accessing the expando.
15081 // Safe to enter our compartment, because IsPlatformObjectSameOrigin tested true.
15082 JSAutoRealm ar(cx, proxy);
15083 $*{addExpandoProps}
15085 for (auto& id : props) {
15086 JS_MarkCrossZoneId(cx, id);
15088 """,
15089 addExpandoProps=addExpandoProps,
15092 return fill(
15094 $*{xrayDecl}
15095 $*{addIndices}
15096 $*{addNames}
15098 $*{addExpandoProps}
15100 return true;
15101 """,
15102 xrayDecl=xrayDecl,
15103 addIndices=addIndices,
15104 addNames=addNames,
15105 addExpandoProps=addExpandoProps,
15109 class CGDOMJSProxyHandler_hasOwn(ClassMethod):
15110 def __init__(self, descriptor):
15111 args = [
15112 Argument("JSContext*", "cx"),
15113 Argument("JS::Handle<JSObject*>", "proxy"),
15114 Argument("JS::Handle<jsid>", "id"),
15115 Argument("bool*", "bp"),
15117 ClassMethod.__init__(
15118 self, "hasOwn", "bool", args, virtual=True, override=True, const=True
15120 self.descriptor = descriptor
15122 def getBody(self):
15123 if self.descriptor.isMaybeCrossOriginObject():
15124 maybeCrossOrigin = dedent(
15126 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
15127 // Just hand this off to BaseProxyHandler to do the slow-path thing.
15128 // The BaseProxyHandler code is OK with this happening without entering the
15129 // compartment of "proxy", which is important to get the right answers.
15130 return js::BaseProxyHandler::hasOwn(cx, proxy, id, bp);
15133 // Now safe to enter the Realm of proxy and do the rest of the work there.
15134 JSAutoRealm ar(cx, proxy);
15135 JS_MarkCrossZoneId(cx, id);
15138 else:
15139 maybeCrossOrigin = ""
15141 if self.descriptor.supportsIndexedProperties():
15142 indexed = fill(
15144 uint32_t index = GetArrayIndexFromId(id);
15145 if (IsArrayIndex(index)) {
15146 bool found = false;
15147 $*{presenceChecker}
15149 *bp = found;
15150 return true;
15153 """,
15154 presenceChecker=CGProxyIndexedPresenceChecker(
15155 self.descriptor, foundVar="found"
15156 ).define(),
15158 else:
15159 indexed = ""
15161 if self.descriptor.supportsNamedProperties():
15162 # If we support indexed properties we always return above for index
15163 # property names, so no need to check for those here.
15164 named = fill(
15166 bool found = false;
15167 $*{presenceChecker}
15169 *bp = found;
15170 """,
15171 presenceChecker=CGProxyNamedPresenceChecker(
15172 self.descriptor, foundVar="found"
15173 ).define(),
15175 if not self.descriptor.interface.getExtendedAttribute(
15176 "LegacyOverrideBuiltIns"
15178 named = fill(
15180 bool hasOnProto;
15181 if (!HasPropertyOnPrototype(cx, proxy, id, &hasOnProto)) {
15182 return false;
15184 if (!hasOnProto) {
15185 $*{protoLacksProperty}
15186 return true;
15188 """,
15189 protoLacksProperty=named,
15191 named += "*bp = false;\n"
15192 else:
15193 named += "\n"
15194 else:
15195 named = "*bp = false;\n"
15197 missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor)
15199 return fill(
15201 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
15202 "Should not have a XrayWrapper here");
15203 $*{maybeCrossOrigin}
15204 $*{indexed}
15206 $*{missingPropUseCounters}
15207 JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy));
15208 if (expando) {
15209 bool b = true;
15210 bool ok = JS_HasPropertyById(cx, expando, id, &b);
15211 *bp = !!b;
15212 if (!ok || *bp) {
15213 return ok;
15217 $*{named}
15218 return true;
15219 """,
15220 maybeCrossOrigin=maybeCrossOrigin,
15221 indexed=indexed,
15222 missingPropUseCounters=missingPropUseCounters,
15223 named=named,
15227 class CGDOMJSProxyHandler_get(ClassMethod):
15228 def __init__(self, descriptor):
15229 args = [
15230 Argument("JSContext*", "cx"),
15231 Argument("JS::Handle<JSObject*>", "proxy"),
15232 Argument("JS::Handle<JS::Value>", "receiver"),
15233 Argument("JS::Handle<jsid>", "id"),
15234 Argument("JS::MutableHandle<JS::Value>", "vp"),
15236 ClassMethod.__init__(
15237 self, "get", "bool", args, virtual=True, override=True, const=True
15239 self.descriptor = descriptor
15241 def getBody(self):
15242 if self.descriptor.isMaybeCrossOriginObject():
15243 maybeCrossOriginGet = dedent(
15245 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
15246 return CrossOriginGet(cx, proxy, receiver, id, vp);
15251 else:
15252 maybeCrossOriginGet = ""
15254 missingPropUseCounters = missingPropUseCountersForDescriptor(self.descriptor)
15256 getUnforgeableOrExpando = dedent(
15258 { // Scope for expando
15259 JS::Rooted<JSObject*> expando(cx, DOMProxyHandler::GetExpandoObject(proxy));
15260 if (expando) {
15261 if (!JS_HasPropertyById(cx, expando, id, &expandoHasProp)) {
15262 return false;
15265 if (expandoHasProp) {
15266 // Forward the get to the expando object, but our receiver is whatever our
15267 // receiver is.
15268 if (!JS_ForwardGetPropertyTo(cx, expando, id, rootedReceiver, vp)) {
15269 return false;
15277 if self.descriptor.isMaybeCrossOriginObject():
15278 getUnforgeableOrExpando = fill(
15280 { // Scope for the JSAutoRealm accessing expando
15281 JSAutoRealm ar(cx, proxy);
15282 if (!MaybeWrapValue(cx, &rootedReceiver)) {
15283 return false;
15285 JS_MarkCrossZoneId(cx, id);
15287 $*{getUnforgeableOrExpando}
15289 if (expandoHasProp) {
15290 return MaybeWrapValue(cx, vp);
15292 """,
15293 getUnforgeableOrExpando=getUnforgeableOrExpando,
15295 else:
15296 getUnforgeableOrExpando = fill(
15298 $*{getUnforgeableOrExpando}
15300 if (expandoHasProp) {
15301 return true;
15303 """,
15304 getUnforgeableOrExpando=getUnforgeableOrExpando,
15307 getUnforgeableOrExpando = fill(
15309 bool expandoHasProp = false;
15310 $*{getUnforgeableOrExpando}
15311 """,
15312 getUnforgeableOrExpando=getUnforgeableOrExpando,
15315 templateValues = {"jsvalRef": "vp", "jsvalHandle": "vp", "obj": "proxy"}
15317 if self.descriptor.supportsIndexedProperties():
15318 # We can't handle this for cross-origin objects
15319 assert not self.descriptor.isMaybeCrossOriginObject()
15321 getIndexedOrExpando = fill(
15323 uint32_t index = GetArrayIndexFromId(id);
15324 if (IsArrayIndex(index)) {
15325 $*{callGetter}
15326 // Even if we don't have this index, we don't forward the
15327 // get on to our expando object.
15328 } else {
15329 $*{getUnforgeableOrExpando}
15331 """,
15332 callGetter=CGProxyIndexedGetter(
15333 self.descriptor, templateValues
15334 ).define(),
15335 getUnforgeableOrExpando=getUnforgeableOrExpando,
15337 else:
15338 getIndexedOrExpando = getUnforgeableOrExpando
15340 if self.descriptor.supportsNamedProperties():
15341 # We can't handle this for cross-origin objects
15342 assert not self.descriptor.isMaybeCrossOriginObject()
15344 getNamed = CGProxyNamedGetter(self.descriptor, templateValues)
15345 if self.descriptor.supportsIndexedProperties():
15346 getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)")
15347 getNamed = getNamed.define() + "\n"
15348 else:
15349 getNamed = ""
15351 getOnPrototype = dedent(
15353 if (!GetPropertyOnPrototype(cx, proxy, rootedReceiver, id, &foundOnPrototype, vp)) {
15354 return false;
15360 if self.descriptor.isMaybeCrossOriginObject():
15361 getOnPrototype = fill(
15363 bool foundOnPrototype;
15364 { // Scope for JSAutoRealm
15365 JSAutoRealm ar(cx, proxy);
15366 // We already wrapped rootedReceiver
15367 MOZ_ASSERT_IF(rootedReceiver.isObject(),
15368 js::IsObjectInContextCompartment(&rootedReceiver.toObject(), cx));
15369 JS_MarkCrossZoneId(cx, id);
15370 $*{getOnPrototype}
15373 if (foundOnPrototype) {
15374 return MaybeWrapValue(cx, vp);
15377 """,
15378 getOnPrototype=getOnPrototype,
15380 else:
15381 getOnPrototype = fill(
15383 bool foundOnPrototype;
15384 $*{getOnPrototype}
15385 if (foundOnPrototype) {
15386 return true;
15389 """,
15390 getOnPrototype=getOnPrototype,
15393 if self.descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
15394 getNamed = getNamed + getOnPrototype
15395 else:
15396 getNamed = getOnPrototype + getNamed
15398 return fill(
15400 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
15401 "Should not have a XrayWrapper here");
15403 $*{maybeCrossOriginGet}
15404 JS::Rooted<JS::Value> rootedReceiver(cx, receiver);
15406 $*{missingPropUseCounters}
15407 $*{indexedOrExpando}
15409 $*{named}
15410 vp.setUndefined();
15411 return true;
15412 """,
15413 maybeCrossOriginGet=maybeCrossOriginGet,
15414 missingPropUseCounters=missingPropUseCounters,
15415 indexedOrExpando=getIndexedOrExpando,
15416 named=getNamed,
15420 class CGDOMJSProxyHandler_setCustom(ClassMethod):
15421 def __init__(self, descriptor):
15422 args = [
15423 Argument("JSContext*", "cx_"),
15424 Argument("JS::Handle<JSObject*>", "proxy"),
15425 Argument("JS::Handle<jsid>", "id"),
15426 Argument("JS::Handle<JS::Value>", "v"),
15427 Argument("bool*", "done"),
15429 ClassMethod.__init__(
15430 self, "setCustom", "bool", args, virtual=True, override=True, const=True
15432 self.descriptor = descriptor
15434 def getBody(self):
15435 assertion = (
15436 "MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),\n"
15437 ' "Should not have a XrayWrapper here");\n'
15440 # Correctness first. If we have a NamedSetter and [LegacyOverrideBuiltIns],
15441 # always call the NamedSetter and never do anything else.
15442 namedSetter = self.descriptor.operations["NamedSetter"]
15443 if namedSetter is not None and self.descriptor.interface.getExtendedAttribute(
15444 "LegacyOverrideBuiltIns"
15446 # Check assumptions.
15447 if self.descriptor.supportsIndexedProperties():
15448 raise ValueError(
15449 "In interface "
15450 + self.descriptor.name
15451 + ": "
15452 + "Can't cope with [LegacyOverrideBuiltIns] and an indexed getter"
15454 if self.descriptor.hasLegacyUnforgeableMembers:
15455 raise ValueError(
15456 "In interface "
15457 + self.descriptor.name
15458 + ": "
15459 + "Can't cope with [LegacyOverrideBuiltIns] and unforgeable members"
15462 tailCode = dedent(
15464 *done = true;
15465 return true;
15468 callSetter = CGProxyNamedSetter(
15469 self.descriptor, tailCode, argumentHandleValue="v"
15471 error_label = CGSpecializedMethod.error_reporting_label_helper(
15472 self.descriptor, namedSetter, isConstructor=False
15474 if error_label:
15475 cxDecl = fill(
15477 BindingCallContext cx(cx_, "${error_label}");
15478 """,
15479 error_label=error_label,
15481 else:
15482 cxDecl = dedent(
15484 JSContext* cx = cx_;
15487 return fill(
15489 $*{assertion}
15490 $*{cxDecl}
15491 $*{callSetter}
15492 *done = false;
15493 return true;
15494 """,
15495 assertion=assertion,
15496 cxDecl=cxDecl,
15497 callSetter=callSetter.define(),
15500 # As an optimization, if we are going to call an IndexedSetter, go
15501 # ahead and call it and have done.
15502 indexedSetter = self.descriptor.operations["IndexedSetter"]
15503 if indexedSetter is not None:
15504 error_label = CGSpecializedMethod.error_reporting_label_helper(
15505 self.descriptor, indexedSetter, isConstructor=False
15507 if error_label:
15508 cxDecl = fill(
15510 BindingCallContext cx(cx_, "${error_label}");
15511 """,
15512 error_label=error_label,
15514 else:
15515 cxDecl = dedent(
15517 JSContext* cx = cx_;
15520 setIndexed = fill(
15522 uint32_t index = GetArrayIndexFromId(id);
15523 if (IsArrayIndex(index)) {
15524 $*{cxDecl}
15525 $*{callSetter}
15526 *done = true;
15527 return true;
15530 """,
15531 cxDecl=cxDecl,
15532 callSetter=CGProxyIndexedSetter(
15533 self.descriptor, argumentHandleValue="v"
15534 ).define(),
15536 else:
15537 setIndexed = ""
15539 return assertion + setIndexed + "*done = false;\n" "return true;\n"
15542 class CGDOMJSProxyHandler_className(ClassMethod):
15543 def __init__(self, descriptor):
15544 args = [
15545 Argument("JSContext*", "cx"),
15546 Argument("JS::Handle<JSObject*>", "proxy"),
15548 ClassMethod.__init__(
15549 self,
15550 "className",
15551 "const char*",
15552 args,
15553 virtual=True,
15554 override=True,
15555 const=True,
15557 self.descriptor = descriptor
15559 def getBody(self):
15560 if self.descriptor.isMaybeCrossOriginObject():
15561 crossOrigin = dedent(
15563 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
15564 return "Object";
15569 else:
15570 crossOrigin = ""
15571 return fill(
15573 $*{crossOrigin}
15574 return "${name}";
15575 """,
15576 crossOrigin=crossOrigin,
15577 name=self.descriptor.name,
15581 class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod):
15582 def __init__(self, descriptor):
15583 args = [Argument("const JS::Value&", "priv")]
15584 ClassMethod.__init__(
15585 self,
15586 "finalizeInBackground",
15587 "bool",
15588 args,
15589 virtual=True,
15590 override=True,
15591 const=True,
15593 self.descriptor = descriptor
15595 def getBody(self):
15596 return "return false;\n"
15599 class CGDOMJSProxyHandler_finalize(ClassMethod):
15600 def __init__(self, descriptor):
15601 args = [Argument("JSFreeOp*", "fop"), Argument("JSObject*", "proxy")]
15602 ClassMethod.__init__(
15603 self, "finalize", "void", args, virtual=True, override=True, const=True
15605 self.descriptor = descriptor
15607 def getBody(self):
15608 return (
15609 "%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(proxy);\n"
15610 % (self.descriptor.nativeType, self.descriptor.nativeType)
15611 ) + finalizeHook(
15612 self.descriptor,
15613 FINALIZE_HOOK_NAME,
15614 self.args[0].name,
15615 self.args[1].name,
15616 ).define()
15619 class CGDOMJSProxyHandler_objectMoved(ClassMethod):
15620 def __init__(self, descriptor):
15621 args = [Argument("JSObject*", "obj"), Argument("JSObject*", "old")]
15622 ClassMethod.__init__(
15623 self, "objectMoved", "size_t", args, virtual=True, override=True, const=True
15625 self.descriptor = descriptor
15627 def getBody(self):
15628 return (
15629 "%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n"
15630 % (self.descriptor.nativeType, self.descriptor.nativeType)
15631 ) + objectMovedHook(
15632 self.descriptor,
15633 OBJECT_MOVED_HOOK_NAME,
15634 self.args[0].name,
15635 self.args[1].name,
15639 class CGDOMJSProxyHandler_getElements(ClassMethod):
15640 def __init__(self, descriptor):
15641 assert descriptor.supportsIndexedProperties()
15643 args = [
15644 Argument("JSContext*", "cx"),
15645 Argument("JS::Handle<JSObject*>", "proxy"),
15646 Argument("uint32_t", "begin"),
15647 Argument("uint32_t", "end"),
15648 Argument("js::ElementAdder*", "adder"),
15650 ClassMethod.__init__(
15651 self, "getElements", "bool", args, virtual=True, override=True, const=True
15653 self.descriptor = descriptor
15655 def getBody(self):
15656 # Just like ownPropertyKeys we'll assume that we have no holes, so
15657 # we have all properties from 0 to length. If that ever changes
15658 # (unlikely), we'll need to do something a bit more clever with how we
15659 # forward on to our ancestor.
15661 templateValues = {
15662 "jsvalRef": "temp",
15663 "jsvalHandle": "&temp",
15664 "obj": "proxy",
15665 "successCode": (
15666 "if (!adder->append(cx, temp)) return false;\n" "continue;\n"
15669 get = CGProxyIndexedGetter(
15670 self.descriptor, templateValues, False, False
15671 ).define()
15673 if self.descriptor.lengthNeedsCallerType():
15674 callerType = callerTypeGetterForDescriptor(self.descriptor)
15675 else:
15676 callerType = ""
15678 return fill(
15680 JS::Rooted<JS::Value> temp(cx);
15681 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
15682 "Should not have a XrayWrapper here");
15684 ${nativeType}* self = UnwrapProxy(proxy);
15685 uint32_t length = self->Length(${callerType});
15686 // Compute the end of the indices we'll get ourselves
15687 uint32_t ourEnd = std::max(begin, std::min(end, length));
15689 for (uint32_t index = begin; index < ourEnd; ++index) {
15690 $*{get}
15693 if (end > ourEnd) {
15694 JS::Rooted<JSObject*> proto(cx);
15695 if (!js::GetObjectProto(cx, proxy, &proto)) {
15696 return false;
15698 return js::GetElementsWithAdder(cx, proto, proxy, ourEnd, end, adder);
15701 return true;
15702 """,
15703 nativeType=self.descriptor.nativeType,
15704 callerType=callerType,
15705 get=get,
15709 class CGDOMJSProxyHandler_getInstance(ClassMethod):
15710 def __init__(self):
15711 ClassMethod.__init__(
15712 self, "getInstance", "const DOMProxyHandler*", [], static=True
15715 def getBody(self):
15716 return dedent(
15718 static const DOMProxyHandler instance;
15719 return &instance;
15724 class CGDOMJSProxyHandler_call(ClassMethod):
15725 def __init__(self):
15726 args = [
15727 Argument("JSContext*", "cx"),
15728 Argument("JS::Handle<JSObject*>", "proxy"),
15729 Argument("const JS::CallArgs&", "args"),
15732 ClassMethod.__init__(
15733 self, "call", "bool", args, virtual=True, override=True, const=True
15736 def getBody(self):
15737 return fill(
15739 return js::ForwardToNative(cx, ${legacyCaller}, args);
15740 """,
15741 legacyCaller=LEGACYCALLER_HOOK_NAME,
15745 class CGDOMJSProxyHandler_isCallable(ClassMethod):
15746 def __init__(self):
15747 ClassMethod.__init__(
15748 self,
15749 "isCallable",
15750 "bool",
15751 [Argument("JSObject*", "obj")],
15752 virtual=True,
15753 override=True,
15754 const=True,
15757 def getBody(self):
15758 return dedent(
15760 return true;
15765 class CGDOMJSProxyHandler_canNurseryAllocate(ClassMethod):
15767 Override the default canNurseryAllocate in BaseProxyHandler, for cases when
15768 we should be nursery-allocated.
15771 def __init__(self):
15772 ClassMethod.__init__(
15773 self,
15774 "canNurseryAllocate",
15775 "bool",
15777 virtual=True,
15778 override=True,
15779 const=True,
15782 def getBody(self):
15783 return dedent(
15785 return true;
15790 class CGDOMJSProxyHandler_getOwnPropertyDescriptor(ClassMethod):
15792 Implementation of getOwnPropertyDescriptor. We only use this for
15793 cross-origin objects.
15796 def __init__(self, descriptor):
15797 assert descriptor.isMaybeCrossOriginObject()
15799 args = [
15800 Argument("JSContext*", "cx"),
15801 Argument("JS::Handle<JSObject*>", "proxy"),
15802 Argument("JS::Handle<jsid>", "id"),
15803 Argument("JS::MutableHandle<Maybe<JS::PropertyDescriptor>>", "desc"),
15805 ClassMethod.__init__(
15806 self,
15807 "getOwnPropertyDescriptor",
15808 "bool",
15809 args,
15810 virtual=True,
15811 override=True,
15812 const=True,
15814 self.descriptor = descriptor
15816 def getBody(self):
15817 return dedent(
15819 // Implementation of <https://html.spec.whatwg.org/multipage/history.html#location-getownproperty>.
15820 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy));
15822 // Step 1.
15823 if (IsPlatformObjectSameOrigin(cx, proxy)) {
15824 { // Scope so we can wrap our PropertyDescriptor back into
15825 // the caller compartment.
15826 // Enter the Realm of "proxy" so we can work with it.
15827 JSAutoRealm ar(cx, proxy);
15829 JS_MarkCrossZoneId(cx, id);
15831 // The spec messes around with configurability of the returned
15832 // descriptor here, but it's not clear what should actually happen
15833 // here. See <https://github.com/whatwg/html/issues/4157>. For
15834 // now, keep our old behavior and don't do any magic.
15835 if (!dom::DOMProxyHandler::getOwnPropertyDescriptor(cx, proxy, id, desc)) {
15836 return false;
15839 return JS_WrapPropertyDescriptor(cx, desc);
15842 // Step 2.
15843 if (!CrossOriginGetOwnPropertyHelper(cx, proxy, id, desc)) {
15844 return false;
15847 // Step 3.
15848 if (desc.isSome()) {
15849 return true;
15852 // And step 4.
15853 return CrossOriginPropertyFallback(cx, proxy, id, desc);
15858 class CGDOMJSProxyHandler_getSameOriginPrototype(ClassMethod):
15860 Implementation of getSameOriginPrototype. We only use this for
15861 cross-origin objects.
15864 def __init__(self, descriptor):
15865 assert descriptor.isMaybeCrossOriginObject()
15867 args = [Argument("JSContext*", "cx")]
15868 ClassMethod.__init__(
15869 self,
15870 "getSameOriginPrototype",
15871 "JSObject*",
15872 args,
15873 virtual=True,
15874 override=True,
15875 const=True,
15877 self.descriptor = descriptor
15879 def getBody(self):
15880 return dedent(
15882 return GetProtoObjectHandle(cx);
15887 class CGDOMJSProxyHandler_definePropertySameOrigin(ClassMethod):
15889 Implementation of definePropertySameOrigin. We only use this for
15890 cross-origin objects.
15893 def __init__(self, descriptor):
15894 assert descriptor.isMaybeCrossOriginObject()
15896 args = [
15897 Argument("JSContext*", "cx"),
15898 Argument("JS::Handle<JSObject*>", "proxy"),
15899 Argument("JS::Handle<jsid>", "id"),
15900 Argument("JS::Handle<JS::PropertyDescriptor>", "desc"),
15901 Argument("JS::ObjectOpResult&", "result"),
15903 ClassMethod.__init__(
15904 self,
15905 "definePropertySameOrigin",
15906 "bool",
15907 args,
15908 virtual=True,
15909 override=True,
15910 const=True,
15912 self.descriptor = descriptor
15914 def getBody(self):
15915 return dedent(
15917 return dom::DOMProxyHandler::defineProperty(cx, proxy, id, desc, result);
15922 class CGDOMJSProxyHandler_set(ClassMethod):
15924 Implementation of set(). We only use this for cross-origin objects.
15927 def __init__(self, descriptor):
15928 assert descriptor.isMaybeCrossOriginObject()
15930 args = [
15931 Argument("JSContext*", "cx"),
15932 Argument("JS::Handle<JSObject*>", "proxy"),
15933 Argument("JS::Handle<jsid>", "id"),
15934 Argument("JS::Handle<JS::Value>", "v"),
15935 Argument("JS::Handle<JS::Value>", "receiver"),
15936 Argument("JS::ObjectOpResult&", "result"),
15938 ClassMethod.__init__(
15939 self, "set", "bool", args, virtual=True, override=True, const=True
15941 self.descriptor = descriptor
15943 def getBody(self):
15944 return dedent(
15946 if (!IsPlatformObjectSameOrigin(cx, proxy)) {
15947 return CrossOriginSet(cx, proxy, id, v, receiver, result);
15950 // Safe to enter the Realm of proxy now, since it's same-origin with us.
15951 JSAutoRealm ar(cx, proxy);
15952 JS::Rooted<JS::Value> wrappedReceiver(cx, receiver);
15953 if (!MaybeWrapValue(cx, &wrappedReceiver)) {
15954 return false;
15957 JS::Rooted<JS::Value> wrappedValue(cx, v);
15958 if (!MaybeWrapValue(cx, &wrappedValue)) {
15959 return false;
15962 JS_MarkCrossZoneId(cx, id);
15964 return dom::DOMProxyHandler::set(cx, proxy, id, wrappedValue, wrappedReceiver, result);
15969 class CGDOMJSProxyHandler_EnsureHolder(ClassMethod):
15971 Implementation of EnsureHolder(). We only use this for cross-origin objects.
15974 def __init__(self, descriptor):
15975 args = [
15976 Argument("JSContext*", "cx"),
15977 Argument("JS::Handle<JSObject*>", "proxy"),
15978 Argument("JS::MutableHandle<JSObject*>", "holder"),
15980 ClassMethod.__init__(
15981 self, "EnsureHolder", "bool", args, virtual=True, override=True, const=True
15983 self.descriptor = descriptor
15985 def getBody(self):
15986 return dedent(
15988 return EnsureHolder(cx, proxy,
15989 JSCLASS_RESERVED_SLOTS(JS::GetClass(proxy)) - 1,
15990 sCrossOriginProperties, holder);
15995 class CGDOMJSProxyHandler(CGClass):
15996 def __init__(self, descriptor):
15997 assert (
15998 descriptor.supportsIndexedProperties()
15999 or descriptor.supportsNamedProperties()
16000 or descriptor.isMaybeCrossOriginObject()
16002 methods = [
16003 CGDOMJSProxyHandler_getOwnPropDescriptor(descriptor),
16004 CGDOMJSProxyHandler_defineProperty(descriptor),
16005 ClassUsingDeclaration("mozilla::dom::DOMProxyHandler", "defineProperty"),
16006 CGDOMJSProxyHandler_ownPropNames(descriptor),
16007 CGDOMJSProxyHandler_hasOwn(descriptor),
16008 CGDOMJSProxyHandler_get(descriptor),
16009 CGDOMJSProxyHandler_className(descriptor),
16010 CGDOMJSProxyHandler_finalizeInBackground(descriptor),
16011 CGDOMJSProxyHandler_finalize(descriptor),
16012 CGDOMJSProxyHandler_getInstance(),
16013 CGDOMJSProxyHandler_delete(descriptor),
16015 constructors = [
16016 ClassConstructor([], constexpr=True, visibility="public", explicit=True)
16019 if descriptor.supportsIndexedProperties():
16020 methods.append(CGDOMJSProxyHandler_getElements(descriptor))
16021 if descriptor.operations["IndexedSetter"] is not None or (
16022 descriptor.operations["NamedSetter"] is not None
16023 and descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns")
16025 methods.append(CGDOMJSProxyHandler_setCustom(descriptor))
16026 if descriptor.operations["LegacyCaller"]:
16027 methods.append(CGDOMJSProxyHandler_call())
16028 methods.append(CGDOMJSProxyHandler_isCallable())
16029 if descriptor.interface.hasProbablyShortLivingWrapper():
16030 if not descriptor.wrapperCache:
16031 raise TypeError(
16032 "Need a wrapper cache to support nursery "
16033 "allocation of DOM objects"
16035 methods.append(CGDOMJSProxyHandler_canNurseryAllocate())
16036 if descriptor.wrapperCache:
16037 methods.append(CGDOMJSProxyHandler_objectMoved(descriptor))
16039 if descriptor.isMaybeCrossOriginObject():
16040 methods.extend(
16042 CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor),
16043 CGDOMJSProxyHandler_getSameOriginPrototype(descriptor),
16044 CGDOMJSProxyHandler_definePropertySameOrigin(descriptor),
16045 CGDOMJSProxyHandler_set(descriptor),
16046 CGDOMJSProxyHandler_EnsureHolder(descriptor),
16047 ClassUsingDeclaration(
16048 "MaybeCrossOriginObjectMixins", "EnsureHolder"
16053 if descriptor.interface.getExtendedAttribute("LegacyOverrideBuiltIns"):
16054 assert not descriptor.isMaybeCrossOriginObject()
16055 parentClass = "ShadowingDOMProxyHandler"
16056 elif descriptor.isMaybeCrossOriginObject():
16057 parentClass = "MaybeCrossOriginObject<mozilla::dom::DOMProxyHandler>"
16058 else:
16059 parentClass = "mozilla::dom::DOMProxyHandler"
16061 CGClass.__init__(
16062 self,
16063 "DOMProxyHandler",
16064 bases=[ClassBase(parentClass)],
16065 constructors=constructors,
16066 methods=methods,
16070 class CGDOMJSProxyHandlerDeclarer(CGThing):
16072 A class for declaring a DOMProxyHandler.
16075 def __init__(self, handlerThing):
16076 self.handlerThing = handlerThing
16078 def declare(self):
16079 # Our class declaration should happen when we're defining
16080 return ""
16082 def define(self):
16083 return self.handlerThing.declare()
16086 class CGDOMJSProxyHandlerDefiner(CGThing):
16088 A class for defining a DOMProxyHandler.
16091 def __init__(self, handlerThing):
16092 self.handlerThing = handlerThing
16094 def declare(self):
16095 return ""
16097 def define(self):
16098 return self.handlerThing.define()
16101 def stripTrailingWhitespace(text):
16102 tail = "\n" if text.endswith("\n") else ""
16103 lines = text.splitlines()
16104 return "\n".join(line.rstrip() for line in lines) + tail
16107 class MemberProperties:
16108 def __init__(self):
16109 self.isCrossOriginMethod = False
16110 self.isCrossOriginGetter = False
16111 self.isCrossOriginSetter = False
16114 def memberProperties(m, descriptor):
16115 props = MemberProperties()
16116 if m.isMethod():
16117 if not m.isIdentifierLess() or m == descriptor.operations["Stringifier"]:
16118 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
16119 if m.getExtendedAttribute("CrossOriginCallable"):
16120 props.isCrossOriginMethod = True
16121 elif m.isAttr():
16122 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
16123 if m.getExtendedAttribute("CrossOriginReadable"):
16124 props.isCrossOriginGetter = True
16125 if not m.readonly:
16126 if not m.isStatic() and descriptor.interface.hasInterfacePrototypeObject():
16127 if m.getExtendedAttribute("CrossOriginWritable"):
16128 props.isCrossOriginSetter = True
16129 elif m.getExtendedAttribute("PutForwards"):
16130 if m.getExtendedAttribute("CrossOriginWritable"):
16131 props.isCrossOriginSetter = True
16132 elif m.getExtendedAttribute("Replaceable") or m.getExtendedAttribute(
16133 "LegacyLenientSetter"
16135 if m.getExtendedAttribute("CrossOriginWritable"):
16136 props.isCrossOriginSetter = True
16138 return props
16141 class CGDescriptor(CGThing):
16142 def __init__(self, descriptor):
16143 CGThing.__init__(self)
16145 assert (
16146 not descriptor.concrete
16147 or descriptor.interface.hasInterfacePrototypeObject()
16150 self._deps = descriptor.interface.getDeps()
16152 cgThings = []
16153 cgThings.append(
16154 CGGeneric(declare="typedef %s NativeType;\n" % descriptor.nativeType)
16156 parent = descriptor.interface.parent
16157 if parent:
16158 cgThings.append(
16159 CGGeneric(
16160 "static_assert(IsRefcounted<NativeType>::value == IsRefcounted<%s::NativeType>::value,\n"
16161 ' "Can\'t inherit from an interface with a different ownership model.");\n'
16162 % toBindingNamespace(descriptor.parentPrototypeName)
16166 defaultToJSONMethod = None
16167 needCrossOriginPropertyArrays = False
16168 unscopableNames = list()
16169 for n in descriptor.interface.legacyFactoryFunctions:
16170 cgThings.append(
16171 CGClassConstructor(descriptor, n, LegacyFactoryFunctionName(n))
16173 for m in descriptor.interface.members:
16174 if m.isMethod() and m.identifier.name == "QueryInterface":
16175 continue
16177 props = memberProperties(m, descriptor)
16179 if m.isMethod():
16180 if m.getExtendedAttribute("Unscopable"):
16181 assert not m.isStatic()
16182 unscopableNames.append(m.identifier.name)
16183 if m.isDefaultToJSON():
16184 defaultToJSONMethod = m
16185 elif (
16186 not m.isIdentifierLess()
16187 or m == descriptor.operations["Stringifier"]
16189 if m.isStatic():
16190 assert descriptor.interface.hasInterfaceObject()
16191 cgThings.append(CGStaticMethod(descriptor, m))
16192 if m.returnsPromise():
16193 cgThings.append(CGStaticMethodJitinfo(m))
16194 elif descriptor.interface.hasInterfacePrototypeObject():
16195 specializedMethod = CGSpecializedMethod(descriptor, m)
16196 cgThings.append(specializedMethod)
16197 if m.returnsPromise():
16198 cgThings.append(
16199 CGMethodPromiseWrapper(descriptor, specializedMethod)
16201 cgThings.append(CGMemberJITInfo(descriptor, m))
16202 if props.isCrossOriginMethod:
16203 needCrossOriginPropertyArrays = True
16204 # If we've hit the maplike/setlike member itself, go ahead and
16205 # generate its convenience functions.
16206 elif m.isMaplikeOrSetlike():
16207 cgThings.append(CGMaplikeOrSetlikeHelperGenerator(descriptor, m))
16208 elif m.isAttr():
16209 if m.getExtendedAttribute("Unscopable"):
16210 assert not m.isStatic()
16211 unscopableNames.append(m.identifier.name)
16212 if m.isStatic():
16213 assert descriptor.interface.hasInterfaceObject()
16214 cgThings.append(CGStaticGetter(descriptor, m))
16215 elif descriptor.interface.hasInterfacePrototypeObject():
16216 specializedGetter = CGSpecializedGetter(descriptor, m)
16217 cgThings.append(specializedGetter)
16218 if m.type.isPromise():
16219 cgThings.append(
16220 CGGetterPromiseWrapper(descriptor, specializedGetter)
16222 if props.isCrossOriginGetter:
16223 needCrossOriginPropertyArrays = True
16224 if not m.readonly:
16225 if m.isStatic():
16226 assert descriptor.interface.hasInterfaceObject()
16227 cgThings.append(CGStaticSetter(descriptor, m))
16228 elif descriptor.interface.hasInterfacePrototypeObject():
16229 cgThings.append(CGSpecializedSetter(descriptor, m))
16230 if props.isCrossOriginSetter:
16231 needCrossOriginPropertyArrays = True
16232 elif m.getExtendedAttribute("PutForwards"):
16233 cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
16234 if props.isCrossOriginSetter:
16235 needCrossOriginPropertyArrays = True
16236 elif m.getExtendedAttribute("Replaceable"):
16237 cgThings.append(CGSpecializedReplaceableSetter(descriptor, m))
16238 elif m.getExtendedAttribute("LegacyLenientSetter"):
16239 # XXX In this case, we need to add an include for mozilla/dom/Document.h to the generated cpp file.
16240 cgThings.append(CGSpecializedLenientSetter(descriptor, m))
16241 if (
16242 not m.isStatic()
16243 and descriptor.interface.hasInterfacePrototypeObject()
16245 cgThings.append(CGMemberJITInfo(descriptor, m))
16246 if m.isConst() and m.type.isPrimitive():
16247 cgThings.append(CGConstDefinition(m))
16249 if defaultToJSONMethod:
16250 cgThings.append(CGDefaultToJSONMethod(descriptor, defaultToJSONMethod))
16251 cgThings.append(CGMemberJITInfo(descriptor, defaultToJSONMethod))
16253 if descriptor.concrete and not descriptor.proxy:
16254 if wantsAddProperty(descriptor):
16255 cgThings.append(CGAddPropertyHook(descriptor))
16257 # Always have a finalize hook, regardless of whether the class
16258 # wants a custom hook.
16259 cgThings.append(CGClassFinalizeHook(descriptor))
16261 if wantsGetWrapperCache(descriptor):
16262 cgThings.append(CGGetWrapperCacheHook(descriptor))
16264 if descriptor.concrete and descriptor.wrapperCache and not descriptor.proxy:
16265 cgThings.append(CGClassObjectMovedHook(descriptor))
16267 properties = PropertyArrays(descriptor)
16268 cgThings.append(CGGeneric(define=str(properties)))
16269 cgThings.append(CGNativeProperties(descriptor, properties))
16271 if defaultToJSONMethod:
16272 # Now that we know about our property arrays, we can
16273 # output our "collect attribute values" method, which uses those.
16274 cgThings.append(
16275 CGCollectJSONAttributesMethod(descriptor, defaultToJSONMethod)
16278 if descriptor.interface.hasInterfaceObject():
16279 cgThings.append(CGClassConstructor(descriptor, descriptor.interface.ctor()))
16280 cgThings.append(CGInterfaceObjectJSClass(descriptor, properties))
16281 cgThings.append(CGLegacyFactoryFunctions(descriptor))
16283 cgThings.append(CGLegacyCallHook(descriptor))
16284 if descriptor.interface.getExtendedAttribute("NeedResolve"):
16285 cgThings.append(CGResolveHook(descriptor))
16286 cgThings.append(CGMayResolveHook(descriptor))
16287 cgThings.append(CGEnumerateHook(descriptor))
16289 if descriptor.hasNamedPropertiesObject:
16290 cgThings.append(CGGetNamedPropertiesObjectMethod(descriptor))
16292 if descriptor.interface.hasInterfacePrototypeObject():
16293 cgThings.append(CGPrototypeJSClass(descriptor, properties))
16295 if (
16296 descriptor.interface.hasInterfaceObject()
16297 and not descriptor.interface.isExternal()
16298 and descriptor.isExposedConditionally()
16300 cgThings.append(CGConstructorEnabled(descriptor))
16302 if (
16303 descriptor.interface.hasMembersInSlots()
16304 and descriptor.interface.hasChildInterfaces()
16306 raise TypeError(
16307 "We don't support members in slots on "
16308 "non-leaf interfaces like %s" % descriptor.interface.identifier.name
16311 if descriptor.needsMissingPropUseCounters:
16312 cgThings.append(CGCountMaybeMissingProperty(descriptor))
16314 if descriptor.concrete:
16315 if descriptor.interface.isSerializable():
16316 cgThings.append(CGSerializer(descriptor))
16317 cgThings.append(CGDeserializer(descriptor))
16319 if descriptor.proxy:
16320 cgThings.append(
16321 CGGeneric(
16322 fill(
16324 static_assert(std::is_base_of_v<nsISupports, ${nativeType}>,
16325 "We don't support non-nsISupports native classes for "
16326 "proxy-based bindings yet");
16328 """,
16329 nativeType=descriptor.nativeType,
16333 if not descriptor.wrapperCache:
16334 raise TypeError(
16335 "We need a wrappercache to support expandos for proxy-based "
16336 "bindings (" + descriptor.name + ")"
16338 handlerThing = CGDOMJSProxyHandler(descriptor)
16339 cgThings.append(CGDOMJSProxyHandlerDeclarer(handlerThing))
16340 cgThings.append(CGProxyIsProxy(descriptor))
16341 cgThings.append(CGProxyUnwrap(descriptor))
16342 cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing))
16343 cgThings.append(CGDOMProxyJSClass(descriptor))
16344 else:
16345 cgThings.append(CGDOMJSClass(descriptor))
16346 cgThings.append(CGGetJSClassMethod(descriptor))
16348 if descriptor.interface.hasMembersInSlots():
16349 cgThings.append(CGUpdateMemberSlotsMethod(descriptor))
16351 if descriptor.isGlobal():
16352 assert descriptor.wrapperCache
16353 cgThings.append(CGWrapGlobalMethod(descriptor, properties))
16354 elif descriptor.wrapperCache:
16355 cgThings.append(CGWrapWithCacheMethod(descriptor, properties))
16356 cgThings.append(CGWrapMethod(descriptor))
16357 else:
16358 cgThings.append(CGWrapNonWrapperCacheMethod(descriptor, properties))
16360 # Set up our Xray callbacks as needed. This needs to come
16361 # after we have our DOMProxyHandler defined.
16362 if descriptor.wantsXrays:
16363 if descriptor.concrete and descriptor.proxy:
16364 if not descriptor.isMaybeCrossOriginObject():
16365 cgThings.append(CGResolveOwnProperty(descriptor))
16366 cgThings.append(CGEnumerateOwnProperties(descriptor))
16367 if descriptor.needsXrayNamedDeleterHook():
16368 cgThings.append(CGDeleteNamedProperty(descriptor))
16369 elif descriptor.needsXrayResolveHooks():
16370 cgThings.append(CGResolveOwnPropertyViaResolve(descriptor))
16371 cgThings.append(
16372 CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor)
16374 if descriptor.wantsXrayExpandoClass:
16375 cgThings.append(CGXrayExpandoJSClass(descriptor))
16377 # Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff
16378 # done, set up our NativePropertyHooks.
16379 cgThings.append(CGNativePropertyHooks(descriptor, properties))
16381 # If we're not wrappercached, we don't know how to clear our
16382 # cached values, since we can't get at the JSObject.
16383 if descriptor.wrapperCache:
16384 cgThings.extend(
16385 CGClearCachedValueMethod(descriptor, m)
16386 for m in clearableCachedAttrs(descriptor)
16389 haveUnscopables = (
16390 len(unscopableNames) != 0
16391 and descriptor.interface.hasInterfacePrototypeObject()
16393 if haveUnscopables:
16394 cgThings.append(
16395 CGList(
16397 CGGeneric("static const char* const unscopableNames[] = {"),
16398 CGIndenter(
16399 CGList(
16400 [CGGeneric('"%s"' % name) for name in unscopableNames]
16401 + [CGGeneric("nullptr")],
16402 ",\n",
16405 CGGeneric("};\n"),
16407 "\n",
16411 legacyWindowAliases = descriptor.interface.legacyWindowAliases
16412 haveLegacyWindowAliases = len(legacyWindowAliases) != 0
16413 if haveLegacyWindowAliases:
16414 cgThings.append(
16415 CGList(
16417 CGGeneric("static const char* const legacyWindowAliases[] = {"),
16418 CGIndenter(
16419 CGList(
16421 CGGeneric('"%s"' % name)
16422 for name in legacyWindowAliases
16424 + [CGGeneric("nullptr")],
16425 ",\n",
16428 CGGeneric("};\n"),
16430 "\n",
16434 # CGCreateInterfaceObjectsMethod needs to come after our
16435 # CGDOMJSClass and unscopables, if any.
16436 cgThings.append(
16437 CGCreateInterfaceObjectsMethod(
16438 descriptor, properties, haveUnscopables, haveLegacyWindowAliases
16442 # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need
16443 # to come after CGCreateInterfaceObjectsMethod.
16444 if descriptor.interface.hasInterfacePrototypeObject():
16445 cgThings.append(CGGetProtoObjectHandleMethod(descriptor))
16446 if descriptor.interface.hasChildInterfaces():
16447 cgThings.append(CGGetProtoObjectMethod(descriptor))
16448 if descriptor.interface.hasInterfaceObject():
16449 cgThings.append(CGGetConstructorObjectHandleMethod(descriptor))
16450 cgThings.append(CGGetConstructorObjectMethod(descriptor))
16452 # See whether we need to generate cross-origin property arrays.
16453 if needCrossOriginPropertyArrays:
16454 cgThings.append(CGCrossOriginProperties(descriptor))
16456 cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
16457 cgThings = CGWrapper(cgThings, pre="\n", post="\n")
16458 self.cgRoot = CGWrapper(
16459 CGNamespace(toBindingNamespace(descriptor.name), cgThings), post="\n"
16462 def declare(self):
16463 return self.cgRoot.declare()
16465 def define(self):
16466 return self.cgRoot.define()
16468 def deps(self):
16469 return self._deps
16472 class CGNamespacedEnum(CGThing):
16473 def __init__(self, namespace, enumName, names, values, comment=""):
16475 if not values:
16476 values = []
16478 # Account for explicit enum values.
16479 entries = []
16480 for i in range(0, len(names)):
16481 if len(values) > i and values[i] is not None:
16482 entry = "%s = %s" % (names[i], values[i])
16483 else:
16484 entry = names[i]
16485 entries.append(entry)
16487 # Append a Count.
16488 entries.append("_" + enumName + "_Count")
16490 # Indent.
16491 entries = [" " + e for e in entries]
16493 # Build the enum body.
16494 enumstr = comment + "enum %s : uint16_t\n{\n%s\n};\n" % (
16495 enumName,
16496 ",\n".join(entries),
16498 curr = CGGeneric(declare=enumstr)
16500 # Add some whitespace padding.
16501 curr = CGWrapper(curr, pre="\n", post="\n")
16503 # Add the namespace.
16504 curr = CGNamespace(namespace, curr)
16506 # Add the typedef
16507 typedef = "\ntypedef %s::%s %s;\n\n" % (namespace, enumName, enumName)
16508 curr = CGList([curr, CGGeneric(declare=typedef)])
16510 # Save the result.
16511 self.node = curr
16513 def declare(self):
16514 return self.node.declare()
16516 def define(self):
16517 return ""
16520 def initIdsClassMethod(identifiers, atomCacheName):
16521 idinit = [
16522 '!atomsCache->%s.init(cx, "%s")' % (CGDictionary.makeIdName(id), id)
16523 for id in identifiers
16525 idinit.reverse()
16526 body = fill(
16528 MOZ_ASSERT(JSID_IS_VOID(*reinterpret_cast<jsid*>(atomsCache)));
16530 // Initialize these in reverse order so that any failure leaves the first one
16531 // uninitialized.
16532 if (${idinit}) {
16533 return false;
16535 return true;
16536 """,
16537 idinit=" ||\n ".join(idinit),
16539 return ClassMethod(
16540 "InitIds",
16541 "bool",
16542 [Argument("JSContext*", "cx"), Argument("%s*" % atomCacheName, "atomsCache")],
16543 static=True,
16544 body=body,
16545 visibility="private",
16549 class CGDictionary(CGThing):
16550 def __init__(self, dictionary, descriptorProvider):
16551 self.dictionary = dictionary
16552 self.descriptorProvider = descriptorProvider
16553 self.needToInitIds = len(dictionary.members) > 0
16554 self.memberInfo = [
16556 member,
16557 getJSToNativeConversionInfo(
16558 member.type,
16559 descriptorProvider,
16560 isMember="Dictionary",
16561 isOptional=member.canHaveMissingValue(),
16562 isKnownMissing=not dictionary.needsConversionFromJS,
16563 defaultValue=member.defaultValue,
16564 sourceDescription=self.getMemberSourceDescription(member),
16567 for member in dictionary.members
16570 # If we have a union member which is going to be declared in a different
16571 # header but contains something that will be declared in the same header
16572 # as us, bail: the C++ includes won't work out.
16573 for member in dictionary.members:
16574 type = member.type.unroll()
16575 if type.isUnion() and CGHeaders.getUnionDeclarationFilename(
16576 descriptorProvider.getConfig(), type
16577 ) != CGHeaders.getDeclarationFilename(dictionary):
16578 for t in type.flatMemberTypes:
16579 if t.isDictionary() and CGHeaders.getDeclarationFilename(
16580 t.inner
16581 ) == CGHeaders.getDeclarationFilename(dictionary):
16582 raise TypeError(
16583 "Dictionary contains a union that will live in a different "
16584 "header that contains a dictionary from the same header as "
16585 "the original dictionary. This won't compile. Move the "
16586 "inner dictionary to a different Web IDL file to move it "
16587 "to a different header.\n%s\n%s"
16588 % (t.location, t.inner.location)
16590 self.structs = self.getStructs()
16592 def declare(self):
16593 return self.structs.declare()
16595 def define(self):
16596 return self.structs.define()
16598 def base(self):
16599 if self.dictionary.parent:
16600 return self.makeClassName(self.dictionary.parent)
16601 return "DictionaryBase"
16603 def initMethod(self):
16605 This function outputs the body of the Init() method for the dictionary.
16607 For the most part, this is some bookkeeping for our atoms so
16608 we can avoid atomizing strings all the time, then we just spit
16609 out the getMemberConversion() output for each member,
16610 separated by newlines.
16613 body = dedent(
16615 // Passing a null JSContext is OK only if we're initing from null,
16616 // Since in that case we will not have to do any property gets
16617 // Also evaluate isNullOrUndefined in order to avoid false-positive
16618 // checkers by static analysis tools
16619 MOZ_ASSERT_IF(!cx, val.isNull() && val.isNullOrUndefined());
16623 if self.needToInitIds:
16624 body += fill(
16626 ${dictName}Atoms* atomsCache = nullptr;
16627 if (cx) {
16628 atomsCache = GetAtomCache<${dictName}Atoms>(cx);
16629 if (JSID_IS_VOID(*reinterpret_cast<jsid*>(atomsCache)) &&
16630 !InitIds(cx, atomsCache)) {
16631 return false;
16635 """,
16636 dictName=self.makeClassName(self.dictionary),
16639 if self.dictionary.parent:
16640 body += fill(
16642 // Per spec, we init the parent's members first
16643 if (!${dictName}::Init(cx, val)) {
16644 return false;
16647 """,
16648 dictName=self.makeClassName(self.dictionary.parent),
16650 else:
16651 body += dedent(
16653 if (!IsConvertibleToDictionary(val)) {
16654 return cx.ThrowErrorMessage<MSG_NOT_DICTIONARY>(sourceDescription);
16660 memberInits = [self.getMemberConversion(m).define() for m in self.memberInfo]
16661 if memberInits:
16662 body += fill(
16664 bool isNull = val.isNullOrUndefined();
16665 // We only need these if !isNull, in which case we have |cx|.
16666 Maybe<JS::Rooted<JSObject *> > object;
16667 Maybe<JS::Rooted<JS::Value> > temp;
16668 if (!isNull) {
16669 MOZ_ASSERT(cx);
16670 object.emplace(cx, &val.toObject());
16671 temp.emplace(cx);
16673 $*{memberInits}
16674 """,
16675 memberInits="\n".join(memberInits),
16678 body += "return true;\n"
16680 return ClassMethod(
16681 "Init",
16682 "bool",
16684 Argument("BindingCallContext&", "cx"),
16685 Argument("JS::Handle<JS::Value>", "val"),
16686 Argument("const char*", "sourceDescription", default='"Value"'),
16687 Argument("bool", "passedToJSImpl", default="false"),
16689 body=body,
16692 def initWithoutCallContextMethod(self):
16694 This function outputs the body of an Init() method for the dictionary
16695 that takes just a JSContext*. This is needed for non-binding consumers.
16697 body = dedent(
16699 // We don't want to use sourceDescription for our context here;
16700 // that's not really what it's formatted for.
16701 BindingCallContext cx(cx_, nullptr);
16702 return Init(cx, val, sourceDescription, passedToJSImpl);
16705 return ClassMethod(
16706 "Init",
16707 "bool",
16709 Argument("JSContext*", "cx_"),
16710 Argument("JS::Handle<JS::Value>", "val"),
16711 Argument("const char*", "sourceDescription", default='"Value"'),
16712 Argument("bool", "passedToJSImpl", default="false"),
16714 body=body,
16717 def simpleInitMethod(self):
16719 This function outputs the body of the Init() method for the dictionary,
16720 for cases when we are just default-initializing it.
16723 relevantMembers = [
16725 for m in self.memberInfo
16726 # We only need to init the things that can have
16727 # default values.
16728 if m[0].optional and m[0].defaultValue
16731 # We mostly avoid outputting code that uses cx in our native-to-JS
16732 # conversions, but there is one exception: we may have a
16733 # dictionary-typed member that _does_ generally support conversion from
16734 # JS. If we have such a thing, we can pass it a null JSContext and
16735 # JS::NullHandleValue to default-initialize it, but since the
16736 # native-to-JS templates hardcode `cx` as the JSContext value, we're
16737 # going to need to provide that.
16738 haveMemberThatNeedsCx = any(
16739 m[0].type.isDictionary() and m[0].type.unroll().inner.needsConversionFromJS
16740 for m in relevantMembers
16742 if haveMemberThatNeedsCx:
16743 body = dedent(
16745 JSContext* cx = nullptr;
16748 else:
16749 body = ""
16751 if self.dictionary.parent:
16752 if self.dictionary.parent.needsConversionFromJS:
16753 args = "nullptr, JS::NullHandleValue"
16754 else:
16755 args = ""
16756 body += fill(
16758 // We init the parent's members first
16759 if (!${dictName}::Init(${args})) {
16760 return false;
16763 """,
16764 dictName=self.makeClassName(self.dictionary.parent),
16765 args=args,
16768 memberInits = [
16769 self.getMemberConversion(m, isKnownMissing=True).define()
16770 for m in relevantMembers
16772 if memberInits:
16773 body += fill(
16775 $*{memberInits}
16776 """,
16777 memberInits="\n".join(memberInits),
16780 body += "return true;\n"
16782 return ClassMethod(
16783 "Init",
16784 "bool",
16786 Argument("const char*", "sourceDescription", default='"Value"'),
16787 Argument("bool", "passedToJSImpl", default="false"),
16789 body=body,
16792 def initFromJSONMethod(self):
16793 return ClassMethod(
16794 "Init",
16795 "bool",
16796 [Argument("const nsAString&", "aJSON")],
16797 body=dedent(
16799 AutoJSAPI jsapi;
16800 JSObject* cleanGlobal = SimpleGlobalObject::Create(SimpleGlobalObject::GlobalType::BindingDetail);
16801 if (!cleanGlobal) {
16802 return false;
16804 if (!jsapi.Init(cleanGlobal)) {
16805 return false;
16807 JSContext* cx = jsapi.cx();
16808 JS::Rooted<JS::Value> json(cx);
16809 bool ok = ParseJSON(cx, aJSON, &json);
16810 NS_ENSURE_TRUE(ok, false);
16811 return Init(cx, json);
16816 def toJSONMethod(self):
16817 return ClassMethod(
16818 "ToJSON",
16819 "bool",
16820 [Argument("nsAString&", "aJSON")],
16821 body=dedent(
16823 AutoJSAPI jsapi;
16824 jsapi.Init();
16825 JSContext *cx = jsapi.cx();
16826 // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here
16827 // because we'll only be creating objects, in ways that have no
16828 // side-effects, followed by a call to JS::ToJSONMaybeSafely,
16829 // which likewise guarantees no side-effects for the sorts of
16830 // things we will pass it.
16831 JSObject* scope = UnprivilegedJunkScopeOrWorkerGlobal(fallible);
16832 if (!scope) {
16833 JS_ReportOutOfMemory(cx);
16834 return false;
16836 JSAutoRealm ar(cx, scope);
16837 JS::Rooted<JS::Value> val(cx);
16838 if (!ToObjectInternal(cx, &val)) {
16839 return false;
16841 JS::Rooted<JSObject*> obj(cx, &val.toObject());
16842 return StringifyToJSON(cx, obj, aJSON);
16845 const=True,
16848 def toObjectInternalMethod(self):
16849 body = ""
16850 if self.needToInitIds:
16851 body += fill(
16853 ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx);
16854 if (JSID_IS_VOID(*reinterpret_cast<jsid*>(atomsCache)) &&
16855 !InitIds(cx, atomsCache)) {
16856 return false;
16859 """,
16860 dictName=self.makeClassName(self.dictionary),
16863 if self.dictionary.parent:
16864 body += fill(
16866 // Per spec, we define the parent's members first
16867 if (!${dictName}::ToObjectInternal(cx, rval)) {
16868 return false;
16870 JS::Rooted<JSObject*> obj(cx, &rval.toObject());
16872 """,
16873 dictName=self.makeClassName(self.dictionary.parent),
16875 else:
16876 body += dedent(
16878 JS::Rooted<JSObject*> obj(cx, JS_NewPlainObject(cx));
16879 if (!obj) {
16880 return false;
16882 rval.set(JS::ObjectValue(*obj));
16887 if self.memberInfo:
16888 body += "\n".join(
16889 self.getMemberDefinition(m).define() for m in self.memberInfo
16891 body += "\nreturn true;\n"
16893 return ClassMethod(
16894 "ToObjectInternal",
16895 "bool",
16897 Argument("JSContext*", "cx"),
16898 Argument("JS::MutableHandle<JS::Value>", "rval"),
16900 const=True,
16901 body=body,
16904 def initIdsMethod(self):
16905 assert self.needToInitIds
16906 return initIdsClassMethod(
16907 [m.identifier.name for m in self.dictionary.members],
16908 "%sAtoms" % self.makeClassName(self.dictionary),
16911 def traceDictionaryMethod(self):
16912 body = ""
16913 if self.dictionary.parent:
16914 cls = self.makeClassName(self.dictionary.parent)
16915 body += "%s::TraceDictionary(trc);\n" % cls
16917 memberTraces = [
16918 self.getMemberTrace(m)
16919 for m in self.dictionary.members
16920 if typeNeedsRooting(m.type)
16923 if memberTraces:
16924 body += "\n".join(memberTraces)
16926 return ClassMethod(
16927 "TraceDictionary",
16928 "void",
16930 Argument("JSTracer*", "trc"),
16932 body=body,
16935 @staticmethod
16936 def dictionaryNeedsCycleCollection(dictionary):
16937 return any(idlTypeNeedsCycleCollection(m.type) for m in dictionary.members) or (
16938 dictionary.parent
16939 and CGDictionary.dictionaryNeedsCycleCollection(dictionary.parent)
16942 def traverseForCCMethod(self):
16943 body = ""
16944 if self.dictionary.parent and self.dictionaryNeedsCycleCollection(
16945 self.dictionary.parent
16947 cls = self.makeClassName(self.dictionary.parent)
16948 body += "%s::TraverseForCC(aCallback, aFlags);\n" % cls
16950 for m, _ in self.memberInfo:
16951 if idlTypeNeedsCycleCollection(m.type):
16952 memberName = self.makeMemberName(m.identifier.name)
16953 body += (
16954 'ImplCycleCollectionTraverse(aCallback, %s, "%s", aFlags);\n'
16955 % (memberName, memberName)
16958 return ClassMethod(
16959 "TraverseForCC",
16960 "void",
16962 Argument("nsCycleCollectionTraversalCallback&", "aCallback"),
16963 Argument("uint32_t", "aFlags"),
16965 body=body,
16966 # Inline so we don't pay a codesize hit unless someone actually uses
16967 # this traverse method.
16968 inline=True,
16969 bodyInHeader=True,
16972 def unlinkForCCMethod(self):
16973 body = ""
16974 if self.dictionary.parent and self.dictionaryNeedsCycleCollection(
16975 self.dictionary.parent
16977 cls = self.makeClassName(self.dictionary.parent)
16978 body += "%s::UnlinkForCC();\n" % cls
16980 for m, _ in self.memberInfo:
16981 if idlTypeNeedsCycleCollection(m.type):
16982 memberName = self.makeMemberName(m.identifier.name)
16983 body += "ImplCycleCollectionUnlink(%s);\n" % memberName
16985 return ClassMethod(
16986 "UnlinkForCC",
16987 "void",
16989 body=body,
16990 # Inline so we don't pay a codesize hit unless someone actually uses
16991 # this unlink method.
16992 inline=True,
16993 bodyInHeader=True,
16996 def assignmentOperator(self):
16997 body = CGList([])
16998 body.append(CGGeneric("%s::operator=(aOther);\n" % self.base()))
17000 for m, _ in self.memberInfo:
17001 memberName = self.makeMemberName(m.identifier.name)
17002 if m.canHaveMissingValue():
17003 memberAssign = CGGeneric(
17004 fill(
17006 ${name}.Reset();
17007 if (aOther.${name}.WasPassed()) {
17008 ${name}.Construct(aOther.${name}.Value());
17010 """,
17011 name=memberName,
17014 else:
17015 memberAssign = CGGeneric("%s = aOther.%s;\n" % (memberName, memberName))
17016 body.append(memberAssign)
17017 body.append(CGGeneric("return *this;\n"))
17018 return ClassMethod(
17019 "operator=",
17020 "%s&" % self.makeClassName(self.dictionary),
17021 [Argument("const %s&" % self.makeClassName(self.dictionary), "aOther")],
17022 body=body.define(),
17025 def canHaveEqualsOperator(self):
17026 return all(
17027 m.type.isString() or m.type.isPrimitive() for (m, _) in self.memberInfo
17030 def equalsOperator(self):
17031 body = CGList([])
17033 for m, _ in self.memberInfo:
17034 memberName = self.makeMemberName(m.identifier.name)
17035 memberTest = CGGeneric(
17036 fill(
17038 if (${memberName} != aOther.${memberName}) {
17039 return false;
17041 """,
17042 memberName=memberName,
17045 body.append(memberTest)
17046 body.append(CGGeneric("return true;\n"))
17047 return ClassMethod(
17048 "operator==",
17049 "bool",
17050 [Argument("const %s&" % self.makeClassName(self.dictionary), "aOther")],
17051 const=True,
17052 body=body.define(),
17055 def getStructs(self):
17056 d = self.dictionary
17057 selfName = self.makeClassName(d)
17058 members = [
17059 ClassMember(
17060 self.makeMemberName(m[0].identifier.name),
17061 self.getMemberType(m),
17062 visibility="public",
17063 body=self.getMemberInitializer(m),
17064 hasIgnoreInitCheckFlag=True,
17066 for m in self.memberInfo
17068 if d.parent:
17069 # We always want to init our parent with our non-initializing
17070 # constructor arg, because either we're about to init ourselves (and
17071 # hence our parent) or we don't want any init happening.
17072 baseConstructors = [
17073 "%s(%s)"
17074 % (self.makeClassName(d.parent), self.getNonInitializingCtorArg())
17076 else:
17077 baseConstructors = None
17079 if d.needsConversionFromJS:
17080 initArgs = "nullptr, JS::NullHandleValue"
17081 else:
17082 initArgs = ""
17083 ctors = [
17084 ClassConstructor(
17086 visibility="public",
17087 baseConstructors=baseConstructors,
17088 body=(
17089 "// Safe to pass a null context if we pass a null value\n"
17090 "Init(%s);\n" % initArgs
17093 ClassConstructor(
17094 [Argument("const FastDictionaryInitializer&", "")],
17095 visibility="public",
17096 baseConstructors=baseConstructors,
17097 explicit=True,
17098 bodyInHeader=True,
17099 body='// Do nothing here; this is used by our "Fast" subclass\n',
17102 methods = []
17104 if self.needToInitIds:
17105 methods.append(self.initIdsMethod())
17107 if d.needsConversionFromJS:
17108 methods.append(self.initMethod())
17109 methods.append(self.initWithoutCallContextMethod())
17110 else:
17111 methods.append(self.simpleInitMethod())
17113 canBeRepresentedAsJSON = self.dictionarySafeToJSONify(d)
17114 if canBeRepresentedAsJSON and d.getExtendedAttribute("GenerateInitFromJSON"):
17115 methods.append(self.initFromJSONMethod())
17117 if d.needsConversionToJS:
17118 methods.append(self.toObjectInternalMethod())
17120 if canBeRepresentedAsJSON and d.getExtendedAttribute("GenerateToJSON"):
17121 methods.append(self.toJSONMethod())
17123 methods.append(self.traceDictionaryMethod())
17125 try:
17126 if self.dictionaryNeedsCycleCollection(d):
17127 methods.append(self.traverseForCCMethod())
17128 methods.append(self.unlinkForCCMethod())
17129 except CycleCollectionUnsupported:
17130 # We have some member that we don't know how to CC. Don't output
17131 # our cycle collection overloads, so attempts to CC us will fail to
17132 # compile instead of misbehaving.
17133 pass
17135 if CGDictionary.isDictionaryCopyConstructible(d):
17136 disallowCopyConstruction = False
17137 # Note: gcc's -Wextra has a warning against not initializng our
17138 # base explicitly. If we have one. Use our non-initializing base
17139 # constructor to get around that.
17140 ctors.append(
17141 ClassConstructor(
17142 [Argument("const %s&" % selfName, "aOther")],
17143 bodyInHeader=True,
17144 visibility="public",
17145 baseConstructors=baseConstructors,
17146 explicit=True,
17147 body="*this = aOther;\n",
17150 methods.append(self.assignmentOperator())
17151 else:
17152 disallowCopyConstruction = True
17154 if self.canHaveEqualsOperator():
17155 methods.append(self.equalsOperator())
17157 struct = CGClass(
17158 selfName,
17159 bases=[ClassBase(self.base())],
17160 members=members,
17161 constructors=ctors,
17162 methods=methods,
17163 isStruct=True,
17164 disallowCopyConstruction=disallowCopyConstruction,
17167 fastDictionaryCtor = ClassConstructor(
17169 visibility="public",
17170 bodyInHeader=True,
17171 baseConstructors=["%s(%s)" % (selfName, self.getNonInitializingCtorArg())],
17172 body="// Doesn't matter what int we pass to the parent constructor\n",
17175 fastStruct = CGClass(
17176 "Fast" + selfName,
17177 bases=[ClassBase(selfName)],
17178 constructors=[fastDictionaryCtor],
17179 isStruct=True,
17182 return CGList([struct, CGNamespace("binding_detail", fastStruct)], "\n")
17184 def deps(self):
17185 return self.dictionary.getDeps()
17187 @staticmethod
17188 def makeDictionaryName(dictionary):
17189 return dictionary.identifier.name
17191 def makeClassName(self, dictionary):
17192 return self.makeDictionaryName(dictionary)
17194 @staticmethod
17195 def makeMemberName(name):
17196 return "m" + name[0].upper() + IDLToCIdentifier(name[1:])
17198 def getMemberType(self, memberInfo):
17199 _, conversionInfo = memberInfo
17200 # We can't handle having a holderType here
17201 assert conversionInfo.holderType is None
17202 declType = conversionInfo.declType
17203 if conversionInfo.dealWithOptional:
17204 declType = CGTemplatedType("Optional", declType)
17205 return declType.define()
17207 def getMemberConversion(self, memberInfo, isKnownMissing=False):
17209 A function that outputs the initialization of a single dictionary
17210 member from the given dictionary value.
17212 We start with our conversionInfo, which tells us how to
17213 convert a JS::Value to whatever type this member is. We
17214 substiture the template from the conversionInfo with values
17215 that point to our "temp" JS::Value and our member (which is
17216 the C++ value we want to produce). The output is a string of
17217 code to do the conversion. We store this string in
17218 conversionReplacements["convert"].
17220 Now we have three different ways we might use (or skip) this
17221 string of code, depending on whether the value is required,
17222 optional with default value, or optional without default
17223 value. We set up a template in the 'conversion' variable for
17224 exactly how to do this, then substitute into it from the
17225 conversionReplacements dictionary.
17227 member, conversionInfo = memberInfo
17229 # We should only be initializing things with default values if
17230 # we're always-missing.
17231 assert not isKnownMissing or (member.optional and member.defaultValue)
17233 replacements = {
17234 "declName": self.makeMemberName(member.identifier.name),
17235 # We need a holder name for external interfaces, but
17236 # it's scoped down to the conversion so we can just use
17237 # anything we want.
17238 "holderName": "holder",
17239 "passedToJSImpl": "passedToJSImpl",
17242 if isKnownMissing:
17243 replacements["val"] = "(JS::NullHandleValue)"
17244 else:
17245 replacements["val"] = "temp.ref()"
17246 replacements["maybeMutableVal"] = "temp.ptr()"
17248 # We can't handle having a holderType here
17249 assert conversionInfo.holderType is None
17250 if conversionInfo.dealWithOptional:
17251 replacements["declName"] = "(" + replacements["declName"] + ".Value())"
17252 if member.defaultValue:
17253 if isKnownMissing:
17254 replacements["haveValue"] = "false"
17255 else:
17256 replacements["haveValue"] = "!isNull && !temp->isUndefined()"
17258 propId = self.makeIdName(member.identifier.name)
17259 propGet = "JS_GetPropertyById(cx, *object, atomsCache->%s, temp.ptr())" % propId
17261 conversionReplacements = {
17262 "prop": self.makeMemberName(member.identifier.name),
17263 "convert": string.Template(conversionInfo.template).substitute(
17264 replacements
17266 "propGet": propGet,
17268 # The conversion code will only run where a default value or a value passed
17269 # by the author needs to get converted, so we can remember if we have any
17270 # members present here.
17271 conversionReplacements["convert"] += "mIsAnyMemberPresent = true;\n"
17272 if isKnownMissing:
17273 conversion = ""
17274 else:
17275 setTempValue = CGGeneric(
17276 dedent(
17278 if (!${propGet}) {
17279 return false;
17284 conditions = getConditionList(member, "cx", "*object")
17285 if len(conditions) != 0:
17286 setTempValue = CGIfElseWrapper(
17287 conditions.define(),
17288 setTempValue,
17289 CGGeneric("temp->setUndefined();\n"),
17291 setTempValue = CGIfWrapper(setTempValue, "!isNull")
17292 conversion = setTempValue.define()
17294 if member.defaultValue:
17295 if member.type.isUnion() and (
17296 not member.type.nullable()
17297 or not isinstance(member.defaultValue, IDLNullValue)
17299 # Since this has a default value, it might have been initialized
17300 # already. Go ahead and uninit it before we try to init it
17301 # again.
17302 memberName = self.makeMemberName(member.identifier.name)
17303 if member.type.nullable():
17304 conversion += fill(
17306 if (!${memberName}.IsNull()) {
17307 ${memberName}.Value().Uninit();
17309 """,
17310 memberName=memberName,
17312 else:
17313 conversion += "%s.Uninit();\n" % memberName
17314 conversion += "${convert}"
17315 elif not conversionInfo.dealWithOptional:
17316 # We're required, but have no default value. Make sure
17317 # that we throw if we have no value provided.
17318 conversion += dedent(
17320 if (!isNull && !temp->isUndefined()) {
17321 ${convert}
17322 } else if (cx) {
17323 // Don't error out if we have no cx. In that
17324 // situation the caller is default-constructing us and we'll
17325 // just assume they know what they're doing.
17326 return cx.ThrowErrorMessage<MSG_MISSING_REQUIRED_DICTIONARY_MEMBER>("%s");
17329 % self.getMemberSourceDescription(member)
17331 conversionReplacements["convert"] = indent(
17332 conversionReplacements["convert"]
17333 ).rstrip()
17334 else:
17335 conversion += (
17336 "if (!isNull && !temp->isUndefined()) {\n"
17337 " ${prop}.Construct();\n"
17338 "${convert}"
17339 "}\n"
17341 conversionReplacements["convert"] = indent(
17342 conversionReplacements["convert"]
17345 return CGGeneric(string.Template(conversion).substitute(conversionReplacements))
17347 def getMemberDefinition(self, memberInfo):
17348 member = memberInfo[0]
17349 declType = memberInfo[1].declType
17350 memberLoc = self.makeMemberName(member.identifier.name)
17351 if not member.canHaveMissingValue():
17352 memberData = memberLoc
17353 else:
17354 # The data is inside the Optional<>
17355 memberData = "%s.InternalValue()" % memberLoc
17357 # If you have to change this list (which you shouldn't!), make sure it
17358 # continues to match the list in test_Object.prototype_props.html
17359 if member.identifier.name in [
17360 "constructor",
17361 "toString",
17362 "toLocaleString",
17363 "valueOf",
17364 "hasOwnProperty",
17365 "isPrototypeOf",
17366 "propertyIsEnumerable",
17367 "__defineGetter__",
17368 "__defineSetter__",
17369 "__lookupGetter__",
17370 "__lookupSetter__",
17371 "__proto__",
17373 raise TypeError(
17374 "'%s' member of %s dictionary shadows "
17375 "a property of Object.prototype, and Xrays to "
17376 "Object can't handle that.\n"
17377 "%s"
17379 member.identifier.name,
17380 self.dictionary.identifier.name,
17381 member.location,
17385 propDef = (
17386 "JS_DefinePropertyById(cx, obj, atomsCache->%s, temp, JSPROP_ENUMERATE)"
17387 % self.makeIdName(member.identifier.name)
17390 innerTemplate = wrapForType(
17391 member.type,
17392 self.descriptorProvider,
17394 "result": "currentValue",
17395 "successCode": (
17396 "if (!%s) {\n" " return false;\n" "}\n" "break;\n" % propDef
17398 "jsvalRef": "temp",
17399 "jsvalHandle": "&temp",
17400 "returnsNewObject": False,
17401 # 'obj' can just be allowed to be the string "obj", since that
17402 # will be our dictionary object, which is presumably itself in
17403 # the right scope.
17404 "spiderMonkeyInterfacesAreStructs": True,
17407 conversion = CGGeneric(innerTemplate)
17408 conversion = CGWrapper(
17409 conversion,
17410 pre=(
17411 "JS::Rooted<JS::Value> temp(cx);\n"
17412 "%s const & currentValue = %s;\n" % (declType.define(), memberData)
17416 # Now make sure that our successCode can actually break out of the
17417 # conversion. This incidentally gives us a scope for 'temp' and
17418 # 'currentValue'.
17419 conversion = CGWrapper(
17420 CGIndenter(conversion),
17421 pre=(
17422 "do {\n"
17423 " // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n"
17425 post="} while(false);\n",
17427 if member.canHaveMissingValue():
17428 # Only do the conversion if we have a value
17429 conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc)
17430 conditions = getConditionList(member, "cx", "obj")
17431 if len(conditions) != 0:
17432 conversion = CGIfWrapper(conversion, conditions.define())
17433 return conversion
17435 def getMemberTrace(self, member):
17436 type = member.type
17437 assert typeNeedsRooting(type)
17438 memberLoc = self.makeMemberName(member.identifier.name)
17439 if not member.canHaveMissingValue():
17440 memberData = memberLoc
17441 else:
17442 # The data is inside the Optional<>
17443 memberData = "%s.Value()" % memberLoc
17445 memberName = "%s.%s" % (self.makeClassName(self.dictionary), memberLoc)
17447 if type.isObject():
17448 trace = CGGeneric(
17449 'JS::UnsafeTraceRoot(trc, %s, "%s");\n' % ("&" + memberData, memberName)
17451 if type.nullable():
17452 trace = CGIfWrapper(trace, memberData)
17453 elif type.isAny():
17454 trace = CGGeneric(
17455 'JS::UnsafeTraceRoot(trc, %s, "%s");\n' % ("&" + memberData, memberName)
17457 elif (
17458 type.isSequence()
17459 or type.isDictionary()
17460 or type.isSpiderMonkeyInterface()
17461 or type.isUnion()
17462 or type.isRecord()
17464 if type.nullable():
17465 memberNullable = memberData
17466 memberData = "%s.Value()" % memberData
17467 if type.isSequence():
17468 trace = CGGeneric("DoTraceSequence(trc, %s);\n" % memberData)
17469 elif type.isDictionary():
17470 trace = CGGeneric("%s.TraceDictionary(trc);\n" % memberData)
17471 elif type.isUnion():
17472 trace = CGGeneric("%s.TraceUnion(trc);\n" % memberData)
17473 elif type.isRecord():
17474 trace = CGGeneric("TraceRecord(trc, %s);\n" % memberData)
17475 else:
17476 assert type.isSpiderMonkeyInterface()
17477 trace = CGGeneric("%s.TraceSelf(trc);\n" % memberData)
17478 if type.nullable():
17479 trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable)
17480 else:
17481 assert False # unknown type
17483 if member.canHaveMissingValue():
17484 trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc)
17486 return trace.define()
17488 def getMemberInitializer(self, memberInfo):
17490 Get the right initializer for the member. Most members don't need one,
17491 but we need to pre-initialize 'object' that have a default value or are
17492 required (and hence are not inside Optional), so they're safe to trace
17493 at all times. And we can optimize a bit for dictionary-typed members.
17495 member, _ = memberInfo
17496 if member.canHaveMissingValue():
17497 # Allowed missing value means no need to set it up front, since it's
17498 # inside an Optional and won't get traced until it's actually set
17499 # up.
17500 return None
17501 type = member.type
17502 if type.isDictionary():
17503 # When we construct ourselves, we don't want to init our member
17504 # dictionaries. Either we're being constructed-but-not-initialized
17505 # ourselves (and then we don't want to init them) or we're about to
17506 # init ourselves and then we'll init them anyway.
17507 return CGDictionary.getNonInitializingCtorArg()
17508 return initializerForType(type)
17510 def getMemberSourceDescription(self, member):
17511 return "'%s' member of %s" % (
17512 member.identifier.name,
17513 self.dictionary.identifier.name,
17516 @staticmethod
17517 def makeIdName(name):
17518 return IDLToCIdentifier(name) + "_id"
17520 @staticmethod
17521 def getNonInitializingCtorArg():
17522 return "FastDictionaryInitializer()"
17524 @staticmethod
17525 def isDictionaryCopyConstructible(dictionary):
17526 if dictionary.parent and not CGDictionary.isDictionaryCopyConstructible(
17527 dictionary.parent
17529 return False
17530 return all(isTypeCopyConstructible(m.type) for m in dictionary.members)
17532 @staticmethod
17533 def typeSafeToJSONify(type):
17535 Determine whether the given type is safe to convert to JSON. The
17536 restriction is that this needs to be safe while in a global controlled
17537 by an adversary, and "safe" means no side-effects when the JS
17538 representation of this type is converted to JSON. That means that we
17539 have to be pretty restrictive about what things we can allow. For
17540 example, "object" is out, because it may have accessor properties on it.
17542 if type.nullable():
17543 # Converting null to JSON is always OK.
17544 return CGDictionary.typeSafeToJSONify(type.inner)
17546 if type.isSequence():
17547 # Sequences are arrays we create ourselves, with no holes. They
17548 # should be safe if their contents are safe, as long as we suppress
17549 # invocation of .toJSON on objects.
17550 return CGDictionary.typeSafeToJSONify(type.inner)
17552 if type.isUnion():
17553 # OK if everything in it is ok.
17554 return all(CGDictionary.typeSafeToJSONify(t) for t in type.flatMemberTypes)
17556 if type.isDictionary():
17557 # OK if the dictionary is OK
17558 return CGDictionary.dictionarySafeToJSONify(type.inner)
17560 if type.isString() or type.isEnum():
17561 # Strings are always OK.
17562 return True
17564 if type.isPrimitive():
17565 # Primitives (numbers and booleans) are ok, as long as
17566 # they're not unrestricted float/double.
17567 return not type.isFloat() or not type.isUnrestricted()
17569 if type.isRecord():
17570 # Records are okay, as long as the value type is.
17571 # Per spec, only strings are allowed as keys.
17572 return CGDictionary.typeSafeToJSONify(type.inner)
17574 return False
17576 @staticmethod
17577 def dictionarySafeToJSONify(dictionary):
17578 # The dictionary itself is OK, so we're good if all our types are.
17579 return all(CGDictionary.typeSafeToJSONify(m.type) for m in dictionary.members)
17582 class CGRegisterWorkerBindings(CGAbstractMethod):
17583 def __init__(self, config):
17584 CGAbstractMethod.__init__(
17585 self,
17586 None,
17587 "RegisterWorkerBindings",
17588 "bool",
17589 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
17591 self.config = config
17593 def definition_body(self):
17594 descriptors = self.config.getDescriptors(
17595 hasInterfaceObject=True, isExposedInAnyWorker=True, register=True
17597 conditions = []
17598 for desc in descriptors:
17599 bindingNS = toBindingNamespace(desc.name)
17600 condition = "!%s::GetConstructorObject(aCx)" % bindingNS
17601 if desc.isExposedConditionally():
17602 condition = (
17603 "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
17605 conditions.append(condition)
17606 lines = [
17607 CGIfWrapper(CGGeneric("return false;\n"), condition)
17608 for condition in conditions
17610 lines.append(CGGeneric("return true;\n"))
17611 return CGList(lines, "\n").define()
17614 class CGRegisterWorkerDebuggerBindings(CGAbstractMethod):
17615 def __init__(self, config):
17616 CGAbstractMethod.__init__(
17617 self,
17618 None,
17619 "RegisterWorkerDebuggerBindings",
17620 "bool",
17621 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
17623 self.config = config
17625 def definition_body(self):
17626 descriptors = self.config.getDescriptors(
17627 hasInterfaceObject=True, isExposedInWorkerDebugger=True, register=True
17629 conditions = []
17630 for desc in descriptors:
17631 bindingNS = toBindingNamespace(desc.name)
17632 condition = "!%s::GetConstructorObject(aCx)" % bindingNS
17633 if desc.isExposedConditionally():
17634 condition = (
17635 "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
17637 conditions.append(condition)
17638 lines = [
17639 CGIfWrapper(CGGeneric("return false;\n"), condition)
17640 for condition in conditions
17642 lines.append(CGGeneric("return true;\n"))
17643 return CGList(lines, "\n").define()
17646 class CGRegisterWorkletBindings(CGAbstractMethod):
17647 def __init__(self, config):
17648 CGAbstractMethod.__init__(
17649 self,
17650 None,
17651 "RegisterWorkletBindings",
17652 "bool",
17653 [Argument("JSContext*", "aCx"), Argument("JS::Handle<JSObject*>", "aObj")],
17655 self.config = config
17657 def definition_body(self):
17658 descriptors = self.config.getDescriptors(
17659 hasInterfaceObject=True, isExposedInAnyWorklet=True, register=True
17661 conditions = []
17662 for desc in descriptors:
17663 bindingNS = toBindingNamespace(desc.name)
17664 condition = "!%s::GetConstructorObject(aCx)" % bindingNS
17665 if desc.isExposedConditionally():
17666 condition = (
17667 "%s::ConstructorEnabled(aCx, aObj) && " % bindingNS + condition
17669 conditions.append(condition)
17670 lines = [
17671 CGIfWrapper(CGGeneric("return false;\n"), condition)
17672 for condition in conditions
17674 lines.append(CGGeneric("return true;\n"))
17675 return CGList(lines, "\n").define()
17678 def getGlobalNames(config):
17679 names = []
17680 for desc in config.getDescriptors(registersGlobalNamesOnWindow=True):
17681 names.append((desc.name, desc))
17682 names.extend(
17683 (n.identifier.name, desc) for n in desc.interface.legacyFactoryFunctions
17685 names.extend((n, desc) for n in desc.interface.legacyWindowAliases)
17686 return names
17689 class CGGlobalNames(CGGeneric):
17690 def __init__(self, config):
17691 currentOffset = 0
17692 strings = []
17693 entries = []
17694 for name, desc in getGlobalNames(config):
17695 # Add a string to the list.
17696 offset = currentOffset
17697 strings.append('/* %i */ "%s\\0"' % (offset, name))
17698 currentOffset += len(name) + 1 # Add trailing null.
17700 # Generate the entry declaration
17701 # XXX(nika): mCreate & mEnabled require relocations. If we want to
17702 # reduce those, we could move them into separate tables.
17703 nativeEntry = fill(
17706 /* mNameOffset */ ${nameOffset}, // "${name}"
17707 /* mNameLength */ ${nameLength},
17708 /* mConstructorId */ constructors::id::${realname},
17709 /* mCreate */ ${realname}_Binding::CreateInterfaceObjects,
17710 /* mEnabled */ ${enabled}
17712 """,
17713 nameOffset=offset,
17714 nameLength=len(name),
17715 name=name,
17716 realname=desc.name,
17717 enabled=(
17718 "%s_Binding::ConstructorEnabled" % desc.name
17719 if desc.isExposedConditionally()
17720 else "nullptr"
17724 entries.append((name, nativeEntry))
17726 # Unfortunately, when running tests, we may have no entries.
17727 # PerfectHash will assert if we give it an empty set of entries, so we
17728 # just generate a dummy value.
17729 if len(entries) == 0:
17730 CGGeneric.__init__(
17731 self,
17732 define=dedent(
17734 static_assert(false, "No WebIDL global name entries!");
17738 return
17740 # Build the perfect hash function.
17741 phf = PerfectHash(entries, GLOBAL_NAMES_PHF_SIZE)
17743 # Generate code for the PHF
17744 phfCodegen = phf.codegen(
17745 "WebIDLGlobalNameHash::sEntries", "WebIDLNameTableEntry"
17747 entries = phfCodegen.gen_entries(lambda e: e[1])
17748 getter = phfCodegen.gen_jslinearstr_getter(
17749 name="WebIDLGlobalNameHash::GetEntry",
17750 return_type="const WebIDLNameTableEntry*",
17751 return_entry=dedent(
17753 if (JS_LinearStringEqualsAscii(aKey, sNames + entry.mNameOffset, entry.mNameLength)) {
17754 return &entry;
17756 return nullptr;
17761 define = fill(
17763 const uint32_t WebIDLGlobalNameHash::sCount = ${count};
17765 const char WebIDLGlobalNameHash::sNames[] =
17766 $*{strings}
17768 $*{entries}
17770 $*{getter}
17772 """,
17773 count=len(phf.entries),
17774 strings="\n".join(strings) + ";\n",
17775 entries=entries,
17776 getter=getter,
17778 CGGeneric.__init__(self, define=define)
17781 def dependencySortObjects(objects, dependencyGetter, nameGetter):
17783 Sort IDL objects with dependencies on each other such that if A
17784 depends on B then B will come before A. This is needed for
17785 declaring C++ classes in the right order, for example. Objects
17786 that have no dependencies are just sorted by name.
17788 objects should be something that can produce a set of objects
17789 (e.g. a set, iterator, list, etc).
17791 dependencyGetter is something that, given an object, should return
17792 the set of objects it depends on.
17794 # XXXbz this will fail if we have two webidl files F1 and F2 such that F1
17795 # declares an object which depends on an object in F2, and F2 declares an
17796 # object (possibly a different one!) that depends on an object in F1. The
17797 # good news is that I expect this to never happen.
17798 sortedObjects = []
17799 objects = set(objects)
17800 while len(objects) != 0:
17801 # Find the dictionaries that don't depend on anything else
17802 # anymore and move them over.
17803 toMove = [o for o in objects if len(dependencyGetter(o) & objects) == 0]
17804 if len(toMove) == 0:
17805 raise TypeError(
17806 "Loop in dependency graph\n" + "\n".join(o.location for o in objects)
17808 objects = objects - set(toMove)
17809 sortedObjects.extend(sorted(toMove, key=nameGetter))
17810 return sortedObjects
17813 class ForwardDeclarationBuilder:
17815 Create a canonical representation of a set of namespaced forward
17816 declarations.
17819 def __init__(self):
17821 The set of declarations is represented as a tree of nested namespaces.
17822 Each tree node has a set of declarations |decls| and a dict |children|.
17823 Each declaration is a pair consisting of the class name and a boolean
17824 that is true iff the class is really a struct. |children| maps the
17825 names of inner namespaces to the declarations in that namespace.
17827 self.decls = set()
17828 self.children = {}
17830 def _ensureNonTemplateType(self, type):
17831 if "<" in type:
17832 # This is a templated type. We don't really know how to
17833 # forward-declare those, and trying to do it naively is not going to
17834 # go well (e.g. we may have :: characters inside the type we're
17835 # templated on!). Just bail out.
17836 raise TypeError(
17837 "Attempt to use ForwardDeclarationBuilder on "
17838 "templated type %s. We don't know how to do that "
17839 "yet." % type
17842 def _listAdd(self, namespaces, name, isStruct=False):
17844 Add a forward declaration, where |namespaces| is a list of namespaces.
17845 |name| should not contain any other namespaces.
17847 if namespaces:
17848 child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder())
17849 child._listAdd(namespaces[1:], name, isStruct)
17850 else:
17851 assert "::" not in name
17852 self.decls.add((name, isStruct))
17854 def addInMozillaDom(self, name, isStruct=False):
17856 Add a forward declaration to the mozilla::dom:: namespace. |name| should not
17857 contain any other namespaces.
17859 self._ensureNonTemplateType(name)
17860 self._listAdd(["mozilla", "dom"], name, isStruct)
17862 def add(self, nativeType, isStruct=False):
17864 Add a forward declaration, where |nativeType| is a string containing
17865 the type and its namespaces, in the usual C++ way.
17867 self._ensureNonTemplateType(nativeType)
17868 components = nativeType.split("::")
17869 self._listAdd(components[:-1], components[-1], isStruct)
17871 def _build(self, atTopLevel):
17873 Return a codegenerator for the forward declarations.
17875 decls = []
17876 if self.decls:
17877 decls.append(
17878 CGList(
17880 CGClassForwardDeclare(cname, isStruct)
17881 for cname, isStruct in sorted(self.decls)
17885 for namespace, child in sorted(six.iteritems(self.children)):
17886 decls.append(
17887 CGNamespace(namespace, child._build(atTopLevel=False), declareOnly=True)
17890 cg = CGList(decls, "\n")
17891 if not atTopLevel and len(decls) + len(self.decls) > 1:
17892 cg = CGWrapper(cg, pre="\n", post="\n")
17893 return cg
17895 def build(self):
17896 return self._build(atTopLevel=True)
17898 def forwardDeclareForType(self, t, config):
17899 t = t.unroll()
17900 if t.isGeckoInterface():
17901 name = t.inner.identifier.name
17902 try:
17903 desc = config.getDescriptor(name)
17904 self.add(desc.nativeType)
17905 except NoSuchDescriptorError:
17906 pass
17908 # Note: SpiderMonkey interfaces are typedefs, so can't be
17909 # forward-declared
17910 elif t.isPromise():
17911 self.addInMozillaDom("Promise")
17912 elif t.isCallback():
17913 self.addInMozillaDom(t.callback.identifier.name)
17914 elif t.isDictionary():
17915 self.addInMozillaDom(t.inner.identifier.name, isStruct=True)
17916 elif t.isCallbackInterface():
17917 self.addInMozillaDom(t.inner.identifier.name)
17918 elif t.isUnion():
17919 # Forward declare both the owning and non-owning version,
17920 # since we don't know which one we might want
17921 self.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
17922 self.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
17923 elif t.isRecord():
17924 self.forwardDeclareForType(t.inner, config)
17925 # Don't need to do anything for void, primitive, string, any or object.
17926 # There may be some other cases we are missing.
17929 class CGForwardDeclarations(CGWrapper):
17931 Code generate the forward declarations for a header file.
17932 additionalDeclarations is a list of tuples containing a classname and a
17933 boolean. If the boolean is true we will declare a struct, otherwise we'll
17934 declare a class.
17937 def __init__(
17938 self,
17939 config,
17940 descriptors,
17941 callbacks,
17942 dictionaries,
17943 callbackInterfaces,
17944 additionalDeclarations=[],
17946 builder = ForwardDeclarationBuilder()
17948 # Needed for at least Wrap.
17949 for d in descriptors:
17950 # If this is a generated iterator interface, we only create these
17951 # in the generated bindings, and don't need to forward declare.
17952 if d.interface.isIteratorInterface():
17953 continue
17954 builder.add(d.nativeType)
17955 if d.interface.isSerializable():
17956 builder.add("nsIGlobalObject")
17957 # If we're an interface and we have a maplike/setlike declaration,
17958 # we'll have helper functions exposed to the native side of our
17959 # bindings, which will need to show up in the header. If either of
17960 # our key/value types are interfaces, they'll be passed as
17961 # arguments to helper functions, and they'll need to be forward
17962 # declared in the header.
17963 if d.interface.maplikeOrSetlikeOrIterable:
17964 if d.interface.maplikeOrSetlikeOrIterable.hasKeyType():
17965 builder.forwardDeclareForType(
17966 d.interface.maplikeOrSetlikeOrIterable.keyType, config
17968 if d.interface.maplikeOrSetlikeOrIterable.hasValueType():
17969 builder.forwardDeclareForType(
17970 d.interface.maplikeOrSetlikeOrIterable.valueType, config
17973 # We just about always need NativePropertyHooks
17974 builder.addInMozillaDom("NativePropertyHooks", isStruct=True)
17975 builder.addInMozillaDom("ProtoAndIfaceCache")
17976 # Add the atoms cache type, even if we don't need it.
17977 for d in descriptors:
17978 # Iterators have native types that are template classes, so
17979 # creating an 'Atoms' cache type doesn't work for them, and is one
17980 # of the cases where we don't need it anyways.
17981 if d.interface.isIteratorInterface():
17982 continue
17983 builder.add(d.nativeType + "Atoms", isStruct=True)
17985 for callback in callbacks:
17986 builder.addInMozillaDom(callback.identifier.name)
17987 for t in getTypesFromCallback(callback):
17988 builder.forwardDeclareForType(t, config)
17990 for d in callbackInterfaces:
17991 builder.add(d.nativeType)
17992 builder.add(d.nativeType + "Atoms", isStruct=True)
17993 for t in getTypesFromDescriptor(d):
17994 builder.forwardDeclareForType(t, config)
17995 if d.hasCEReactions():
17996 builder.addInMozillaDom("DocGroup")
17998 for d in dictionaries:
17999 if len(d.members) > 0:
18000 builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True)
18001 for t in getTypesFromDictionary(d):
18002 builder.forwardDeclareForType(t, config)
18004 for className, isStruct in additionalDeclarations:
18005 builder.add(className, isStruct=isStruct)
18007 CGWrapper.__init__(self, builder.build())
18010 class CGBindingRoot(CGThing):
18012 Root codegen class for binding generation. Instantiate the class, and call
18013 declare or define to generate header or cpp code (respectively).
18016 def __init__(self, config, prefix, webIDLFile):
18017 bindingHeaders = dict.fromkeys(
18018 ("mozilla/dom/NonRefcountedDOMObject.h", "MainThreadUtils.h"), True
18020 bindingDeclareHeaders = dict.fromkeys(
18022 "mozilla/dom/BindingDeclarations.h",
18023 "mozilla/dom/Nullable.h",
18025 True,
18028 descriptors = config.getDescriptors(
18029 webIDLFile=webIDLFile, hasInterfaceOrInterfacePrototypeObject=True
18032 unionTypes = UnionsForFile(config, webIDLFile)
18035 unionHeaders,
18036 unionImplheaders,
18037 unionDeclarations,
18038 traverseMethods,
18039 unlinkMethods,
18040 unionStructs,
18041 ) = UnionTypes(unionTypes, config)
18043 bindingDeclareHeaders.update(dict.fromkeys(unionHeaders, True))
18044 bindingHeaders.update(dict.fromkeys(unionImplheaders, True))
18045 bindingDeclareHeaders["mozilla/dom/UnionMember.h"] = len(unionStructs) > 0
18046 bindingDeclareHeaders["mozilla/dom/FakeString.h"] = len(unionStructs) > 0
18047 # BindingUtils.h is only needed for SetToObject.
18048 # If it stops being inlined or stops calling CallerSubsumes
18049 # both this bit and the bit in UnionTypes can be removed.
18050 bindingDeclareHeaders["mozilla/dom/BindingUtils.h"] = any(
18051 d.isObject() for t in unionTypes for d in t.flatMemberTypes
18053 bindingDeclareHeaders["mozilla/dom/IterableIterator.h"] = any(
18054 d.interface.isIteratorInterface() or d.interface.isIterable()
18055 for d in descriptors
18058 def memberNeedsSubjectPrincipal(d, m):
18059 if m.isAttr():
18060 return (
18061 "needsSubjectPrincipal" in d.getExtendedAttributes(m, getter=True)
18062 ) or (
18063 not m.readonly
18064 and "needsSubjectPrincipal"
18065 in d.getExtendedAttributes(m, setter=True)
18067 return m.isMethod() and "needsSubjectPrincipal" in d.getExtendedAttributes(
18071 if any(
18072 memberNeedsSubjectPrincipal(d, m)
18073 for d in descriptors
18074 for m in d.interface.members
18076 bindingHeaders["mozilla/BasePrincipal.h"] = True
18077 bindingHeaders["nsJSPrincipals.h"] = True
18079 # The conditions for which we generate profiler labels are fairly
18080 # complicated. The check below is a little imprecise to make it simple.
18081 # It includes the profiler header in all cases where it is necessary and
18082 # generates only a few false positives.
18083 bindingHeaders["mozilla/ProfilerLabels.h"] = any(
18084 # constructor profiler label
18085 d.interface.legacyFactoryFunctions
18086 or (d.interface.hasInterfaceObject() and d.interface.ctor())
18087 or any(
18088 # getter/setter profiler labels
18089 m.isAttr()
18090 # method profiler label
18091 or m.isMethod()
18092 for m in d.interface.members
18094 for d in descriptors
18097 def descriptorHasCrossOriginProperties(desc):
18098 def hasCrossOriginProperty(m):
18099 props = memberProperties(m, desc)
18100 return (
18101 props.isCrossOriginMethod
18102 or props.isCrossOriginGetter
18103 or props.isCrossOriginSetter
18106 return any(hasCrossOriginProperty(m) for m in desc.interface.members)
18108 bindingDeclareHeaders["mozilla/dom/RemoteObjectProxy.h"] = any(
18109 descriptorHasCrossOriginProperties(d) for d in descriptors
18111 bindingDeclareHeaders["jsapi.h"] = any(
18112 descriptorHasCrossOriginProperties(d) for d in descriptors
18114 bindingDeclareHeaders["js/TypeDecls.h"] = not bindingDeclareHeaders["jsapi.h"]
18115 bindingDeclareHeaders["js/RootingAPI.h"] = not bindingDeclareHeaders["jsapi.h"]
18117 def descriptorHasIteratorAlias(desc):
18118 def hasIteratorAlias(m):
18119 return m.isMethod() and "@@iterator" in m.aliases
18121 return any(hasIteratorAlias(m) for m in desc.interface.members)
18123 bindingHeaders["js/Symbol.h"] = any(
18124 descriptorHasIteratorAlias(d) for d in descriptors
18127 bindingHeaders["js/shadow/Object.h"] = any(
18128 d.interface.hasMembersInSlots() for d in descriptors
18131 # The symbols supplied by this header are used so ubiquitously it's not
18132 # worth the effort delineating the exact dependency, if it can't be done
18133 # *at* the places where their definitions are required.
18134 bindingHeaders["js/experimental/JitInfo.h"] = True
18136 # JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, and
18137 # JS::SetReservedSlot are also used too many places to restate
18138 # dependency logic.
18139 bindingHeaders["js/Object.h"] = True
18141 def descriptorDeprecated(desc):
18142 iface = desc.interface
18143 return any(
18144 m.getExtendedAttribute("Deprecated") for m in iface.members + [iface]
18147 bindingHeaders["mozilla/dom/Document.h"] = any(
18148 descriptorDeprecated(d) for d in descriptors
18151 bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any(
18152 d.concrete and d.proxy for d in descriptors
18155 bindingHeaders["js/String.h"] = any(
18156 d.needsMissingPropUseCounters for d in descriptors
18159 hasCrossOriginObjects = any(
18160 d.concrete and d.isMaybeCrossOriginObject() for d in descriptors
18162 bindingHeaders["mozilla/dom/MaybeCrossOriginObject.h"] = hasCrossOriginObjects
18163 bindingHeaders["AccessCheck.h"] = hasCrossOriginObjects
18164 hasCEReactions = any(d.hasCEReactions() for d in descriptors)
18165 bindingHeaders["mozilla/dom/CustomElementRegistry.h"] = hasCEReactions
18166 bindingHeaders["mozilla/dom/DocGroup.h"] = hasCEReactions
18168 def descriptorHasChromeOnly(desc):
18169 ctor = desc.interface.ctor()
18171 return (
18172 any(
18173 isChromeOnly(a) or needsContainsHack(a) or needsCallerType(a)
18174 for a in desc.interface.members
18176 or desc.interface.getExtendedAttribute("ChromeOnly") is not None
18178 # JS-implemented interfaces with an interface object get a
18179 # chromeonly _create method. And interfaces with an
18180 # interface object might have a ChromeOnly constructor.
18182 desc.interface.hasInterfaceObject()
18183 and (
18184 desc.interface.isJSImplemented()
18185 or (ctor and isChromeOnly(ctor))
18190 # XXXkhuey ugly hack but this is going away soon.
18191 bindingHeaders["xpcprivate.h"] = webIDLFile.endswith("EventTarget.webidl")
18193 hasThreadChecks = any(d.hasThreadChecks() for d in descriptors)
18194 bindingHeaders["nsThreadUtils.h"] = hasThreadChecks
18196 dictionaries = config.getDictionaries(webIDLFile)
18198 def dictionaryHasChromeOnly(dictionary):
18199 while dictionary:
18200 if any(isChromeOnly(m) for m in dictionary.members):
18201 return True
18202 dictionary = dictionary.parent
18203 return False
18205 def needsNonSystemPrincipal(member):
18206 return (
18207 member.getExtendedAttribute("NeedsSubjectPrincipal") == ["NonSystem"]
18208 or member.getExtendedAttribute("SetterNeedsSubjectPrincipal")
18209 == ["NonSystem"]
18210 or member.getExtendedAttribute("GetterNeedsSubjectPrincipal")
18211 == ["NonSystem"]
18214 def descriptorNeedsNonSystemPrincipal(d):
18215 return any(needsNonSystemPrincipal(m) for m in d.interface.members)
18217 def descriptorHasPrefDisabler(desc):
18218 iface = desc.interface
18219 return any(
18220 PropertyDefiner.getControllingCondition(m, desc).hasDisablers()
18221 for m in iface.members
18222 if (m.isMethod() or m.isAttr() or m.isConst())
18225 def addPrefHeaderForObject(bindingHeaders, obj):
18227 obj might be a dictionary member or an interface.
18229 if obj is not None:
18230 pref = PropertyDefiner.getStringAttr(obj, "Pref")
18231 if pref:
18232 bindingHeaders[prefHeader(pref)] = True
18234 def addPrefHeadersForDictionary(bindingHeaders, dictionary):
18235 while dictionary:
18236 for m in dictionary.members:
18237 addPrefHeaderForObject(bindingHeaders, m)
18238 dictionary = dictionary.parent
18240 for d in dictionaries:
18241 addPrefHeadersForDictionary(bindingHeaders, d)
18242 for d in descriptors:
18243 interface = d.interface
18244 addPrefHeaderForObject(bindingHeaders, interface)
18245 addPrefHeaderForObject(bindingHeaders, interface.ctor())
18247 bindingHeaders["mozilla/dom/WebIDLPrefs.h"] = any(
18248 descriptorHasPrefDisabler(d) for d in descriptors
18250 bindingHeaders["nsContentUtils.h"] = (
18251 any(descriptorHasChromeOnly(d) for d in descriptors)
18252 or any(descriptorNeedsNonSystemPrincipal(d) for d in descriptors)
18253 or any(dictionaryHasChromeOnly(d) for d in dictionaries)
18255 hasNonEmptyDictionaries = any(len(dict.members) > 0 for dict in dictionaries)
18256 callbacks = config.getCallbacks(webIDLFile)
18257 callbackDescriptors = config.getDescriptors(
18258 webIDLFile=webIDLFile, isCallback=True
18260 jsImplemented = config.getDescriptors(
18261 webIDLFile=webIDLFile, isJSImplemented=True
18263 bindingDeclareHeaders["nsWeakReference.h"] = jsImplemented
18264 bindingDeclareHeaders["mozilla/dom/PrototypeList.h"] = descriptors
18265 bindingHeaders["nsIGlobalObject.h"] = jsImplemented
18266 bindingHeaders["AtomList.h"] = (
18267 hasNonEmptyDictionaries or jsImplemented or callbackDescriptors
18270 if callbackDescriptors:
18271 bindingDeclareHeaders["mozilla/ErrorResult.h"] = True
18273 def descriptorClearsPropsInSlots(descriptor):
18274 if not descriptor.wrapperCache:
18275 return False
18276 return any(
18277 m.isAttr() and m.getExtendedAttribute("StoreInSlot")
18278 for m in descriptor.interface.members
18281 bindingHeaders["nsJSUtils.h"] = any(
18282 descriptorClearsPropsInSlots(d) for d in descriptors
18285 # Make sure we can sanely use binding_detail in generated code.
18286 cgthings = [
18287 CGGeneric(
18288 dedent(
18290 namespace binding_detail {}; // Just to make sure it's known as a namespace
18291 using namespace mozilla::dom::binding_detail;
18297 # Do codegen for all the enums
18298 enums = config.getEnums(webIDLFile)
18299 cgthings.extend(CGEnum(e) for e in enums)
18301 bindingDeclareHeaders["mozilla/Span.h"] = enums
18302 bindingDeclareHeaders["mozilla/ArrayUtils.h"] = enums
18304 hasCode = descriptors or callbackDescriptors or dictionaries or callbacks
18305 bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode
18306 bindingHeaders["mozilla/OwningNonNull.h"] = hasCode
18307 bindingHeaders["<type_traits>"] = hasCode
18308 bindingHeaders["mozilla/dom/BindingDeclarations.h"] = not hasCode and enums
18310 bindingHeaders["WrapperFactory.h"] = descriptors
18311 bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors
18312 bindingHeaders["mozilla/dom/ScriptSettings.h"] = dictionaries # AutoJSAPI
18313 # Ensure we see our enums in the generated .cpp file, for the ToJSValue
18314 # method body. Also ensure that we see jsapi.h.
18315 if enums:
18316 bindingHeaders[CGHeaders.getDeclarationFilename(enums[0])] = True
18317 bindingHeaders["jsapi.h"] = True
18319 # For things that have [UseCounter] or [InstrumentedProps]
18320 descriptorsHaveUseCounters = any(
18321 m.getExtendedAttribute("UseCounter")
18322 for d in descriptors
18323 for m in d.interface.members
18325 descriptorsHaveInstrumentedProps = any(
18326 d.instrumentedProps for d in descriptors if d.concrete
18328 descriptorsHaveNeedsMissingPropUseCounters = any(
18329 d.needsMissingPropUseCounters for d in descriptors if d.concrete
18332 bindingHeaders["mozilla/UseCounter.h"] = (
18333 descriptorsHaveUseCounters or descriptorsHaveInstrumentedProps
18335 # Make sure to not overwrite existing pref header bits!
18336 bindingHeaders[prefHeader(MISSING_PROP_PREF)] = (
18337 bindingHeaders.get(prefHeader(MISSING_PROP_PREF))
18338 or descriptorsHaveNeedsMissingPropUseCounters
18340 bindingHeaders["mozilla/dom/SimpleGlobalObject.h"] = any(
18341 CGDictionary.dictionarySafeToJSONify(d) for d in dictionaries
18343 bindingHeaders["XrayWrapper.h"] = any(
18344 d.wantsXrays and d.wantsXrayExpandoClass for d in descriptors
18346 bindingHeaders["mozilla/dom/XrayExpandoClass.h"] = any(
18347 d.wantsXrays for d in descriptors
18349 bindingHeaders["mozilla/dom/StructuredCloneTags.h"] = any(
18350 d.interface.isSerializable() for d in descriptors
18352 bindingHeaders["mozilla/Atomics.h"] = any(d.wantsXrays for d in descriptors)
18354 for ancestor in (findAncestorWithInstrumentedProps(d) for d in descriptors):
18355 if not ancestor:
18356 continue
18357 bindingHeaders[CGHeaders.getDeclarationFilename(ancestor)] = True
18359 cgthings.extend(traverseMethods)
18360 cgthings.extend(unlinkMethods)
18362 # Do codegen for all the dictionaries. We have to be a bit careful
18363 # here, because we have to generate these in order from least derived
18364 # to most derived so that class inheritance works out. We also have to
18365 # generate members before the dictionary that contains them.
18367 def getDependenciesFromType(type):
18368 if type.isDictionary():
18369 return set([type.unroll().inner])
18370 if type.isSequence():
18371 return getDependenciesFromType(type.unroll())
18372 if type.isUnion():
18373 return set([type.unroll()])
18374 return set()
18376 def getDependencies(unionTypeOrDictionary):
18377 if isinstance(unionTypeOrDictionary, IDLDictionary):
18378 deps = set()
18379 if unionTypeOrDictionary.parent:
18380 deps.add(unionTypeOrDictionary.parent)
18381 for member in unionTypeOrDictionary.members:
18382 deps |= getDependenciesFromType(member.type)
18383 return deps
18385 assert unionTypeOrDictionary.isType() and unionTypeOrDictionary.isUnion()
18386 deps = set()
18387 for member in unionTypeOrDictionary.flatMemberTypes:
18388 deps |= getDependenciesFromType(member)
18389 return deps
18391 def getName(unionTypeOrDictionary):
18392 if isinstance(unionTypeOrDictionary, IDLDictionary):
18393 return unionTypeOrDictionary.identifier.name
18395 assert unionTypeOrDictionary.isType() and unionTypeOrDictionary.isUnion()
18396 return unionTypeOrDictionary.name
18398 for t in dependencySortObjects(
18399 dictionaries + unionStructs, getDependencies, getName
18401 if t.isDictionary():
18402 cgthings.append(CGDictionary(t, config))
18403 else:
18404 assert t.isUnion()
18405 cgthings.append(CGUnionStruct(t, config))
18406 cgthings.append(CGUnionStruct(t, config, True))
18408 # Do codegen for all the callbacks.
18409 cgthings.extend(CGCallbackFunction(c, config) for c in callbacks)
18411 cgthings.extend(
18412 [CGNamespace("binding_detail", CGFastCallback(c)) for c in callbacks]
18415 # Do codegen for all the descriptors
18416 cgthings.extend([CGDescriptor(x) for x in descriptors])
18418 # Do codegen for all the callback interfaces.
18419 cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors])
18421 cgthings.extend(
18423 CGNamespace("binding_detail", CGFastCallback(x.interface))
18424 for x in callbackDescriptors
18428 # Do codegen for JS implemented classes
18429 def getParentDescriptor(desc):
18430 if not desc.interface.parent:
18431 return set()
18432 return {desc.getDescriptor(desc.interface.parent.identifier.name)}
18434 for x in dependencySortObjects(
18435 jsImplemented, getParentDescriptor, lambda d: d.interface.identifier.name
18437 cgthings.append(
18438 CGCallbackInterface(x, spiderMonkeyInterfacesAreStructs=True)
18440 cgthings.append(CGJSImplClass(x))
18442 # And make sure we have the right number of newlines at the end
18443 curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
18445 # Wrap all of that in our namespaces.
18446 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, pre="\n"))
18448 curr = CGList(
18450 CGForwardDeclarations(
18451 config,
18452 descriptors,
18453 callbacks,
18454 dictionaries,
18455 callbackDescriptors + jsImplemented,
18456 additionalDeclarations=unionDeclarations,
18458 curr,
18460 "\n",
18463 # Add header includes.
18464 bindingHeaders = [
18465 header for header, include in six.iteritems(bindingHeaders) if include
18467 bindingDeclareHeaders = [
18468 header
18469 for header, include in six.iteritems(bindingDeclareHeaders)
18470 if include
18473 curr = CGHeaders(
18474 descriptors,
18475 dictionaries,
18476 callbacks,
18477 callbackDescriptors,
18478 bindingDeclareHeaders,
18479 bindingHeaders,
18480 prefix,
18481 curr,
18482 config,
18483 jsImplemented,
18486 # Add include guards.
18487 curr = CGIncludeGuard(prefix, curr)
18489 # Add the auto-generated comment.
18490 curr = CGWrapper(
18491 curr,
18492 pre=(
18493 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT % os.path.basename(webIDLFile)
18497 # Store the final result.
18498 self.root = curr
18500 def declare(self):
18501 return stripTrailingWhitespace(self.root.declare())
18503 def define(self):
18504 return stripTrailingWhitespace(self.root.define())
18506 def deps(self):
18507 return self.root.deps()
18510 class CGNativeMember(ClassMethod):
18511 def __init__(
18512 self,
18513 descriptorProvider,
18514 member,
18515 name,
18516 signature,
18517 extendedAttrs,
18518 breakAfter=True,
18519 passJSBitsAsNeeded=True,
18520 visibility="public",
18521 spiderMonkeyInterfacesAreStructs=True,
18522 variadicIsSequence=False,
18523 resultNotAddRefed=False,
18524 virtual=False,
18525 override=False,
18526 canRunScript=False,
18529 If spiderMonkeyInterfacesAreStructs is false, SpiderMonkey interfaces
18530 will be passed as JS::Handle<JSObject*>. If it's true they will be
18531 passed as one of the dom::SpiderMonkeyInterfaceObjectStorage subclasses.
18533 If passJSBitsAsNeeded is false, we don't automatically pass in a
18534 JSContext* or a JSObject* based on the return and argument types. We
18535 can still pass it based on 'implicitJSContext' annotations.
18537 self.descriptorProvider = descriptorProvider
18538 self.member = member
18539 self.extendedAttrs = extendedAttrs
18540 self.resultAlreadyAddRefed = not resultNotAddRefed
18541 self.passJSBitsAsNeeded = passJSBitsAsNeeded
18542 self.spiderMonkeyInterfacesAreStructs = spiderMonkeyInterfacesAreStructs
18543 self.variadicIsSequence = variadicIsSequence
18544 breakAfterSelf = "\n" if breakAfter else ""
18545 ClassMethod.__init__(
18546 self,
18547 name,
18548 self.getReturnType(signature[0], False),
18549 self.getArgs(signature[0], signature[1]),
18550 static=member.isStatic(),
18551 # Mark our getters, which are attrs that
18552 # have a non-void return type, as const.
18553 const=(
18554 not member.isStatic() and member.isAttr() and not signature[0].isVoid()
18556 breakAfterReturnDecl=" ",
18557 breakAfterSelf=breakAfterSelf,
18558 visibility=visibility,
18559 virtual=virtual,
18560 override=override,
18561 canRunScript=canRunScript,
18564 def getReturnType(self, type, isMember):
18565 return self.getRetvalInfo(type, isMember)[0]
18567 def getRetvalInfo(self, type, isMember):
18569 Returns a tuple:
18571 The first element is the type declaration for the retval
18573 The second element is a default value that can be used on error returns.
18574 For cases whose behavior depends on isMember, the second element will be
18575 None if isMember is true.
18577 The third element is a template for actually returning a value stored in
18578 "${declName}" and "${holderName}". This means actually returning it if
18579 we're not outparam, else assigning to the "retval" outparam. If
18580 isMember is true, this can be None, since in that case the caller will
18581 never examine this value.
18583 if type.isVoid():
18584 return "void", "", ""
18585 if type.isPrimitive() and type.tag() in builtinNames:
18586 result = CGGeneric(builtinNames[type.tag()])
18587 defaultReturnArg = "0"
18588 if type.nullable():
18589 result = CGTemplatedType("Nullable", result)
18590 defaultReturnArg = ""
18591 return (
18592 result.define(),
18593 "%s(%s)" % (result.define(), defaultReturnArg),
18594 "return ${declName};\n",
18596 if type.isJSString():
18597 if isMember:
18598 raise TypeError("JSString not supported as return type member")
18599 # Outparam
18600 return "void", "", "aRetVal.set(${declName});\n"
18601 if type.isDOMString() or type.isUSVString():
18602 if isMember:
18603 # No need for a third element in the isMember case
18604 return "nsString", None, None
18605 # Outparam
18606 return "void", "", "aRetVal = ${declName};\n"
18607 if type.isByteString() or type.isUTF8String():
18608 if isMember:
18609 # No need for a third element in the isMember case
18610 return "nsCString", None, None
18611 # Outparam
18612 return "void", "", "aRetVal = ${declName};\n"
18613 if type.isEnum():
18614 enumName = type.unroll().inner.identifier.name
18615 if type.nullable():
18616 enumName = CGTemplatedType("Nullable", CGGeneric(enumName)).define()
18617 defaultValue = "%s()" % enumName
18618 else:
18619 defaultValue = "%s(0)" % enumName
18620 return enumName, defaultValue, "return ${declName};\n"
18621 if type.isGeckoInterface() or type.isPromise():
18622 if type.isGeckoInterface():
18623 iface = type.unroll().inner
18624 result = CGGeneric(
18625 self.descriptorProvider.getDescriptor(
18626 iface.identifier.name
18627 ).prettyNativeType
18629 else:
18630 result = CGGeneric("Promise")
18631 if self.resultAlreadyAddRefed:
18632 if isMember:
18633 holder = "RefPtr"
18634 else:
18635 holder = "already_AddRefed"
18636 if memberReturnsNewObject(self.member) or isMember:
18637 warning = ""
18638 else:
18639 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"
18640 result = CGWrapper(result, pre=("%s%s<" % (warning, holder)), post=">")
18641 else:
18642 result = CGWrapper(result, post="*")
18643 # Since we always force an owning type for callback return values,
18644 # our ${declName} is an OwningNonNull or RefPtr. So we can just
18645 # .forget() to get our already_AddRefed.
18646 return result.define(), "nullptr", "return ${declName}.forget();\n"
18647 if type.isCallback():
18648 return (
18649 "already_AddRefed<%s>" % type.unroll().callback.identifier.name,
18650 "nullptr",
18651 "return ${declName}.forget();\n",
18653 if type.isAny():
18654 if isMember:
18655 # No need for a third element in the isMember case
18656 return "JS::Value", None, None
18657 # Outparam
18658 return "void", "", "aRetVal.set(${declName});\n"
18660 if type.isObject():
18661 if isMember:
18662 # No need for a third element in the isMember case
18663 return "JSObject*", None, None
18664 return "void", "", "aRetVal.set(${declName});\n"
18665 if type.isSpiderMonkeyInterface():
18666 if isMember:
18667 # No need for a third element in the isMember case
18668 return "JSObject*", None, None
18669 if type.nullable():
18670 returnCode = (
18671 "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj()"
18673 else:
18674 returnCode = "${declName}.Obj()"
18675 return "void", "", "aRetVal.set(%s);\n" % returnCode
18676 if type.isSequence():
18677 # If we want to handle sequence-of-sequences return values, we're
18678 # going to need to fix example codegen to not produce nsTArray<void>
18679 # for the relevant argument...
18680 assert not isMember
18681 # Outparam.
18682 if type.nullable():
18683 returnCode = dedent(
18685 if (${declName}.IsNull()) {
18686 aRetVal.SetNull();
18687 } else {
18688 aRetVal.SetValue() = std::move(${declName}.Value());
18692 else:
18693 returnCode = "aRetVal = std::move(${declName});\n"
18694 return "void", "", returnCode
18695 if type.isRecord():
18696 # If we want to handle record-of-record return values, we're
18697 # going to need to fix example codegen to not produce record<void>
18698 # for the relevant argument...
18699 assert not isMember
18700 # In this case we convert directly into our outparam to start with
18701 return "void", "", ""
18702 if type.isDictionary():
18703 if isMember:
18704 # Only the first member of the tuple matters here, but return
18705 # bogus values for the others in case someone decides to use
18706 # them.
18707 return CGDictionary.makeDictionaryName(type.inner), None, None
18708 # In this case we convert directly into our outparam to start with
18709 return "void", "", ""
18710 if type.isUnion():
18711 if isMember:
18712 # Only the first member of the tuple matters here, but return
18713 # bogus values for the others in case someone decides to use
18714 # them.
18715 return CGUnionStruct.unionTypeDecl(type, True), None, None
18716 # In this case we convert directly into our outparam to start with
18717 return "void", "", ""
18719 raise TypeError("Don't know how to declare return value for %s" % type)
18721 def getArgs(self, returnType, argList):
18722 args = [self.getArg(arg) for arg in argList]
18723 # Now the outparams
18724 if returnType.isJSString():
18725 args.append(Argument("JS::MutableHandle<JSString*>", "aRetVal"))
18726 elif returnType.isDOMString() or returnType.isUSVString():
18727 args.append(Argument("nsString&", "aRetVal"))
18728 elif returnType.isByteString() or returnType.isUTF8String():
18729 args.append(Argument("nsCString&", "aRetVal"))
18730 elif returnType.isSequence():
18731 nullable = returnType.nullable()
18732 if nullable:
18733 returnType = returnType.inner
18734 # And now the actual underlying type
18735 elementDecl = self.getReturnType(returnType.inner, True)
18736 type = CGTemplatedType("nsTArray", CGGeneric(elementDecl))
18737 if nullable:
18738 type = CGTemplatedType("Nullable", type)
18739 args.append(Argument("%s&" % type.define(), "aRetVal"))
18740 elif returnType.isRecord():
18741 nullable = returnType.nullable()
18742 if nullable:
18743 returnType = returnType.inner
18744 # And now the actual underlying type
18745 elementDecl = self.getReturnType(returnType.inner, True)
18746 type = CGTemplatedType(
18747 "Record", [recordKeyDeclType(returnType), CGGeneric(elementDecl)]
18749 if nullable:
18750 type = CGTemplatedType("Nullable", type)
18751 args.append(Argument("%s&" % type.define(), "aRetVal"))
18752 elif returnType.isDictionary():
18753 nullable = returnType.nullable()
18754 if nullable:
18755 returnType = returnType.inner
18756 dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner))
18757 if nullable:
18758 dictType = CGTemplatedType("Nullable", dictType)
18759 args.append(Argument("%s&" % dictType.define(), "aRetVal"))
18760 elif returnType.isUnion():
18761 args.append(
18762 Argument(
18763 "%s&" % CGUnionStruct.unionTypeDecl(returnType, True), "aRetVal"
18766 elif returnType.isAny():
18767 args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal"))
18768 elif returnType.isObject() or returnType.isSpiderMonkeyInterface():
18769 args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal"))
18771 # And the nsIPrincipal
18772 if "needsSubjectPrincipal" in self.extendedAttrs:
18773 # Cheat and assume self.descriptorProvider is a descriptor
18774 if self.descriptorProvider.interface.isExposedInAnyWorker():
18775 args.append(Argument("Maybe<nsIPrincipal*>", "aSubjectPrincipal"))
18776 elif "needsNonSystemSubjectPrincipal" in self.extendedAttrs:
18777 args.append(Argument("nsIPrincipal*", "aPrincipal"))
18778 else:
18779 args.append(Argument("nsIPrincipal&", "aPrincipal"))
18780 # And the caller type, if desired.
18781 if needsCallerType(self.member):
18782 args.append(Argument("CallerType", "aCallerType"))
18783 # And the ErrorResult or OOMReporter
18784 if "needsErrorResult" in self.extendedAttrs:
18785 # Use aRv so it won't conflict with local vars named "rv"
18786 args.append(Argument("ErrorResult&", "aRv"))
18787 elif "canOOM" in self.extendedAttrs:
18788 args.append(Argument("OOMReporter&", "aRv"))
18790 # The legacycaller thisval
18791 if self.member.isMethod() and self.member.isLegacycaller():
18792 # If it has an identifier, we can't deal with it yet
18793 assert self.member.isIdentifierLess()
18794 args.insert(0, Argument("const JS::Value&", "aThisVal"))
18795 # And jscontext bits.
18796 if needCx(
18797 returnType,
18798 argList,
18799 self.extendedAttrs,
18800 self.passJSBitsAsNeeded,
18801 self.member.isStatic(),
18803 args.insert(0, Argument("JSContext*", "cx"))
18804 if needScopeObject(
18805 returnType,
18806 argList,
18807 self.extendedAttrs,
18808 self.descriptorProvider.wrapperCache,
18809 self.passJSBitsAsNeeded,
18810 self.member.getExtendedAttribute("StoreInSlot"),
18812 args.insert(1, Argument("JS::Handle<JSObject*>", "obj"))
18813 # And if we're static, a global
18814 if self.member.isStatic():
18815 args.insert(0, Argument("const GlobalObject&", "global"))
18816 return args
18818 def doGetArgType(self, type, optional, isMember):
18820 The main work of getArgType. Returns a string type decl, whether this
18821 is a const ref, as well as whether the type should be wrapped in
18822 Nullable as needed.
18824 isMember can be false or one of the strings "Sequence", "Variadic",
18825 "Record"
18827 if type.isSequence():
18828 nullable = type.nullable()
18829 if nullable:
18830 type = type.inner
18831 elementType = type.inner
18832 argType = self.getArgType(elementType, False, "Sequence")[0]
18833 decl = CGTemplatedType("Sequence", argType)
18834 return decl.define(), True, True
18836 if type.isRecord():
18837 nullable = type.nullable()
18838 if nullable:
18839 type = type.inner
18840 elementType = type.inner
18841 argType = self.getArgType(elementType, False, "Record")[0]
18842 decl = CGTemplatedType("Record", [recordKeyDeclType(type), argType])
18843 return decl.define(), True, True
18845 if type.isUnion():
18846 # unionTypeDecl will handle nullable types, so return False for
18847 # auto-wrapping in Nullable
18848 return CGUnionStruct.unionTypeDecl(type, isMember), True, False
18850 if type.isPromise():
18851 assert not type.nullable()
18852 if optional or isMember:
18853 typeDecl = "OwningNonNull<Promise>"
18854 else:
18855 typeDecl = "Promise&"
18856 return (typeDecl, False, False)
18858 if type.isGeckoInterface() and not type.isCallbackInterface():
18859 iface = type.unroll().inner
18860 if iface.identifier.name == "WindowProxy":
18861 return "WindowProxyHolder", True, False
18863 argIsPointer = type.nullable() or iface.isExternal()
18864 forceOwningType = iface.isCallback() or isMember
18865 if argIsPointer:
18866 if (optional or isMember) and forceOwningType:
18867 typeDecl = "RefPtr<%s>"
18868 else:
18869 typeDecl = "%s*"
18870 else:
18871 if optional or isMember:
18872 if forceOwningType:
18873 typeDecl = "OwningNonNull<%s>"
18874 else:
18875 typeDecl = "NonNull<%s>"
18876 else:
18877 typeDecl = "%s&"
18878 return (
18880 typeDecl
18881 % self.descriptorProvider.getDescriptor(
18882 iface.identifier.name
18883 ).prettyNativeType
18885 False,
18886 False,
18889 if type.isSpiderMonkeyInterface():
18890 if not self.spiderMonkeyInterfacesAreStructs:
18891 return "JS::Handle<JSObject*>", False, False
18893 # Unroll for the name, in case we're nullable.
18894 return type.unroll().name, True, True
18896 if type.isJSString():
18897 if isMember:
18898 raise TypeError("JSString not supported as member")
18899 return "JS::Handle<JSString*>", False, False
18901 if type.isDOMString() or type.isUSVString():
18902 if isMember:
18903 declType = "nsString"
18904 else:
18905 declType = "nsAString"
18906 return declType, True, False
18908 if type.isByteString() or type.isUTF8String():
18909 # TODO(emilio): Maybe bytestrings could benefit from nsAutoCString
18910 # or such too.
18911 if type.isUTF8String() and not isMember:
18912 declType = "nsACString"
18913 else:
18914 declType = "nsCString"
18915 return declType, True, False
18917 if type.isEnum():
18918 return type.unroll().inner.identifier.name, False, True
18920 if type.isCallback() or type.isCallbackInterface():
18921 forceOwningType = optional or isMember
18922 if type.nullable():
18923 if forceOwningType:
18924 declType = "RefPtr<%s>"
18925 else:
18926 declType = "%s*"
18927 else:
18928 if forceOwningType:
18929 declType = "OwningNonNull<%s>"
18930 else:
18931 declType = "%s&"
18932 if type.isCallback():
18933 name = type.unroll().callback.identifier.name
18934 else:
18935 name = type.unroll().inner.identifier.name
18936 return declType % name, False, False
18938 if type.isAny():
18939 # Don't do the rooting stuff for variadics for now
18940 if isMember:
18941 declType = "JS::Value"
18942 else:
18943 declType = "JS::Handle<JS::Value>"
18944 return declType, False, False
18946 if type.isObject():
18947 if isMember:
18948 declType = "JSObject*"
18949 else:
18950 declType = "JS::Handle<JSObject*>"
18951 return declType, False, False
18953 if type.isDictionary():
18954 typeName = CGDictionary.makeDictionaryName(type.inner)
18955 return typeName, True, True
18957 assert type.isPrimitive()
18959 return builtinNames[type.tag()], False, True
18961 def getArgType(self, type, optional, isMember):
18963 Get the type of an argument declaration. Returns the type CGThing, and
18964 whether this should be a const ref.
18966 isMember can be False, "Sequence", or "Variadic"
18968 decl, ref, handleNullable = self.doGetArgType(type, optional, isMember)
18969 decl = CGGeneric(decl)
18970 if handleNullable and type.nullable():
18971 decl = CGTemplatedType("Nullable", decl)
18972 ref = True
18973 if isMember == "Variadic":
18974 arrayType = "Sequence" if self.variadicIsSequence else "nsTArray"
18975 decl = CGTemplatedType(arrayType, decl)
18976 ref = True
18977 elif optional:
18978 # Note: All variadic args claim to be optional, but we can just use
18979 # empty arrays to represent them not being present.
18980 decl = CGTemplatedType("Optional", decl)
18981 ref = True
18982 return (decl, ref)
18984 def getArg(self, arg):
18986 Get the full argument declaration for an argument
18988 decl, ref = self.getArgType(
18989 arg.type, arg.canHaveMissingValue(), "Variadic" if arg.variadic else False
18991 if ref:
18992 decl = CGWrapper(decl, pre="const ", post="&")
18994 return Argument(decl.define(), arg.identifier.name)
18996 def arguments(self):
18997 return self.member.signatures()[0][1]
19000 class CGExampleMethod(CGNativeMember):
19001 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
19002 CGNativeMember.__init__(
19003 self,
19004 descriptor,
19005 method,
19006 CGSpecializedMethod.makeNativeName(descriptor, method),
19007 signature,
19008 descriptor.getExtendedAttributes(method),
19009 breakAfter=breakAfter,
19010 variadicIsSequence=True,
19013 def declare(self, cgClass):
19014 assert self.member.isMethod()
19015 # We skip declaring ourselves if this is a maplike/setlike/iterable
19016 # method, because those get implemented automatically by the binding
19017 # machinery, so the implementor of the interface doesn't have to worry
19018 # about it.
19019 if self.member.isMaplikeOrSetlikeOrIterableMethod():
19020 return ""
19021 return CGNativeMember.declare(self, cgClass)
19023 def define(self, cgClass):
19024 return ""
19027 class CGExampleGetter(CGNativeMember):
19028 def __init__(self, descriptor, attr):
19029 CGNativeMember.__init__(
19030 self,
19031 descriptor,
19032 attr,
19033 CGSpecializedGetter.makeNativeName(descriptor, attr),
19034 (attr.type, []),
19035 descriptor.getExtendedAttributes(attr, getter=True),
19038 def declare(self, cgClass):
19039 assert self.member.isAttr()
19040 # We skip declaring ourselves if this is a maplike/setlike attr (in
19041 # practice, "size"), because those get implemented automatically by the
19042 # binding machinery, so the implementor of the interface doesn't have to
19043 # worry about it.
19044 if self.member.isMaplikeOrSetlikeAttr():
19045 return ""
19046 return CGNativeMember.declare(self, cgClass)
19048 def define(self, cgClass):
19049 return ""
19052 class CGExampleSetter(CGNativeMember):
19053 def __init__(self, descriptor, attr):
19054 CGNativeMember.__init__(
19055 self,
19056 descriptor,
19057 attr,
19058 CGSpecializedSetter.makeNativeName(descriptor, attr),
19059 (BuiltinTypes[IDLBuiltinType.Types.void], [FakeArgument(attr.type, attr)]),
19060 descriptor.getExtendedAttributes(attr, setter=True),
19063 def define(self, cgClass):
19064 return ""
19067 class CGBindingImplClass(CGClass):
19069 Common codegen for generating a C++ implementation of a WebIDL interface
19072 def __init__(
19073 self,
19074 descriptor,
19075 cgMethod,
19076 cgGetter,
19077 cgSetter,
19078 wantGetParent=True,
19079 wrapMethodName="WrapObject",
19080 skipStaticMethods=False,
19083 cgMethod, cgGetter and cgSetter are classes used to codegen methods,
19084 getters and setters.
19086 self.descriptor = descriptor
19087 self._deps = descriptor.interface.getDeps()
19089 iface = descriptor.interface
19091 self.methodDecls = []
19093 def appendMethod(m, isConstructor=False):
19094 sigs = m.signatures()
19095 for s in sigs[:-1]:
19096 # Don't put a blank line after overloads, until we
19097 # get to the last one.
19098 self.methodDecls.append(
19099 cgMethod(descriptor, m, s, isConstructor, breakAfter=False)
19101 self.methodDecls.append(cgMethod(descriptor, m, sigs[-1], isConstructor))
19103 if iface.ctor():
19104 appendMethod(iface.ctor(), isConstructor=True)
19105 for n in iface.legacyFactoryFunctions:
19106 appendMethod(n, isConstructor=True)
19107 for m in iface.members:
19108 if m.isMethod():
19109 if m.isIdentifierLess():
19110 continue
19111 if m.isMaplikeOrSetlikeOrIterableMethod():
19112 # Handled by generated code already
19113 continue
19114 if not m.isStatic() or not skipStaticMethods:
19115 appendMethod(m)
19116 elif m.isAttr():
19117 if m.isMaplikeOrSetlikeAttr():
19118 # Handled by generated code already
19119 continue
19120 self.methodDecls.append(cgGetter(descriptor, m))
19121 if not m.readonly:
19122 self.methodDecls.append(cgSetter(descriptor, m))
19124 # Now do the special operations
19125 def appendSpecialOperation(name, op):
19126 if op is None:
19127 return
19128 assert len(op.signatures()) == 1
19129 returnType, args = op.signatures()[0]
19130 # Make a copy of the args, since we plan to modify them.
19131 args = list(args)
19132 if op.isGetter() or op.isDeleter():
19133 # This is a total hack. The '&' belongs with the
19134 # type, not the name! But it works, and is simpler
19135 # than trying to somehow make this pretty.
19136 args.append(
19137 FakeArgument(
19138 BuiltinTypes[IDLBuiltinType.Types.boolean], op, name="&found"
19141 if name == "Stringifier":
19142 if op.isIdentifierLess():
19143 # XXXbz I wish we were consistent about our renaming here.
19144 name = "Stringify"
19145 else:
19146 # We already added this method
19147 return
19148 if name == "LegacyCaller":
19149 if op.isIdentifierLess():
19150 # XXXbz I wish we were consistent about our renaming here.
19151 name = "LegacyCall"
19152 else:
19153 # We already added this method
19154 return
19155 self.methodDecls.append(
19156 CGNativeMember(
19157 descriptor,
19159 name,
19160 (returnType, args),
19161 descriptor.getExtendedAttributes(op),
19165 # Sort things by name so we get stable ordering in the output.
19166 ops = sorted(descriptor.operations.items(), key=lambda x: x[0])
19167 for name, op in ops:
19168 appendSpecialOperation(name, op)
19169 # If we support indexed properties, then we need a Length()
19170 # method so we know which indices are supported.
19171 if descriptor.supportsIndexedProperties():
19172 # But we don't need it if we already have an infallible
19173 # "length" attribute, which we often do.
19174 haveLengthAttr = any(
19176 for m in iface.members
19177 if m.isAttr()
19178 and CGSpecializedGetter.makeNativeName(descriptor, m) == "Length"
19180 if not haveLengthAttr:
19181 self.methodDecls.append(
19182 CGNativeMember(
19183 descriptor,
19184 FakeMember(),
19185 "Length",
19186 (BuiltinTypes[IDLBuiltinType.Types.unsigned_long], []),
19190 # And if we support named properties we need to be able to
19191 # enumerate the supported names.
19192 if descriptor.supportsNamedProperties():
19193 self.methodDecls.append(
19194 CGNativeMember(
19195 descriptor,
19196 FakeMember(),
19197 "GetSupportedNames",
19199 IDLSequenceType(
19200 None, BuiltinTypes[IDLBuiltinType.Types.domstring]
19208 if descriptor.concrete:
19209 wrapArgs = [
19210 Argument("JSContext*", "aCx"),
19211 Argument("JS::Handle<JSObject*>", "aGivenProto"),
19213 if not descriptor.wrapperCache:
19214 wrapReturnType = "bool"
19215 wrapArgs.append(Argument("JS::MutableHandle<JSObject*>", "aReflector"))
19216 else:
19217 wrapReturnType = "JSObject*"
19218 self.methodDecls.insert(
19220 ClassMethod(
19221 wrapMethodName,
19222 wrapReturnType,
19223 wrapArgs,
19224 virtual=descriptor.wrapperCache,
19225 breakAfterReturnDecl=" ",
19226 override=descriptor.wrapperCache,
19227 body=self.getWrapObjectBody(),
19230 if descriptor.hasCEReactions():
19231 self.methodDecls.insert(
19233 ClassMethod(
19234 "GetDocGroup",
19235 "DocGroup*",
19237 const=True,
19238 breakAfterReturnDecl=" ",
19239 body=self.getGetDocGroupBody(),
19242 if wantGetParent:
19243 self.methodDecls.insert(
19245 ClassMethod(
19246 "GetParentObject",
19247 self.getGetParentObjectReturnType(),
19249 const=True,
19250 breakAfterReturnDecl=" ",
19251 body=self.getGetParentObjectBody(),
19255 # Invoke CGClass.__init__ in any subclasses afterwards to do the actual codegen.
19257 def getWrapObjectBody(self):
19258 return None
19260 def getGetParentObjectReturnType(self):
19261 # The lack of newline before the end of the string is on purpose.
19262 return dedent(
19264 // This should return something that eventually allows finding a
19265 // path to the global this object is associated with. Most simply,
19266 // returning an actual global works.
19267 nsIGlobalObject*"""
19270 def getGetParentObjectBody(self):
19271 return None
19273 def getGetDocGroupBody(self):
19274 return None
19276 def deps(self):
19277 return self._deps
19280 class CGExampleClass(CGBindingImplClass):
19282 Codegen for the actual example class implementation for this descriptor
19285 def __init__(self, descriptor):
19286 CGBindingImplClass.__init__(
19287 self,
19288 descriptor,
19289 CGExampleMethod,
19290 CGExampleGetter,
19291 CGExampleSetter,
19292 wantGetParent=descriptor.wrapperCache,
19295 self.parentIface = descriptor.interface.parent
19296 if self.parentIface:
19297 self.parentDesc = descriptor.getDescriptor(self.parentIface.identifier.name)
19298 bases = [ClassBase(self.nativeLeafName(self.parentDesc))]
19299 else:
19300 bases = [
19301 ClassBase(
19302 "nsISupports /* or NonRefcountedDOMObject if this is a non-refcounted object */"
19305 if descriptor.wrapperCache:
19306 bases.append(
19307 ClassBase(
19308 "nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */"
19312 destructorVisibility = "protected"
19313 if self.parentIface:
19314 extradeclarations = (
19315 "public:\n"
19316 " NS_DECL_ISUPPORTS_INHERITED\n"
19317 " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n"
19318 "\n"
19320 self.nativeLeafName(descriptor),
19321 self.nativeLeafName(self.parentDesc),
19324 else:
19325 extradeclarations = (
19326 "public:\n"
19327 " NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
19328 " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n"
19329 "\n" % self.nativeLeafName(descriptor)
19332 if descriptor.interface.hasChildInterfaces():
19333 decorators = ""
19334 else:
19335 decorators = "final"
19337 CGClass.__init__(
19338 self,
19339 self.nativeLeafName(descriptor),
19340 bases=bases,
19341 constructors=[ClassConstructor([], visibility="public")],
19342 destructor=ClassDestructor(visibility=destructorVisibility),
19343 methods=self.methodDecls,
19344 decorators=decorators,
19345 extradeclarations=extradeclarations,
19348 def define(self):
19349 # Just override CGClass and do our own thing
19350 nativeType = self.nativeLeafName(self.descriptor)
19352 ctordtor = fill(
19354 ${nativeType}::${nativeType}()
19356 // Add |MOZ_COUNT_CTOR(${nativeType});| for a non-refcounted object.
19359 ${nativeType}::~${nativeType}()
19361 // Add |MOZ_COUNT_DTOR(${nativeType});| for a non-refcounted object.
19363 """,
19364 nativeType=nativeType,
19367 if self.parentIface:
19368 ccImpl = fill(
19371 // Only needed for refcounted objects.
19372 # error "If you don't have members that need cycle collection,
19373 # then remove all the cycle collection bits from this
19374 # implementation and the corresponding header. If you do, you
19375 # want NS_IMPL_CYCLE_COLLECTION_INHERITED(${nativeType},
19376 # ${parentType}, your, members, here)"
19377 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
19378 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
19379 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
19380 NS_INTERFACE_MAP_END_INHERITING(${parentType})
19382 """,
19383 nativeType=nativeType,
19384 parentType=self.nativeLeafName(self.parentDesc),
19386 else:
19387 ccImpl = fill(
19390 // Only needed for refcounted objects.
19391 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType})
19392 NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType})
19393 NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType})
19394 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
19395 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
19396 NS_INTERFACE_MAP_ENTRY(nsISupports)
19397 NS_INTERFACE_MAP_END
19399 """,
19400 nativeType=nativeType,
19403 classImpl = ccImpl + ctordtor + "\n"
19404 if self.descriptor.concrete:
19405 if self.descriptor.wrapperCache:
19406 reflectorArg = ""
19407 reflectorPassArg = ""
19408 returnType = "JSObject*"
19409 else:
19410 reflectorArg = ", JS::MutableHandle<JSObject*> aReflector"
19411 reflectorPassArg = ", aReflector"
19412 returnType = "bool"
19413 classImpl += fill(
19415 ${returnType}
19416 ${nativeType}::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto${reflectorArg})
19418 return ${ifaceName}_Binding::Wrap(aCx, this, aGivenProto${reflectorPassArg});
19421 """,
19422 returnType=returnType,
19423 nativeType=nativeType,
19424 reflectorArg=reflectorArg,
19425 ifaceName=self.descriptor.name,
19426 reflectorPassArg=reflectorPassArg,
19429 return classImpl
19431 @staticmethod
19432 def nativeLeafName(descriptor):
19433 return descriptor.nativeType.split("::")[-1]
19436 class CGExampleRoot(CGThing):
19438 Root codegen class for example implementation generation. Instantiate the
19439 class and call declare or define to generate header or cpp code,
19440 respectively.
19443 def __init__(self, config, interfaceName):
19444 descriptor = config.getDescriptor(interfaceName)
19446 self.root = CGWrapper(CGExampleClass(descriptor), pre="\n", post="\n")
19448 self.root = CGNamespace.build(["mozilla", "dom"], self.root)
19450 builder = ForwardDeclarationBuilder()
19451 if descriptor.hasCEReactions():
19452 builder.addInMozillaDom("DocGroup")
19453 for member in descriptor.interface.members:
19454 if not member.isAttr() and not member.isMethod():
19455 continue
19456 if member.isStatic():
19457 builder.addInMozillaDom("GlobalObject")
19458 if member.isAttr():
19459 if not member.isMaplikeOrSetlikeAttr():
19460 builder.forwardDeclareForType(member.type, config)
19461 else:
19462 assert member.isMethod()
19463 if not member.isMaplikeOrSetlikeOrIterableMethod():
19464 for sig in member.signatures():
19465 builder.forwardDeclareForType(sig[0], config)
19466 for arg in sig[1]:
19467 builder.forwardDeclareForType(arg.type, config)
19469 self.root = CGList([builder.build(), self.root], "\n")
19471 # Throw in our #includes
19472 self.root = CGHeaders(
19478 "nsWrapperCache.h",
19479 "nsCycleCollectionParticipant.h",
19480 "mozilla/Attributes.h",
19481 "mozilla/ErrorResult.h",
19482 "mozilla/dom/BindingDeclarations.h",
19483 "js/TypeDecls.h",
19486 "mozilla/dom/%s.h" % interfaceName,
19488 "mozilla/dom/%s"
19489 % CGHeaders.getDeclarationFilename(descriptor.interface)
19493 self.root,
19496 # And now some include guards
19497 self.root = CGIncludeGuard(interfaceName, self.root)
19499 # And our license block comes before everything else
19500 self.root = CGWrapper(
19501 self.root,
19502 pre=dedent(
19504 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
19505 /* vim:set ts=2 sw=2 sts=2 et cindent: */
19506 /* This Source Code Form is subject to the terms of the Mozilla Public
19507 * License, v. 2.0. If a copy of the MPL was not distributed with this
19508 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
19514 def declare(self):
19515 return self.root.declare()
19517 def define(self):
19518 return self.root.define()
19521 def jsImplName(name):
19522 return name + "JSImpl"
19525 class CGJSImplMember(CGNativeMember):
19527 Base class for generating code for the members of the implementation class
19528 for a JS-implemented WebIDL interface.
19531 def __init__(
19532 self,
19533 descriptorProvider,
19534 member,
19535 name,
19536 signature,
19537 extendedAttrs,
19538 breakAfter=True,
19539 passJSBitsAsNeeded=True,
19540 visibility="public",
19541 variadicIsSequence=False,
19542 virtual=False,
19543 override=False,
19545 CGNativeMember.__init__(
19546 self,
19547 descriptorProvider,
19548 member,
19549 name,
19550 signature,
19551 extendedAttrs,
19552 breakAfter=breakAfter,
19553 passJSBitsAsNeeded=passJSBitsAsNeeded,
19554 visibility=visibility,
19555 variadicIsSequence=variadicIsSequence,
19556 virtual=virtual,
19557 override=override,
19559 self.body = self.getImpl()
19561 def getArgs(self, returnType, argList):
19562 args = CGNativeMember.getArgs(self, returnType, argList)
19563 args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
19564 return args
19567 class CGJSImplMethod(CGJSImplMember):
19569 Class for generating code for the methods for a JS-implemented WebIDL
19570 interface.
19573 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
19574 self.signature = signature
19575 self.descriptor = descriptor
19576 self.isConstructor = isConstructor
19577 CGJSImplMember.__init__(
19578 self,
19579 descriptor,
19580 method,
19581 CGSpecializedMethod.makeNativeName(descriptor, method),
19582 signature,
19583 descriptor.getExtendedAttributes(method),
19584 breakAfter=breakAfter,
19585 variadicIsSequence=True,
19586 passJSBitsAsNeeded=False,
19589 def getArgs(self, returnType, argList):
19590 if self.isConstructor:
19591 # Skip the JS::Compartment bits for constructors; it's handled
19592 # manually in getImpl. But we do need our aGivenProto argument. We
19593 # allow it to be omitted if the default proto is desired.
19594 return CGNativeMember.getArgs(self, returnType, argList) + [
19595 Argument("JS::Handle<JSObject*>", "aGivenProto", "nullptr")
19597 return CGJSImplMember.getArgs(self, returnType, argList)
19599 def getImpl(self):
19600 args = self.getArgs(self.signature[0], self.signature[1])
19601 if not self.isConstructor:
19602 return "return mImpl->%s(%s);\n" % (
19603 self.name,
19604 ", ".join(arg.name for arg in args),
19607 assert self.descriptor.interface.isJSImplemented()
19608 if self.name != "Constructor":
19609 raise TypeError(
19610 "Named constructors are not supported for JS implemented WebIDL. See bug 851287."
19612 if len(self.signature[1]) != 0:
19613 # The first two arguments to the constructor implementation are not
19614 # arguments to the WebIDL constructor, so don't pass them to
19615 # __Init(). The last argument is the prototype we're supposed to
19616 # use, and shouldn't get passed to __Init() either.
19617 assert args[0].argType == "const GlobalObject&"
19618 assert args[1].argType == "JSContext*"
19619 assert args[-1].argType == "JS::Handle<JSObject*>"
19620 assert args[-1].name == "aGivenProto"
19621 constructorArgs = [arg.name for arg in args[2:-1]]
19622 constructorArgs.append("js::GetNonCCWObjectRealm(scopeObj)")
19623 initCall = fill(
19625 // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
19626 JS::Rooted<JSObject*> scopeObj(cx, global.Get());
19627 MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx));
19628 JS::Rooted<JS::Value> wrappedVal(cx);
19629 if (!GetOrCreateDOMReflector(cx, impl, &wrappedVal, aGivenProto)) {
19630 MOZ_ASSERT(JS_IsExceptionPending(cx));
19631 aRv.Throw(NS_ERROR_UNEXPECTED);
19632 return nullptr;
19634 // Initialize the object with the constructor arguments.
19635 impl->mImpl->__Init(${args});
19636 if (aRv.Failed()) {
19637 return nullptr;
19639 """,
19640 args=", ".join(constructorArgs),
19642 else:
19643 initCall = ""
19644 return fill(
19646 RefPtr<${implClass}> impl =
19647 ConstructJSImplementation<${implClass}>("${contractId}", global, aRv);
19648 if (aRv.Failed()) {
19649 return nullptr;
19651 $*{initCall}
19652 return impl.forget();
19653 """,
19654 contractId=self.descriptor.interface.getJSImplementation(),
19655 implClass=self.descriptor.name,
19656 initCall=initCall,
19660 # We're always fallible
19661 def callbackGetterName(attr, descriptor):
19662 return "Get" + MakeNativeName(descriptor.binaryNameFor(attr.identifier.name))
19665 def callbackSetterName(attr, descriptor):
19666 return "Set" + MakeNativeName(descriptor.binaryNameFor(attr.identifier.name))
19669 class CGJSImplGetter(CGJSImplMember):
19671 Class for generating code for the getters of attributes for a JS-implemented
19672 WebIDL interface.
19675 def __init__(self, descriptor, attr):
19676 CGJSImplMember.__init__(
19677 self,
19678 descriptor,
19679 attr,
19680 CGSpecializedGetter.makeNativeName(descriptor, attr),
19681 (attr.type, []),
19682 descriptor.getExtendedAttributes(attr, getter=True),
19683 passJSBitsAsNeeded=False,
19686 def getImpl(self):
19687 callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])]
19688 return "return mImpl->%s(%s);\n" % (
19689 callbackGetterName(self.member, self.descriptorProvider),
19690 ", ".join(callbackArgs),
19694 class CGJSImplSetter(CGJSImplMember):
19696 Class for generating code for the setters of attributes for a JS-implemented
19697 WebIDL interface.
19700 def __init__(self, descriptor, attr):
19701 CGJSImplMember.__init__(
19702 self,
19703 descriptor,
19704 attr,
19705 CGSpecializedSetter.makeNativeName(descriptor, attr),
19706 (BuiltinTypes[IDLBuiltinType.Types.void], [FakeArgument(attr.type, attr)]),
19707 descriptor.getExtendedAttributes(attr, setter=True),
19708 passJSBitsAsNeeded=False,
19711 def getImpl(self):
19712 callbackArgs = [
19713 arg.name
19714 for arg in self.getArgs(
19715 BuiltinTypes[IDLBuiltinType.Types.void],
19716 [FakeArgument(self.member.type, self.member)],
19719 return "mImpl->%s(%s);\n" % (
19720 callbackSetterName(self.member, self.descriptorProvider),
19721 ", ".join(callbackArgs),
19725 class CGJSImplClass(CGBindingImplClass):
19726 def __init__(self, descriptor):
19727 CGBindingImplClass.__init__(
19728 self,
19729 descriptor,
19730 CGJSImplMethod,
19731 CGJSImplGetter,
19732 CGJSImplSetter,
19733 skipStaticMethods=True,
19736 if descriptor.interface.parent:
19737 parentClass = descriptor.getDescriptor(
19738 descriptor.interface.parent.identifier.name
19739 ).jsImplParent
19740 baseClasses = [ClassBase(parentClass)]
19741 isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n"
19742 ccDecl = "NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" % (
19743 descriptor.name,
19744 parentClass,
19746 constructorBody = dedent(
19748 // Make sure we're an nsWrapperCache already
19749 MOZ_ASSERT(static_cast<nsWrapperCache*>(this));
19752 extradefinitions = fill(
19754 NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent)
19755 NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass})
19756 NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass})
19757 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
19758 NS_INTERFACE_MAP_END_INHERITING(${parentClass})
19759 """,
19760 ifaceName=self.descriptor.name,
19761 parentClass=parentClass,
19763 else:
19764 baseClasses = [
19765 ClassBase("nsSupportsWeakReference"),
19766 ClassBase("nsWrapperCache"),
19768 isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
19769 ccDecl = (
19770 "NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n" % descriptor.name
19772 extradefinitions = fill(
19774 NS_IMPL_CYCLE_COLLECTION_CLASS(${ifaceName})
19775 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(${ifaceName})
19776 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl)
19777 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
19778 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
19779 tmp->ClearWeakReferences();
19780 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
19781 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(${ifaceName})
19782 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl)
19783 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
19784 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
19785 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(${ifaceName})
19786 NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName})
19787 NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName})
19788 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
19789 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
19790 NS_INTERFACE_MAP_ENTRY(nsISupports)
19791 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
19792 NS_INTERFACE_MAP_END
19793 """,
19794 ifaceName=self.descriptor.name,
19797 extradeclarations = fill(
19799 public:
19800 $*{isupportsDecl}
19801 $*{ccDecl}
19803 private:
19804 RefPtr<${jsImplName}> mImpl;
19805 nsCOMPtr<nsIGlobalObject> mParent;
19807 """,
19808 isupportsDecl=isupportsDecl,
19809 ccDecl=ccDecl,
19810 jsImplName=jsImplName(descriptor.name),
19813 if descriptor.interface.getExtendedAttribute("WantsEventListenerHooks"):
19814 # No need to do too much sanity checking here; the
19815 # generated code will fail to compile if the methods we
19816 # try to overrid aren't on a superclass.
19817 self.methodDecls.extend(
19818 self.getEventHookMethod(parentClass, "EventListenerAdded")
19820 self.methodDecls.extend(
19821 self.getEventHookMethod(parentClass, "EventListenerRemoved")
19824 if descriptor.interface.hasChildInterfaces():
19825 decorators = ""
19826 # We need a protected virtual destructor our subclasses can use
19827 destructor = ClassDestructor(virtual=True, visibility="protected")
19828 else:
19829 decorators = "final"
19830 destructor = ClassDestructor(virtual=False, visibility="private")
19832 baseConstructors = [
19834 "mImpl(new %s(nullptr, aJSImplObject, aJSImplGlobal, /* aIncumbentGlobal = */ nullptr))"
19835 % jsImplName(descriptor.name)
19837 "mParent(aParent)",
19839 parentInterface = descriptor.interface.parent
19840 while parentInterface:
19841 if parentInterface.isJSImplemented():
19842 baseConstructors.insert(
19843 0, "%s(aJSImplObject, aJSImplGlobal, aParent)" % parentClass
19845 break
19846 parentInterface = parentInterface.parent
19847 if not parentInterface and descriptor.interface.parent:
19848 # We only have C++ ancestors, so only pass along the window
19849 baseConstructors.insert(0, "%s(aParent)" % parentClass)
19851 constructor = ClassConstructor(
19853 Argument("JS::Handle<JSObject*>", "aJSImplObject"),
19854 Argument("JS::Handle<JSObject*>", "aJSImplGlobal"),
19855 Argument("nsIGlobalObject*", "aParent"),
19857 visibility="public",
19858 baseConstructors=baseConstructors,
19861 self.methodDecls.append(
19862 ClassMethod(
19863 "_Create",
19864 "bool",
19865 JSNativeArguments(),
19866 static=True,
19867 body=self.getCreateFromExistingBody(),
19871 if (
19872 descriptor.interface.isJSImplemented()
19873 and descriptor.interface.maplikeOrSetlikeOrIterable
19874 and descriptor.interface.maplikeOrSetlikeOrIterable.isMaplike()
19876 self.methodDecls.append(
19877 ClassMethod(
19878 "__OnGet",
19879 "void",
19881 Argument("JS::Handle<JS::Value>", "aKey"),
19882 Argument("JS::Handle<JS::Value>", "aValue"),
19883 Argument("ErrorResult&", "aRv"),
19885 body="mImpl->__OnGet(aKey, aValue, aRv);\n",
19889 CGClass.__init__(
19890 self,
19891 descriptor.name,
19892 bases=baseClasses,
19893 constructors=[constructor],
19894 destructor=destructor,
19895 methods=self.methodDecls,
19896 decorators=decorators,
19897 extradeclarations=extradeclarations,
19898 extradefinitions=extradefinitions,
19901 def getWrapObjectBody(self):
19902 return fill(
19904 JS::Rooted<JSObject*> obj(aCx, ${name}_Binding::Wrap(aCx, this, aGivenProto));
19905 if (!obj) {
19906 return nullptr;
19909 // Now define it on our chrome object
19910 JSAutoRealm ar(aCx, mImpl->CallbackGlobalOrNull());
19911 if (!JS_WrapObject(aCx, &obj)) {
19912 return nullptr;
19914 JS::Rooted<JSObject*> callback(aCx, mImpl->CallbackOrNull());
19915 if (!JS_DefineProperty(aCx, callback, "__DOM_IMPL__", obj, 0)) {
19916 return nullptr;
19918 return obj;
19919 """,
19920 name=self.descriptor.name,
19923 def getGetParentObjectReturnType(self):
19924 return "nsISupports*"
19926 def getGetParentObjectBody(self):
19927 return "return mParent;\n"
19929 def getGetDocGroupBody(self):
19930 return dedent(
19932 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent);
19933 if (!window) {
19934 return nullptr;
19936 return window->GetDocGroup();
19940 def getCreateFromExistingBody(self):
19941 # XXXbz we could try to get parts of this (e.g. the argument
19942 # conversions) auto-generated by somehow creating an IDLMethod and
19943 # adding it to our interface, but we'd still need to special-case the
19944 # implementation slightly to have it not try to forward to the JS
19945 # object...
19946 return fill(
19948 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
19949 if (!args.requireAtLeast(cx, "${ifaceName}._create", 2)) {
19950 return false;
19952 BindingCallContext callCx(cx, "${ifaceName}._create");
19953 if (!args[0].isObject()) {
19954 return callCx.ThrowErrorMessage<MSG_NOT_OBJECT>("Argument 1");
19956 if (!args[1].isObject()) {
19957 return callCx.ThrowErrorMessage<MSG_NOT_OBJECT>("Argument 2");
19960 // GlobalObject will go through wrappers as needed for us, and
19961 // is simpler than the right UnwrapArg incantation.
19962 GlobalObject global(cx, &args[0].toObject());
19963 if (global.Failed()) {
19964 return false;
19966 nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(global.GetAsSupports());
19967 MOZ_ASSERT(globalHolder);
19968 JS::Rooted<JSObject*> arg(cx, &args[1].toObject());
19969 JS::Rooted<JSObject*> argGlobal(cx, JS::CurrentGlobalOrNull(cx));
19970 RefPtr<${implName}> impl = new ${implName}(arg, argGlobal, globalHolder);
19971 MOZ_ASSERT(js::IsObjectInContextCompartment(arg, cx));
19972 return GetOrCreateDOMReflector(cx, impl, args.rval());
19973 """,
19974 ifaceName=self.descriptor.interface.identifier.name,
19975 implName=self.descriptor.name,
19978 def getEventHookMethod(self, parentClass, methodName):
19979 body = fill(
19981 ${parentClass}::${methodName}(aType);
19982 mImpl->${methodName}(Substring(nsDependentAtomString(aType), 2), IgnoreErrors());
19983 """,
19984 parentClass=parentClass,
19985 methodName=methodName,
19987 return [
19988 ClassMethod(
19989 methodName,
19990 "void",
19991 [Argument("nsAtom*", "aType")],
19992 virtual=True,
19993 override=True,
19994 body=body,
19996 ClassUsingDeclaration(parentClass, methodName),
20000 def isJSImplementedDescriptor(descriptorProvider):
20001 return (
20002 isinstance(descriptorProvider, Descriptor)
20003 and descriptorProvider.interface.isJSImplemented()
20007 class CGCallback(CGClass):
20008 def __init__(
20009 self, idlObject, descriptorProvider, baseName, methods, getters=[], setters=[]
20011 self.baseName = baseName
20012 self._deps = idlObject.getDeps()
20013 self.idlObject = idlObject
20014 self.name = idlObject.identifier.name
20015 if isJSImplementedDescriptor(descriptorProvider):
20016 self.name = jsImplName(self.name)
20017 # For our public methods that needThisHandling we want most of the
20018 # same args and the same return type as what CallbackMember
20019 # generates. So we want to take advantage of all its
20020 # CGNativeMember infrastructure, but that infrastructure can't deal
20021 # with templates and most especially template arguments. So just
20022 # cheat and have CallbackMember compute all those things for us.
20023 realMethods = []
20024 for method in methods:
20025 if not isinstance(method, CallbackMember) or not method.needThisHandling:
20026 realMethods.append(method)
20027 else:
20028 realMethods.extend(self.getMethodImpls(method))
20029 realMethods.append(
20030 ClassMethod(
20031 "operator==",
20032 "bool",
20033 [Argument("const %s&" % self.name, "aOther")],
20034 inline=True,
20035 bodyInHeader=True,
20036 const=True,
20037 body=("return %s::operator==(aOther);\n" % baseName),
20040 CGClass.__init__(
20041 self,
20042 self.name,
20043 bases=[ClassBase(baseName)],
20044 constructors=self.getConstructors(),
20045 methods=realMethods + getters + setters,
20048 def getConstructors(self):
20049 if (
20050 not self.idlObject.isInterface()
20051 and not self.idlObject._treatNonObjectAsNull
20053 body = "MOZ_ASSERT(JS::IsCallable(mCallback));\n"
20054 else:
20055 # Not much we can assert about it, other than not being null, and
20056 # CallbackObject does that already.
20057 body = ""
20058 return [
20059 ClassConstructor(
20061 Argument("JSContext*", "aCx"),
20062 Argument("JS::Handle<JSObject*>", "aCallback"),
20063 Argument("JS::Handle<JSObject*>", "aCallbackGlobal"),
20064 Argument("nsIGlobalObject*", "aIncumbentGlobal"),
20066 bodyInHeader=True,
20067 visibility="public",
20068 explicit=True,
20069 baseConstructors=[
20070 "%s(aCx, aCallback, aCallbackGlobal, aIncumbentGlobal)"
20071 % self.baseName,
20073 body=body,
20075 ClassConstructor(
20077 Argument("JSObject*", "aCallback"),
20078 Argument("JSObject*", "aCallbackGlobal"),
20079 Argument("const FastCallbackConstructor&", ""),
20081 bodyInHeader=True,
20082 visibility="public",
20083 explicit=True,
20084 baseConstructors=[
20085 "%s(aCallback, aCallbackGlobal, FastCallbackConstructor())"
20086 % self.baseName,
20088 body=body,
20090 ClassConstructor(
20092 Argument("JSObject*", "aCallback"),
20093 Argument("JSObject*", "aCallbackGlobal"),
20094 Argument("JSObject*", "aAsyncStack"),
20095 Argument("nsIGlobalObject*", "aIncumbentGlobal"),
20097 bodyInHeader=True,
20098 visibility="public",
20099 explicit=True,
20100 baseConstructors=[
20101 "%s(aCallback, aCallbackGlobal, aAsyncStack, aIncumbentGlobal)"
20102 % self.baseName,
20104 body=body,
20108 def getMethodImpls(self, method):
20109 assert method.needThisHandling
20110 args = list(method.args)
20111 # Strip out the BindingCallContext&/JSObject* args
20112 # that got added.
20113 assert args[0].name == "cx" and args[0].argType == "BindingCallContext&"
20114 assert args[1].name == "aThisVal" and args[1].argType == "JS::Handle<JS::Value>"
20115 args = args[2:]
20117 # Now remember which index the ErrorResult argument is at;
20118 # we'll need this below.
20119 assert args[-1].name == "aRv" and args[-1].argType == "ErrorResult&"
20120 rvIndex = len(args) - 1
20121 assert rvIndex >= 0
20123 # Record the names of all the arguments, so we can use them when we call
20124 # the private method.
20125 argnames = [arg.name for arg in args]
20126 argnamesWithThis = ["s.GetCallContext()", "thisValJS"] + argnames
20127 argnamesWithoutThis = [
20128 "s.GetCallContext()",
20129 "JS::UndefinedHandleValue",
20130 ] + argnames
20131 # Now that we've recorded the argnames for our call to our private
20132 # method, insert our optional argument for the execution reason.
20133 args.append(Argument("const char*", "aExecutionReason", "nullptr"))
20135 # Make copies of the arg list for the two "without rv" overloads. Note
20136 # that those don't need aExceptionHandling or aRealm arguments because
20137 # those would make not sense anyway: the only sane thing to do with
20138 # exceptions in the "without rv" cases is to report them.
20139 argsWithoutRv = list(args)
20140 argsWithoutRv.pop(rvIndex)
20141 argsWithoutThisAndRv = list(argsWithoutRv)
20143 # Add the potional argument for deciding whether the CallSetup should
20144 # re-throw exceptions on aRv.
20145 args.append(
20146 Argument("ExceptionHandling", "aExceptionHandling", "eReportExceptions")
20148 # And the argument for communicating when exceptions should really be
20149 # rethrown. In particular, even when aExceptionHandling is
20150 # eRethrowExceptions they won't get rethrown if aRealm is provided
20151 # and its principal doesn't subsume either the callback or the
20152 # exception.
20153 args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
20154 # And now insert our template argument.
20155 argsWithoutThis = list(args)
20156 args.insert(0, Argument("const T&", "thisVal"))
20157 argsWithoutRv.insert(0, Argument("const T&", "thisVal"))
20159 argnamesWithoutThisAndRv = [arg.name for arg in argsWithoutThisAndRv]
20160 argnamesWithoutThisAndRv.insert(rvIndex, "IgnoreErrors()")
20161 # If we just leave things like that, and have no actual arguments in the
20162 # IDL, we will end up trying to call the templated "without rv" overload
20163 # with "rv" as the thisVal. That's no good. So explicitly append the
20164 # aExceptionHandling and aRealm values we need to end up matching the
20165 # signature of our non-templated "with rv" overload.
20166 argnamesWithoutThisAndRv.extend(["eReportExceptions", "nullptr"])
20168 argnamesWithoutRv = [arg.name for arg in argsWithoutRv]
20169 # Note that we need to insert at rvIndex + 1, since we inserted a
20170 # thisVal arg at the start.
20171 argnamesWithoutRv.insert(rvIndex + 1, "IgnoreErrors()")
20173 errorReturn = method.getDefaultRetval()
20175 setupCall = fill(
20177 MOZ_ASSERT(!aRv.Failed(), "Don't pass an already-failed ErrorResult to a callback!");
20178 if (!aExecutionReason) {
20179 aExecutionReason = "${executionReason}";
20181 CallSetup s(this, aRv, aExecutionReason, aExceptionHandling, aRealm);
20182 if (!s.GetContext()) {
20183 MOZ_ASSERT(aRv.Failed());
20184 return${errorReturn};
20186 """,
20187 errorReturn=errorReturn,
20188 executionReason=method.getPrettyName(),
20191 bodyWithThis = fill(
20193 $*{setupCall}
20194 JS::Rooted<JS::Value> thisValJS(s.GetContext());
20195 if (!ToJSValue(s.GetContext(), thisVal, &thisValJS)) {
20196 aRv.Throw(NS_ERROR_FAILURE);
20197 return${errorReturn};
20199 return ${methodName}(${callArgs});
20200 """,
20201 setupCall=setupCall,
20202 errorReturn=errorReturn,
20203 methodName=method.name,
20204 callArgs=", ".join(argnamesWithThis),
20206 bodyWithoutThis = fill(
20208 $*{setupCall}
20209 return ${methodName}(${callArgs});
20210 """,
20211 setupCall=setupCall,
20212 errorReturn=errorReturn,
20213 methodName=method.name,
20214 callArgs=", ".join(argnamesWithoutThis),
20216 bodyWithThisWithoutRv = fill(
20218 return ${methodName}(${callArgs});
20219 """,
20220 methodName=method.name,
20221 callArgs=", ".join(argnamesWithoutRv),
20223 bodyWithoutThisAndRv = fill(
20225 return ${methodName}(${callArgs});
20226 """,
20227 methodName=method.name,
20228 callArgs=", ".join(argnamesWithoutThisAndRv),
20231 return [
20232 ClassMethod(
20233 method.name,
20234 method.returnType,
20235 args,
20236 bodyInHeader=True,
20237 templateArgs=["typename T"],
20238 body=bodyWithThis,
20239 canRunScript=method.canRunScript,
20241 ClassMethod(
20242 method.name,
20243 method.returnType,
20244 argsWithoutThis,
20245 bodyInHeader=True,
20246 body=bodyWithoutThis,
20247 canRunScript=method.canRunScript,
20249 ClassMethod(
20250 method.name,
20251 method.returnType,
20252 argsWithoutRv,
20253 bodyInHeader=True,
20254 templateArgs=["typename T"],
20255 body=bodyWithThisWithoutRv,
20256 canRunScript=method.canRunScript,
20258 ClassMethod(
20259 method.name,
20260 method.returnType,
20261 argsWithoutThisAndRv,
20262 bodyInHeader=True,
20263 body=bodyWithoutThisAndRv,
20264 canRunScript=method.canRunScript,
20266 method,
20269 def deps(self):
20270 return self._deps
20273 class CGCallbackFunction(CGCallback):
20274 def __init__(self, callback, descriptorProvider):
20275 self.callback = callback
20276 if callback.isConstructor():
20277 methods = [ConstructCallback(callback, descriptorProvider)]
20278 else:
20279 methods = [CallCallback(callback, descriptorProvider)]
20280 CGCallback.__init__(
20281 self, callback, descriptorProvider, "CallbackFunction", methods
20284 def getConstructors(self):
20285 return CGCallback.getConstructors(self) + [
20286 ClassConstructor(
20287 [Argument("CallbackFunction*", "aOther")],
20288 bodyInHeader=True,
20289 visibility="public",
20290 explicit=True,
20291 baseConstructors=["CallbackFunction(aOther)"],
20296 class CGFastCallback(CGClass):
20297 def __init__(self, idlObject):
20298 self._deps = idlObject.getDeps()
20299 baseName = idlObject.identifier.name
20300 constructor = ClassConstructor(
20302 Argument("JSObject*", "aCallback"),
20303 Argument("JSObject*", "aCallbackGlobal"),
20305 bodyInHeader=True,
20306 visibility="public",
20307 explicit=True,
20308 baseConstructors=[
20309 "%s(aCallback, aCallbackGlobal, FastCallbackConstructor())" % baseName,
20311 body="",
20314 traceMethod = ClassMethod(
20315 "Trace",
20316 "void",
20317 [Argument("JSTracer*", "aTracer")],
20318 inline=True,
20319 bodyInHeader=True,
20320 visibility="public",
20321 body="%s::Trace(aTracer);\n" % baseName,
20323 holdMethod = ClassMethod(
20324 "FinishSlowJSInitIfMoreThanOneOwner",
20325 "void",
20326 [Argument("JSContext*", "aCx")],
20327 inline=True,
20328 bodyInHeader=True,
20329 visibility="public",
20330 body=("%s::FinishSlowJSInitIfMoreThanOneOwner(aCx);\n" % baseName),
20333 CGClass.__init__(
20334 self,
20335 "Fast%s" % baseName,
20336 bases=[ClassBase(baseName)],
20337 constructors=[constructor],
20338 methods=[traceMethod, holdMethod],
20341 def deps(self):
20342 return self._deps
20345 class CGCallbackInterface(CGCallback):
20346 def __init__(self, descriptor, spiderMonkeyInterfacesAreStructs=False):
20347 iface = descriptor.interface
20348 attrs = [
20350 for m in iface.members
20351 if (
20352 m.isAttr()
20353 and not m.isStatic()
20354 and (not m.isMaplikeOrSetlikeAttr() or not iface.isJSImplemented())
20357 getters = [
20358 CallbackGetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
20359 for a in attrs
20361 setters = [
20362 CallbackSetter(a, descriptor, spiderMonkeyInterfacesAreStructs)
20363 for a in attrs
20364 if not a.readonly
20366 methods = [
20368 for m in iface.members
20369 if (
20370 m.isMethod()
20371 and not m.isStatic()
20372 and not m.isIdentifierLess()
20373 and (
20374 not m.isMaplikeOrSetlikeOrIterableMethod()
20375 or not iface.isJSImplemented()
20379 methods = [
20380 CallbackOperation(m, sig, descriptor, spiderMonkeyInterfacesAreStructs)
20381 for m in methods
20382 for sig in m.signatures()
20385 needInitId = False
20386 if iface.isJSImplemented() and iface.ctor():
20387 sigs = descriptor.interface.ctor().signatures()
20388 if len(sigs) != 1:
20389 raise TypeError("We only handle one constructor. See bug 869268.")
20390 methods.append(CGJSImplInitOperation(sigs[0], descriptor))
20391 needInitId = True
20393 needOnGetId = False
20394 if (
20395 iface.isJSImplemented()
20396 and iface.maplikeOrSetlikeOrIterable
20397 and iface.maplikeOrSetlikeOrIterable.isMaplike()
20399 methods.append(CGJSImplOnGetOperation(descriptor))
20400 needOnGetId = True
20402 idlist = [
20403 descriptor.binaryNameFor(m.identifier.name)
20404 for m in iface.members
20405 if m.isAttr() or m.isMethod()
20407 if needInitId:
20408 idlist.append("__init")
20409 if needOnGetId:
20410 idlist.append("__onget")
20412 if iface.isJSImplemented() and iface.getExtendedAttribute(
20413 "WantsEventListenerHooks"
20415 methods.append(CGJSImplEventHookOperation(descriptor, "eventListenerAdded"))
20416 methods.append(
20417 CGJSImplEventHookOperation(descriptor, "eventListenerRemoved")
20419 idlist.append("eventListenerAdded")
20420 idlist.append("eventListenerRemoved")
20422 if len(idlist) != 0:
20423 methods.append(initIdsClassMethod(idlist, iface.identifier.name + "Atoms"))
20424 CGCallback.__init__(
20425 self,
20426 iface,
20427 descriptor,
20428 "CallbackInterface",
20429 methods,
20430 getters=getters,
20431 setters=setters,
20435 class FakeMember:
20436 def __init__(self, name=None):
20437 if name is not None:
20438 self.identifier = FakeIdentifier(name)
20440 def isStatic(self):
20441 return False
20443 def isAttr(self):
20444 return False
20446 def isMethod(self):
20447 return False
20449 def getExtendedAttribute(self, name):
20450 # Claim to be a [NewObject] so we can avoid the "return a raw pointer"
20451 # comments CGNativeMember codegen would otherwise stick in.
20452 if name == "NewObject":
20453 return True
20454 return None
20457 class CallbackMember(CGNativeMember):
20458 # XXXbz It's OK to use CallbackKnownNotGray for wrapScope because
20459 # CallSetup already handled the unmark-gray bits for us. we don't have
20460 # anything better to use for 'obj', really...
20461 def __init__(
20462 self,
20463 sig,
20464 name,
20465 descriptorProvider,
20466 needThisHandling,
20467 rethrowContentException=False,
20468 spiderMonkeyInterfacesAreStructs=False,
20469 wrapScope=None,
20470 canRunScript=False,
20471 passJSBitsAsNeeded=False,
20474 needThisHandling is True if we need to be able to accept a specified
20475 thisObj, False otherwise.
20477 assert not rethrowContentException or not needThisHandling
20479 self.retvalType = sig[0]
20480 self.originalSig = sig
20481 args = sig[1]
20482 self.argCount = len(args)
20483 if self.argCount > 0:
20484 # Check for variadic arguments
20485 lastArg = args[self.argCount - 1]
20486 if lastArg.variadic:
20487 self.argCountStr = "(%d - 1) + %s.Length()" % (
20488 self.argCount,
20489 lastArg.identifier.name,
20491 else:
20492 self.argCountStr = "%d" % self.argCount
20493 self.needThisHandling = needThisHandling
20494 # If needThisHandling, we generate ourselves as private and the caller
20495 # will handle generating public versions that handle the "this" stuff.
20496 visibility = "private" if needThisHandling else "public"
20497 self.rethrowContentException = rethrowContentException
20499 self.wrapScope = wrapScope
20500 # We don't care, for callback codegen, whether our original member was
20501 # a method or attribute or whatnot. Just always pass FakeMember()
20502 # here.
20503 CGNativeMember.__init__(
20504 self,
20505 descriptorProvider,
20506 FakeMember(),
20507 name,
20508 (self.retvalType, args),
20509 extendedAttrs=["needsErrorResult"],
20510 passJSBitsAsNeeded=passJSBitsAsNeeded,
20511 visibility=visibility,
20512 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
20513 canRunScript=canRunScript,
20515 # We have to do all the generation of our body now, because
20516 # the caller relies on us throwing if we can't manage it.
20517 self.exceptionCode = (
20518 "aRv.Throw(NS_ERROR_UNEXPECTED);\n" "return%s;\n" % self.getDefaultRetval()
20520 self.body = self.getImpl()
20522 def getImpl(self):
20523 setupCall = self.getCallSetup()
20524 declRval = self.getRvalDecl()
20525 if self.argCount > 0:
20526 argvDecl = fill(
20528 JS::RootedVector<JS::Value> argv(cx);
20529 if (!argv.resize(${argCount})) {
20530 $*{failureCode}
20531 return${errorReturn};
20533 """,
20534 argCount=self.argCountStr,
20535 failureCode=self.getArgvDeclFailureCode(),
20536 errorReturn=self.getDefaultRetval(),
20538 else:
20539 # Avoid weird 0-sized arrays
20540 argvDecl = ""
20541 convertArgs = self.getArgConversions()
20542 doCall = self.getCall()
20543 returnResult = self.getResultConversion()
20545 body = declRval + argvDecl + convertArgs + doCall
20546 if self.needsScopeBody():
20547 body = "{\n" + indent(body) + "}\n"
20548 return setupCall + body + returnResult
20550 def needsScopeBody(self):
20551 return False
20553 def getArgvDeclFailureCode(self):
20554 return dedent(
20556 // That threw an exception on the JSContext, and our CallSetup will do
20557 // the right thing with that.
20561 def getResultConversion(
20562 self, val="rval", failureCode=None, isDefinitelyObject=False, exceptionCode=None
20564 replacements = {
20565 "val": val,
20566 "holderName": "rvalHolder",
20567 "declName": "rvalDecl",
20568 # We actually want to pass in a null scope object here, because
20569 # wrapping things into our current compartment (that of mCallback)
20570 # is what we want.
20571 "obj": "nullptr",
20572 "passedToJSImpl": "false",
20575 if isJSImplementedDescriptor(self.descriptorProvider):
20576 isCallbackReturnValue = "JSImpl"
20577 else:
20578 isCallbackReturnValue = "Callback"
20579 sourceDescription = "return value of %s" % self.getPrettyName()
20580 convertType = instantiateJSToNativeConversion(
20581 getJSToNativeConversionInfo(
20582 self.retvalType,
20583 self.descriptorProvider,
20584 failureCode=failureCode,
20585 isDefinitelyObject=isDefinitelyObject,
20586 exceptionCode=exceptionCode or self.exceptionCode,
20587 isCallbackReturnValue=isCallbackReturnValue,
20588 # Allow returning a callback type that
20589 # allows non-callable objects.
20590 allowTreatNonCallableAsNull=True,
20591 sourceDescription=sourceDescription,
20593 replacements,
20595 assignRetval = string.Template(
20596 self.getRetvalInfo(self.retvalType, False)[2]
20597 ).substitute(replacements)
20598 type = convertType.define()
20599 return type + assignRetval
20601 def getArgConversions(self):
20602 # Just reget the arglist from self.originalSig, because our superclasses
20603 # just have way to many members they like to clobber, so I can't find a
20604 # safe member name to store it in.
20605 argConversions = [
20606 self.getArgConversion(i, arg) for i, arg in enumerate(self.originalSig[1])
20608 if not argConversions:
20609 return "\n"
20611 # Do them back to front, so our argc modifications will work
20612 # correctly, because we examine trailing arguments first.
20613 argConversions.reverse()
20614 # Wrap each one in a scope so that any locals it has don't leak out, and
20615 # also so that we can just "break;" for our successCode.
20616 argConversions = [
20617 CGWrapper(CGIndenter(CGGeneric(c)), pre="do {\n", post="} while (false);\n")
20618 for c in argConversions
20620 if self.argCount > 0:
20621 argConversions.insert(0, self.getArgcDecl())
20622 # And slap them together.
20623 return CGList(argConversions, "\n").define() + "\n"
20625 def getArgConversion(self, i, arg):
20626 argval = arg.identifier.name
20628 if arg.variadic:
20629 argval = argval + "[idx]"
20630 jsvalIndex = "%d + idx" % i
20631 else:
20632 jsvalIndex = "%d" % i
20633 if arg.canHaveMissingValue():
20634 argval += ".Value()"
20636 if arg.type.isDOMString():
20637 # XPConnect string-to-JS conversion wants to mutate the string. So
20638 # let's give it a string it can mutate
20639 # XXXbz if we try to do a sequence of strings, this will kinda fail.
20640 result = "mutableStr"
20641 prepend = "nsString mutableStr(%s);\n" % argval
20642 else:
20643 result = argval
20644 prepend = ""
20646 if arg.type.isUnion() and self.wrapScope is None:
20647 prepend += (
20648 "JS::Rooted<JSObject*> callbackObj(cx, CallbackKnownNotGray());\n"
20650 self.wrapScope = "callbackObj"
20652 conversion = prepend + wrapForType(
20653 arg.type,
20654 self.descriptorProvider,
20656 "result": result,
20657 "successCode": "continue;\n" if arg.variadic else "break;\n",
20658 "jsvalRef": "argv[%s]" % jsvalIndex,
20659 "jsvalHandle": "argv[%s]" % jsvalIndex,
20660 "obj": self.wrapScope,
20661 "returnsNewObject": False,
20662 "exceptionCode": self.exceptionCode,
20663 "spiderMonkeyInterfacesAreStructs": self.spiderMonkeyInterfacesAreStructs,
20667 if arg.variadic:
20668 conversion = fill(
20670 for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) {
20671 $*{conversion}
20673 break;
20674 """,
20675 arg=arg.identifier.name,
20676 conversion=conversion,
20678 elif arg.canHaveMissingValue():
20679 conversion = fill(
20681 if (${argName}.WasPassed()) {
20682 $*{conversion}
20683 } else if (argc == ${iPlus1}) {
20684 // This is our current trailing argument; reduce argc
20685 --argc;
20686 } else {
20687 argv[${i}].setUndefined();
20689 """,
20690 argName=arg.identifier.name,
20691 conversion=conversion,
20692 iPlus1=i + 1,
20693 i=i,
20695 return conversion
20697 def getDefaultRetval(self):
20698 default = self.getRetvalInfo(self.retvalType, False)[1]
20699 if len(default) != 0:
20700 default = " " + default
20701 return default
20703 def getArgs(self, returnType, argList):
20704 args = CGNativeMember.getArgs(self, returnType, argList)
20705 if not self.needThisHandling:
20706 # Since we don't need this handling, we're the actual method that
20707 # will be called, so we need an aRethrowExceptions argument.
20708 if not self.rethrowContentException:
20709 args.append(Argument("const char*", "aExecutionReason", "nullptr"))
20710 args.append(
20711 Argument(
20712 "ExceptionHandling", "aExceptionHandling", "eReportExceptions"
20715 args.append(Argument("JS::Realm*", "aRealm", "nullptr"))
20716 return args
20717 # We want to allow the caller to pass in a "this" value, as
20718 # well as a BindingCallContext.
20719 return [
20720 Argument("BindingCallContext&", "cx"),
20721 Argument("JS::Handle<JS::Value>", "aThisVal"),
20722 ] + args
20724 def getCallSetup(self):
20725 if self.needThisHandling:
20726 # It's been done for us already
20727 return ""
20728 callSetup = "CallSetup s(this, aRv"
20729 if self.rethrowContentException:
20730 # getArgs doesn't add the aExceptionHandling argument but does add
20731 # aRealm for us.
20732 callSetup += (
20733 ', "%s", eRethrowContentExceptions, aRealm, /* aIsJSImplementedWebIDL = */ '
20734 % self.getPrettyName()
20736 callSetup += toStringBool(
20737 isJSImplementedDescriptor(self.descriptorProvider)
20739 else:
20740 callSetup += ', "%s", aExceptionHandling, aRealm' % self.getPrettyName()
20741 callSetup += ");\n"
20742 return fill(
20744 $*{callSetup}
20745 if (aRv.Failed()) {
20746 return${errorReturn};
20748 MOZ_ASSERT(s.GetContext());
20749 BindingCallContext& cx = s.GetCallContext();
20751 """,
20752 callSetup=callSetup,
20753 errorReturn=self.getDefaultRetval(),
20756 def getArgcDecl(self):
20757 return CGGeneric("unsigned argc = %s;\n" % self.argCountStr)
20759 @staticmethod
20760 def ensureASCIIName(idlObject):
20761 type = "attribute" if idlObject.isAttr() else "operation"
20762 if re.match("[^\x20-\x7E]", idlObject.identifier.name):
20763 raise SyntaxError(
20764 'Callback %s name "%s" contains non-ASCII '
20765 "characters. We can't handle that. %s"
20766 % (type, idlObject.identifier.name, idlObject.location)
20768 if re.match('"', idlObject.identifier.name):
20769 raise SyntaxError(
20770 "Callback %s name '%s' contains "
20771 "double-quote character. We can't handle "
20772 "that. %s" % (type, idlObject.identifier.name, idlObject.location)
20776 class ConstructCallback(CallbackMember):
20777 def __init__(self, callback, descriptorProvider):
20778 self.callback = callback
20779 CallbackMember.__init__(
20780 self,
20781 callback.signatures()[0],
20782 "Construct",
20783 descriptorProvider,
20784 needThisHandling=False,
20785 canRunScript=True,
20788 def getRvalDecl(self):
20789 # Box constructedObj for getJSToNativeConversionInfo().
20790 return "JS::Rooted<JS::Value> rval(cx);\n"
20792 def getCall(self):
20793 if self.argCount > 0:
20794 args = "JS::HandleValueArray::subarray(argv, 0, argc)"
20795 else:
20796 args = "JS::HandleValueArray::empty()"
20798 return fill(
20800 JS::Rooted<JS::Value> constructor(cx, JS::ObjectValue(*mCallback));
20801 JS::Rooted<JSObject*> constructedObj(cx);
20802 if (!JS::Construct(cx, constructor,
20803 ${args}, &constructedObj)) {
20804 aRv.NoteJSContextException(cx);
20805 return${errorReturn};
20807 rval.setObject(*constructedObj);
20808 """,
20809 args=args,
20810 errorReturn=self.getDefaultRetval(),
20813 def getResultConversion(self):
20814 return CallbackMember.getResultConversion(self, isDefinitelyObject=True)
20816 def getPrettyName(self):
20817 return self.callback.identifier.name
20820 class CallbackMethod(CallbackMember):
20821 def __init__(
20822 self,
20823 sig,
20824 name,
20825 descriptorProvider,
20826 needThisHandling,
20827 rethrowContentException=False,
20828 spiderMonkeyInterfacesAreStructs=False,
20829 canRunScript=False,
20831 CallbackMember.__init__(
20832 self,
20833 sig,
20834 name,
20835 descriptorProvider,
20836 needThisHandling,
20837 rethrowContentException,
20838 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
20839 canRunScript=canRunScript,
20842 def getRvalDecl(self):
20843 return "JS::Rooted<JS::Value> rval(cx);\n"
20845 def getCall(self):
20846 if self.argCount > 0:
20847 args = "JS::HandleValueArray::subarray(argv, 0, argc)"
20848 else:
20849 args = "JS::HandleValueArray::empty()"
20851 return fill(
20853 $*{declCallable}
20854 $*{declThis}
20855 if (${callGuard}!JS::Call(cx, ${thisVal}, callable,
20856 ${args}, &rval)) {
20857 aRv.NoteJSContextException(cx);
20858 return${errorReturn};
20860 """,
20861 declCallable=self.getCallableDecl(),
20862 declThis=self.getThisDecl(),
20863 callGuard=self.getCallGuard(),
20864 thisVal=self.getThisVal(),
20865 args=args,
20866 errorReturn=self.getDefaultRetval(),
20870 class CallCallback(CallbackMethod):
20871 def __init__(self, callback, descriptorProvider):
20872 self.callback = callback
20873 CallbackMethod.__init__(
20874 self,
20875 callback.signatures()[0],
20876 "Call",
20877 descriptorProvider,
20878 needThisHandling=True,
20879 canRunScript=not callback.isRunScriptBoundary(),
20882 def getThisDecl(self):
20883 return ""
20885 def getThisVal(self):
20886 return "aThisVal"
20888 def getCallableDecl(self):
20889 return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n"
20891 def getPrettyName(self):
20892 return self.callback.identifier.name
20894 def getCallGuard(self):
20895 if self.callback._treatNonObjectAsNull:
20896 return "JS::IsCallable(mCallback) && "
20897 return ""
20900 class CallbackOperationBase(CallbackMethod):
20902 Common class for implementing various callback operations.
20905 def __init__(
20906 self,
20907 signature,
20908 jsName,
20909 nativeName,
20910 descriptor,
20911 singleOperation,
20912 rethrowContentException=False,
20913 spiderMonkeyInterfacesAreStructs=False,
20915 self.singleOperation = singleOperation
20916 self.methodName = descriptor.binaryNameFor(jsName)
20917 CallbackMethod.__init__(
20918 self,
20919 signature,
20920 nativeName,
20921 descriptor,
20922 singleOperation,
20923 rethrowContentException,
20924 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
20927 def getThisDecl(self):
20928 if not self.singleOperation:
20929 return "JS::Rooted<JS::Value> thisValue(cx, JS::ObjectValue(*mCallback));\n"
20930 # This relies on getCallableDecl declaring a boolean
20931 # isCallable in the case when we're a single-operation
20932 # interface.
20933 return dedent(
20935 JS::Rooted<JS::Value> thisValue(cx, isCallable ? aThisVal.get()
20936 : JS::ObjectValue(*mCallback));
20940 def getThisVal(self):
20941 return "thisValue"
20943 def getCallableDecl(self):
20944 getCallableFromProp = fill(
20946 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
20947 if ((JSID_IS_VOID(*reinterpret_cast<jsid*>(atomsCache)) &&
20948 !InitIds(cx, atomsCache)) ||
20949 !GetCallableProperty(cx, atomsCache->${methodAtomName}, &callable)) {
20950 aRv.Throw(NS_ERROR_UNEXPECTED);
20951 return${errorReturn};
20953 """,
20954 methodAtomName=CGDictionary.makeIdName(self.methodName),
20955 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
20956 errorReturn=self.getDefaultRetval(),
20958 if not self.singleOperation:
20959 return "JS::Rooted<JS::Value> callable(cx);\n" + getCallableFromProp
20960 return fill(
20962 bool isCallable = JS::IsCallable(mCallback);
20963 JS::Rooted<JS::Value> callable(cx);
20964 if (isCallable) {
20965 callable = JS::ObjectValue(*mCallback);
20966 } else {
20967 $*{getCallableFromProp}
20969 """,
20970 getCallableFromProp=getCallableFromProp,
20973 def getCallGuard(self):
20974 return ""
20977 class CallbackOperation(CallbackOperationBase):
20979 Codegen actual WebIDL operations on callback interfaces.
20982 def __init__(self, method, signature, descriptor, spiderMonkeyInterfacesAreStructs):
20983 self.ensureASCIIName(method)
20984 self.method = method
20985 jsName = method.identifier.name
20986 CallbackOperationBase.__init__(
20987 self,
20988 signature,
20989 jsName,
20990 MakeNativeName(descriptor.binaryNameFor(jsName)),
20991 descriptor,
20992 descriptor.interface.isSingleOperationInterface(),
20993 rethrowContentException=descriptor.interface.isJSImplemented(),
20994 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
20997 def getPrettyName(self):
20998 return "%s.%s" % (
20999 self.descriptorProvider.interface.identifier.name,
21000 self.method.identifier.name,
21004 class CallbackAccessor(CallbackMember):
21006 Shared superclass for CallbackGetter and CallbackSetter.
21009 def __init__(self, attr, sig, name, descriptor, spiderMonkeyInterfacesAreStructs):
21010 self.ensureASCIIName(attr)
21011 self.attrName = attr.identifier.name
21012 CallbackMember.__init__(
21013 self,
21014 sig,
21015 name,
21016 descriptor,
21017 needThisHandling=False,
21018 rethrowContentException=descriptor.interface.isJSImplemented(),
21019 spiderMonkeyInterfacesAreStructs=spiderMonkeyInterfacesAreStructs,
21022 def getPrettyName(self):
21023 return "%s.%s" % (
21024 self.descriptorProvider.interface.identifier.name,
21025 self.attrName,
21029 class CallbackGetter(CallbackAccessor):
21030 def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
21031 CallbackAccessor.__init__(
21032 self,
21033 attr,
21034 (attr.type, []),
21035 callbackGetterName(attr, descriptor),
21036 descriptor,
21037 spiderMonkeyInterfacesAreStructs,
21040 def getRvalDecl(self):
21041 return "JS::Rooted<JS::Value> rval(cx);\n"
21043 def getCall(self):
21044 return fill(
21046 JS::Rooted<JSObject *> callback(cx, mCallback);
21047 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
21048 if ((JSID_IS_VOID(*reinterpret_cast<jsid*>(atomsCache))
21049 && !InitIds(cx, atomsCache)) ||
21050 !JS_GetPropertyById(cx, callback, atomsCache->${attrAtomName}, &rval)) {
21051 aRv.Throw(NS_ERROR_UNEXPECTED);
21052 return${errorReturn};
21054 """,
21055 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
21056 attrAtomName=CGDictionary.makeIdName(
21057 self.descriptorProvider.binaryNameFor(self.attrName)
21059 errorReturn=self.getDefaultRetval(),
21063 class CallbackSetter(CallbackAccessor):
21064 def __init__(self, attr, descriptor, spiderMonkeyInterfacesAreStructs):
21065 CallbackAccessor.__init__(
21066 self,
21067 attr,
21068 (BuiltinTypes[IDLBuiltinType.Types.void], [FakeArgument(attr.type, attr)]),
21069 callbackSetterName(attr, descriptor),
21070 descriptor,
21071 spiderMonkeyInterfacesAreStructs,
21074 def getRvalDecl(self):
21075 # We don't need an rval
21076 return ""
21078 def getCall(self):
21079 return fill(
21081 MOZ_ASSERT(argv.length() == 1);
21082 JS::Rooted<JSObject*> callback(cx, CallbackKnownNotGray());
21083 ${atomCacheName}* atomsCache = GetAtomCache<${atomCacheName}>(cx);
21084 if ((JSID_IS_VOID(*reinterpret_cast<jsid*>(atomsCache)) &&
21085 !InitIds(cx, atomsCache)) ||
21086 !JS_SetPropertyById(cx, callback, atomsCache->${attrAtomName}, argv[0])) {
21087 aRv.Throw(NS_ERROR_UNEXPECTED);
21088 return${errorReturn};
21090 """,
21091 atomCacheName=self.descriptorProvider.interface.identifier.name + "Atoms",
21092 attrAtomName=CGDictionary.makeIdName(
21093 self.descriptorProvider.binaryNameFor(self.attrName)
21095 errorReturn=self.getDefaultRetval(),
21098 def getArgcDecl(self):
21099 return None
21102 class CGJSImplInitOperation(CallbackOperationBase):
21104 Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL.
21107 def __init__(self, sig, descriptor):
21108 assert sig in descriptor.interface.ctor().signatures()
21109 CallbackOperationBase.__init__(
21110 self,
21111 (BuiltinTypes[IDLBuiltinType.Types.void], sig[1]),
21112 "__init",
21113 "__Init",
21114 descriptor,
21115 singleOperation=False,
21116 rethrowContentException=True,
21117 spiderMonkeyInterfacesAreStructs=True,
21120 def getPrettyName(self):
21121 return "__init"
21124 class CGJSImplOnGetOperation(CallbackOperationBase):
21126 Codegen the __OnGet() method used to notify the JS impl that a get() is
21127 happening on a JS-implemented maplike. This method takes two arguments
21128 (key and value) and returns nothing.
21131 def __init__(self, descriptor):
21132 CallbackOperationBase.__init__(
21133 self,
21135 BuiltinTypes[IDLBuiltinType.Types.void],
21137 FakeArgument(BuiltinTypes[IDLBuiltinType.Types.any], None, "key"),
21138 FakeArgument(BuiltinTypes[IDLBuiltinType.Types.any], None, "value"),
21141 "__onget",
21142 "__OnGet",
21143 descriptor,
21144 singleOperation=False,
21145 rethrowContentException=True,
21146 spiderMonkeyInterfacesAreStructs=True,
21149 def getPrettyName(self):
21150 return "__onget"
21153 class CGJSImplEventHookOperation(CallbackOperationBase):
21155 Codegen the hooks on a JS impl for adding/removing event listeners.
21158 def __init__(self, descriptor, name):
21159 self.name = name
21161 CallbackOperationBase.__init__(
21162 self,
21164 BuiltinTypes[IDLBuiltinType.Types.void],
21166 FakeArgument(
21167 BuiltinTypes[IDLBuiltinType.Types.domstring], None, "aType"
21171 name,
21172 MakeNativeName(name),
21173 descriptor,
21174 singleOperation=False,
21175 rethrowContentException=False,
21176 spiderMonkeyInterfacesAreStructs=True,
21179 def getPrettyName(self):
21180 return self.name
21183 def getMaplikeOrSetlikeErrorReturn(helperImpl):
21185 Generate return values based on whether a maplike or setlike generated
21186 method is an interface method (which returns bool) or a helper function
21187 (which uses ErrorResult).
21189 if helperImpl:
21190 return dedent(
21192 aRv.Throw(NS_ERROR_UNEXPECTED);
21193 return%s;
21195 % helperImpl.getDefaultRetval()
21197 return "return false;\n"
21200 def getMaplikeOrSetlikeBackingObject(descriptor, maplikeOrSetlike, helperImpl=None):
21202 Generate code to get/create a JS backing object for a maplike/setlike
21203 declaration from the declaration slot.
21205 func_prefix = maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title()
21206 ret = fill(
21208 JS::Rooted<JSObject*> backingObj(cx);
21209 bool created = false;
21210 if (!Get${func_prefix}BackingObject(cx, obj, ${slot}, &backingObj, &created)) {
21211 $*{errorReturn}
21213 if (created) {
21214 PreserveWrapper<${selfType}>(self);
21216 """,
21217 slot=memberReservedSlot(maplikeOrSetlike, descriptor),
21218 func_prefix=func_prefix,
21219 errorReturn=getMaplikeOrSetlikeErrorReturn(helperImpl),
21220 selfType=descriptor.nativeType,
21222 return ret
21225 def getMaplikeOrSetlikeSizeGetterBody(descriptor, attr):
21227 Creates the body for the size getter method of maplike/setlike interfaces.
21229 # We should only have one declaration attribute currently
21230 assert attr.identifier.name == "size"
21231 assert attr.isMaplikeOrSetlikeAttr()
21232 return fill(
21234 $*{getBackingObj}
21235 uint32_t result = JS::${funcPrefix}Size(cx, backingObj);
21236 MOZ_ASSERT(!JS_IsExceptionPending(cx));
21237 args.rval().setNumber(result);
21238 return true;
21239 """,
21240 getBackingObj=getMaplikeOrSetlikeBackingObject(
21241 descriptor, attr.maplikeOrSetlike
21243 funcPrefix=attr.maplikeOrSetlike.prefix,
21247 class CGMaplikeOrSetlikeMethodGenerator(CGThing):
21249 Creates methods for maplike/setlike interfaces. It is expected that all
21250 methods will be have a maplike/setlike object attached. Unwrapping/wrapping
21251 will be taken care of by the usual method generation machinery in
21252 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
21253 using CGCallGenerator.
21256 def __init__(
21257 self,
21258 descriptor,
21259 maplikeOrSetlike,
21260 methodName,
21261 needsValueTypeReturn=False,
21262 helperImpl=None,
21264 CGThing.__init__(self)
21265 # True if this will be the body of a C++ helper function.
21266 self.helperImpl = helperImpl
21267 self.descriptor = descriptor
21268 self.maplikeOrSetlike = maplikeOrSetlike
21269 self.cgRoot = CGList([])
21270 impl_method_name = methodName
21271 if impl_method_name[0] == "_":
21272 # double underscore means this is a js-implemented chrome only rw
21273 # function. Truncate the double underscore so calling the right
21274 # underlying JSAPI function still works.
21275 impl_method_name = impl_method_name[2:]
21276 self.cgRoot.append(
21277 CGGeneric(
21278 getMaplikeOrSetlikeBackingObject(
21279 self.descriptor, self.maplikeOrSetlike, self.helperImpl
21283 self.returnStmt = getMaplikeOrSetlikeErrorReturn(self.helperImpl)
21285 # Generates required code for the method. Method descriptions included
21286 # in definitions below. Throw if we don't have a method to fill in what
21287 # we're looking for.
21288 try:
21289 methodGenerator = getattr(self, impl_method_name)
21290 except AttributeError:
21291 raise TypeError(
21292 "Missing %s method definition '%s'"
21293 % (self.maplikeOrSetlike.maplikeOrSetlikeType, methodName)
21295 # Method generator returns tuple, containing:
21297 # - a list of CGThings representing setup code for preparing to call
21298 # the JS API function
21299 # - a list of arguments needed for the JS API function we're calling
21300 # - list of code CGThings needed for return value conversion.
21301 (setupCode, arguments, setResult) = methodGenerator()
21303 # Create the actual method call, and then wrap it with the code to
21304 # return the value if needed.
21305 funcName = self.maplikeOrSetlike.prefix + MakeNativeName(impl_method_name)
21306 # Append the list of setup code CGThings
21307 self.cgRoot.append(CGList(setupCode))
21308 # Create the JS API call
21309 code = dedent(
21311 if (!JS::${funcName}(${args})) {
21312 $*{errorReturn}
21317 if needsValueTypeReturn:
21318 assert self.helperImpl and impl_method_name == "get"
21319 code += fill(
21321 if (result.isUndefined()) {
21322 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
21323 return${retval};
21325 """,
21326 retval=self.helperImpl.getDefaultRetval(),
21329 self.cgRoot.append(
21330 CGWrapper(
21331 CGGeneric(
21332 fill(
21333 code,
21334 funcName=funcName,
21335 args=", ".join(["cx", "backingObj"] + arguments),
21336 errorReturn=self.returnStmt,
21341 # Append result conversion
21342 self.cgRoot.append(CGList(setResult))
21344 def mergeTuples(self, a, b):
21346 Expecting to take 2 tuples were all elements are lists, append the lists in
21347 the second tuple to the lists in the first.
21349 return tuple([x + y for x, y in zip(a, b)])
21351 def appendArgConversion(self, name):
21353 Generate code to convert arguments to JS::Values, so they can be
21354 passed into JSAPI functions.
21356 return CGGeneric(
21357 fill(
21359 JS::Rooted<JS::Value> ${name}Val(cx);
21360 if (!ToJSValue(cx, ${name}, &${name}Val)) {
21361 $*{errorReturn}
21363 """,
21364 name=name,
21365 errorReturn=self.returnStmt,
21369 def appendKeyArgConversion(self):
21371 Generates the key argument for methods. Helper functions will use
21372 a RootedVector<JS::Value>, while interface methods have separate JS::Values.
21374 if self.helperImpl:
21375 return ([], ["argv[0]"], [])
21376 return ([self.appendArgConversion("arg0")], ["arg0Val"], [])
21378 def appendKeyAndValueArgConversion(self):
21380 Generates arguments for methods that require a key and value. Helper
21381 functions will use a RootedVector<JS::Value>, while interface methods have
21382 separate JS::Values.
21384 r = self.appendKeyArgConversion()
21385 if self.helperImpl:
21386 return self.mergeTuples(r, ([], ["argv[1]"], []))
21387 return self.mergeTuples(
21388 r, ([self.appendArgConversion("arg1")], ["arg1Val"], [])
21391 def appendIteratorResult(self):
21393 Generate code to output JSObject* return values, needed for functions that
21394 return iterators. Iterators cannot currently be wrapped via Xrays. If
21395 something that would return an iterator is called via Xray, fail early.
21397 # TODO: Bug 1173651 - Remove check once bug 1023984 is fixed.
21398 code = CGGeneric(
21399 dedent(
21401 // TODO (Bug 1173651): Xrays currently cannot wrap iterators. Change
21402 // after bug 1023984 is fixed.
21403 if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
21404 JS_ReportErrorASCII(cx, "Xray wrapping of iterators not supported.");
21405 return false;
21407 JS::Rooted<JSObject*> result(cx);
21408 JS::Rooted<JS::Value> v(cx);
21412 arguments = "&v"
21413 setResult = CGGeneric(
21414 dedent(
21416 result = &v.toObject();
21420 return ([code], [arguments], [setResult])
21422 def appendSelfResult(self):
21424 Generate code to return the interface object itself.
21426 code = CGGeneric(
21427 dedent(
21429 JS::Rooted<JSObject*> result(cx);
21433 setResult = CGGeneric(
21434 dedent(
21436 result = obj;
21440 return ([code], [], [setResult])
21442 def appendBoolResult(self):
21443 if self.helperImpl:
21444 return ([CGGeneric()], ["&aRetVal"], [])
21445 return ([CGGeneric("bool result;\n")], ["&result"], [])
21447 def forEach(self):
21449 void forEach(callback c, any thisval);
21451 ForEach takes a callback, and a possible value to use as 'this'. The
21452 callback needs to take value, key, and the interface object
21453 implementing maplike/setlike. In order to make sure that the third arg
21454 is our interface object instead of the map/set backing object, we
21455 create a js function with the callback and original object in its
21456 storage slots, then use a helper function in BindingUtils to make sure
21457 the callback is called correctly.
21459 assert not self.helperImpl
21460 code = [
21461 CGGeneric(
21462 dedent(
21464 // Create a wrapper function.
21465 JSFunction* func = js::NewFunctionWithReserved(cx, ForEachHandler, 3, 0, nullptr);
21466 if (!func) {
21467 return false;
21469 JS::Rooted<JSObject*> funcObj(cx, JS_GetFunctionObject(func));
21470 JS::Rooted<JS::Value> funcVal(cx, JS::ObjectValue(*funcObj));
21471 js::SetFunctionNativeReserved(funcObj, FOREACH_CALLBACK_SLOT,
21472 JS::ObjectValue(*arg0));
21473 js::SetFunctionNativeReserved(funcObj, FOREACH_MAPLIKEORSETLIKEOBJ_SLOT,
21474 JS::ObjectValue(*obj));
21479 arguments = ["funcVal", "arg1"]
21480 return (code, arguments, [])
21482 def set(self):
21484 object set(key, value);
21486 Maplike only function, takes key and sets value to it, returns
21487 interface object unless being called from a C++ helper.
21489 assert self.maplikeOrSetlike.isMaplike()
21490 r = self.appendKeyAndValueArgConversion()
21491 if self.helperImpl:
21492 return r
21493 return self.mergeTuples(r, self.appendSelfResult())
21495 def add(self):
21497 object add(value);
21499 Setlike only function, adds value to set, returns interface object
21500 unless being called from a C++ helper
21502 assert self.maplikeOrSetlike.isSetlike()
21503 r = self.appendKeyArgConversion()
21504 if self.helperImpl:
21505 return r
21506 return self.mergeTuples(r, self.appendSelfResult())
21508 def get(self):
21510 type? get(key);
21512 Retrieves a value from a backing object based on the key. Returns value
21513 if key is in backing object, undefined otherwise.
21515 assert self.maplikeOrSetlike.isMaplike()
21516 r = self.appendKeyArgConversion()
21518 code = []
21519 # We don't need to create the result variable because it'll be created elsewhere
21520 # for JSObject Get method
21521 if not self.helperImpl or not self.helperImpl.handleJSObjectGetHelper():
21522 code = [
21523 CGGeneric(
21524 dedent(
21526 JS::Rooted<JS::Value> result(cx);
21532 arguments = ["&result"]
21533 callOnGet = []
21534 if (
21535 self.descriptor.interface.isJSImplemented()
21536 and not self.helperImpl # For C++ MaplikeHelper Get method, we don't notify underlying js implementation
21538 callOnGet = [
21539 CGGeneric(
21540 dedent(
21543 JS::ExposeValueToActiveJS(result);
21544 ErrorResult onGetResult;
21545 self->__OnGet(arg0Val, result, onGetResult);
21546 if (onGetResult.MaybeSetPendingException(cx)) {
21547 return false;
21554 return self.mergeTuples(r, (code, arguments, callOnGet))
21556 def has(self):
21558 bool has(key);
21560 Check if an entry exists in the backing object. Returns true if value
21561 exists in backing object, false otherwise.
21563 return self.mergeTuples(self.appendKeyArgConversion(), self.appendBoolResult())
21565 def keys(self):
21567 object keys();
21569 Returns new object iterator with all keys from backing object.
21571 return self.appendIteratorResult()
21573 def values(self):
21575 object values();
21577 Returns new object iterator with all values from backing object.
21579 return self.appendIteratorResult()
21581 def entries(self):
21583 object entries();
21585 Returns new object iterator with all keys and values from backing
21586 object. Keys will be null for set.
21588 return self.appendIteratorResult()
21590 def clear(self):
21592 void clear();
21594 Removes all entries from map/set.
21596 return ([], [], [])
21598 def delete(self):
21600 bool delete(key);
21602 Deletes an entry from the backing object. Returns true if value existed
21603 in backing object, false otherwise.
21605 return self.mergeTuples(self.appendKeyArgConversion(), self.appendBoolResult())
21607 def define(self):
21608 return self.cgRoot.define()
21611 class CGMaplikeOrSetlikeHelperFunctionGenerator(CallbackMember):
21613 Generates code to allow C++ to perform operations on backing objects. Gets
21614 a context from the binding wrapper, turns arguments into JS::Values (via
21615 CallbackMember/CGNativeMember argument conversion), then uses
21616 CGMaplikeOrSetlikeMethodGenerator to generate the body.
21620 class HelperFunction(CGAbstractMethod):
21622 Generates context retrieval code and rooted JSObject for interface for
21623 CGMaplikeOrSetlikeMethodGenerator to use
21626 def __init__(self, descriptor, name, args, code, returnType):
21627 self.code = code
21628 CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
21630 def definition_body(self):
21631 return self.code
21633 def __init__(
21634 self,
21635 descriptor,
21636 maplikeOrSetlike,
21637 name,
21638 needsKeyArg=False,
21639 needsValueArg=False,
21640 needsValueTypeReturn=False,
21641 needsBoolReturn=False,
21643 assert not (needsValueTypeReturn and needsBoolReturn)
21644 args = []
21645 self.maplikeOrSetlike = maplikeOrSetlike
21646 self.needsBoolReturn = needsBoolReturn
21647 self.needsValueTypeReturn = needsValueTypeReturn
21649 returnType = (
21650 BuiltinTypes[IDLBuiltinType.Types.void]
21651 if not self.needsValueTypeReturn
21652 else maplikeOrSetlike.valueType
21655 if needsKeyArg:
21656 args.append(FakeArgument(maplikeOrSetlike.keyType, None, "aKey"))
21657 if needsValueArg:
21658 assert needsKeyArg
21659 assert not needsValueTypeReturn
21660 args.append(FakeArgument(maplikeOrSetlike.valueType, None, "aValue"))
21661 # Run CallbackMember init function to generate argument conversion code.
21662 # wrapScope is set to 'obj' when generating maplike or setlike helper
21663 # functions, as we don't have access to the CallbackPreserveColor
21664 # method.
21665 CallbackMember.__init__(
21666 self,
21667 [returnType, args],
21668 name,
21669 descriptor,
21670 False,
21671 wrapScope="obj",
21672 passJSBitsAsNeeded=self.handleJSObjectGetHelper(),
21675 if self.needsValueTypeReturn:
21676 finalReturnType = self.returnType
21677 elif needsBoolReturn:
21678 finalReturnType = "bool"
21679 else:
21680 finalReturnType = "void"
21681 # Wrap CallbackMember body code into a CGAbstractMethod to make
21682 # generation easier.
21683 self.implMethod = CGMaplikeOrSetlikeHelperFunctionGenerator.HelperFunction(
21684 descriptor, name, self.args, self.body, finalReturnType
21687 def getCallSetup(self):
21688 # If handleJSObjectGetHelper is true, it means the caller will provide a JSContext,
21689 # so we don't need to create JSContext and enter UnprivilegedJunkScopeOrWorkerGlobal here.
21690 code = "MOZ_ASSERT(self);\n"
21691 if not self.handleJSObjectGetHelper():
21692 code += dedent(
21694 AutoJSAPI jsapi;
21695 jsapi.Init();
21696 JSContext* cx = jsapi.cx();
21697 // It's safe to use UnprivilegedJunkScopeOrWorkerGlobal here because
21698 // all we want is to wrap into _some_ scope and then unwrap to find
21699 // the reflector, and wrapping has no side-effects.
21700 JSObject* scope = UnprivilegedJunkScopeOrWorkerGlobal(fallible);
21701 if (!scope) {
21702 aRv.Throw(NS_ERROR_UNEXPECTED);
21703 return%s;
21705 JSAutoRealm tempRealm(cx, scope);
21707 % self.getDefaultRetval()
21710 code += dedent(
21712 JS::Rooted<JS::Value> v(cx);
21713 if(!ToJSValue(cx, self, &v)) {
21714 aRv.Throw(NS_ERROR_UNEXPECTED);
21715 return%s;
21717 // This is a reflector, but due to trying to name things
21718 // similarly across method generators, it's called obj here.
21719 JS::Rooted<JSObject*> obj(cx);
21720 obj = js::UncheckedUnwrap(&v.toObject(), /* stopAtWindowProxy = */ false);
21722 % self.getDefaultRetval()
21725 # For the JSObject Get method, we'd like wrap the inner code in a scope such that
21726 # the code can use the same realm. So here we are creating the result variable
21727 # outside of the scope.
21728 if self.handleJSObjectGetHelper():
21729 code += dedent(
21731 JS::Rooted<JS::Value> result(cx);
21734 else:
21735 code += dedent(
21737 JSAutoRealm reflectorRealm(cx, obj);
21740 return code
21742 def getArgs(self, returnType, argList):
21743 # We don't need the context or the value. We'll generate those instead.
21744 args = CGNativeMember.getArgs(self, returnType, argList)
21745 # Prepend a pointer to the binding object onto the arguments
21746 return [Argument(self.descriptorProvider.nativeType + "*", "self")] + args
21748 def needsScopeBody(self):
21749 return self.handleJSObjectGetHelper()
21751 def getArgvDeclFailureCode(self):
21752 return "aRv.Throw(NS_ERROR_UNEXPECTED);\n"
21754 def handleJSObjectGetHelper(self):
21755 return self.needsValueTypeReturn and self.maplikeOrSetlike.valueType.isObject()
21757 def getResultConversion(self):
21758 if self.needsBoolReturn:
21759 return "return aRetVal;\n"
21760 elif self.needsValueTypeReturn:
21761 code = ""
21762 if self.handleJSObjectGetHelper():
21763 code = dedent(
21765 if (!JS_WrapValue(cx, &result)) {
21766 aRv.NoteJSContextException(cx);
21767 return;
21772 failureCode = dedent("aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n")
21774 exceptionCode = None
21775 if self.maplikeOrSetlike.valueType.isPrimitive():
21776 exceptionCode = dedent(
21777 "aRv.NoteJSContextException(cx);\nreturn%s;\n"
21778 % self.getDefaultRetval()
21781 return code + CallbackMember.getResultConversion(
21782 self,
21783 "result",
21784 failureCode=failureCode,
21785 isDefinitelyObject=True,
21786 exceptionCode=exceptionCode,
21788 return "return;\n"
21790 def getRvalDecl(self):
21791 if self.needsBoolReturn:
21792 return "bool aRetVal;\n"
21793 elif self.handleJSObjectGetHelper():
21794 return "JSAutoRealm reflectorRealm(cx, obj);\n"
21795 return ""
21797 def getArgcDecl(self):
21798 # Don't need argc for anything.
21799 return None
21801 def getDefaultRetval(self):
21802 if self.needsBoolReturn:
21803 return " false"
21804 elif self.needsValueTypeReturn:
21805 return CallbackMember.getDefaultRetval(self)
21806 return ""
21808 def getCall(self):
21809 return CGMaplikeOrSetlikeMethodGenerator(
21810 self.descriptorProvider,
21811 self.maplikeOrSetlike,
21812 self.name.lower(),
21813 self.needsValueTypeReturn,
21814 helperImpl=self,
21815 ).define()
21817 def getPrettyName(self):
21818 return self.name
21820 def declare(self):
21821 return self.implMethod.declare()
21823 def define(self):
21824 return self.implMethod.define()
21827 class CGMaplikeOrSetlikeHelperGenerator(CGNamespace):
21829 Declares and defines convenience methods for accessing backing objects on
21830 setlike/maplike interface. Generates function signatures, un/packs
21831 backing objects from slot, etc.
21834 def __init__(self, descriptor, maplikeOrSetlike):
21835 self.descriptor = descriptor
21836 # Since iterables are folded in with maplike/setlike, make sure we've
21837 # got the right type here.
21838 assert maplikeOrSetlike.isMaplike() or maplikeOrSetlike.isSetlike()
21839 self.maplikeOrSetlike = maplikeOrSetlike
21840 self.namespace = "%sHelpers" % (
21841 self.maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title()
21843 self.helpers = [
21844 CGMaplikeOrSetlikeHelperFunctionGenerator(
21845 descriptor, maplikeOrSetlike, "Clear"
21847 CGMaplikeOrSetlikeHelperFunctionGenerator(
21848 descriptor,
21849 maplikeOrSetlike,
21850 "Delete",
21851 needsKeyArg=True,
21852 needsBoolReturn=True,
21854 CGMaplikeOrSetlikeHelperFunctionGenerator(
21855 descriptor,
21856 maplikeOrSetlike,
21857 "Has",
21858 needsKeyArg=True,
21859 needsBoolReturn=True,
21862 if self.maplikeOrSetlike.isMaplike():
21863 self.helpers.append(
21864 CGMaplikeOrSetlikeHelperFunctionGenerator(
21865 descriptor,
21866 maplikeOrSetlike,
21867 "Set",
21868 needsKeyArg=True,
21869 needsValueArg=True,
21872 self.helpers.append(
21873 CGMaplikeOrSetlikeHelperFunctionGenerator(
21874 descriptor,
21875 maplikeOrSetlike,
21876 "Get",
21877 needsKeyArg=True,
21878 needsValueTypeReturn=True,
21881 else:
21882 assert self.maplikeOrSetlike.isSetlike()
21883 self.helpers.append(
21884 CGMaplikeOrSetlikeHelperFunctionGenerator(
21885 descriptor, maplikeOrSetlike, "Add", needsKeyArg=True
21888 CGNamespace.__init__(self, self.namespace, CGList(self.helpers))
21891 class CGIterableMethodGenerator(CGGeneric):
21893 Creates methods for iterable interfaces. Unwrapping/wrapping
21894 will be taken care of by the usual method generation machinery in
21895 CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
21896 using CGCallGenerator.
21899 def __init__(self, descriptor, iterable, methodName):
21900 if methodName == "forEach":
21901 CGGeneric.__init__(
21902 self,
21903 fill(
21905 if (!JS::IsCallable(arg0)) {
21906 cx.ThrowErrorMessage<MSG_NOT_CALLABLE>("Argument 1");
21907 return false;
21909 JS::RootedValueArray<3> callArgs(cx);
21910 callArgs[2].setObject(*obj);
21911 JS::Rooted<JS::Value> ignoredReturnVal(cx);
21912 auto GetKeyAtIndex = &${selfType}::GetKeyAtIndex;
21913 auto GetValueAtIndex = &${selfType}::GetValueAtIndex;
21914 for (size_t i = 0; i < self->GetIterableLength(); ++i) {
21915 if (!CallIterableGetter(cx, GetValueAtIndex, self, i,
21916 callArgs[0])) {
21917 return false;
21919 if (!CallIterableGetter(cx, GetKeyAtIndex, self, i,
21920 callArgs[1])) {
21921 return false;
21923 if (!JS::Call(cx, arg1, arg0, JS::HandleValueArray(callArgs),
21924 &ignoredReturnVal)) {
21925 return false;
21928 """,
21929 ifaceName=descriptor.interface.identifier.name,
21930 selfType=descriptor.nativeType,
21933 return
21934 CGGeneric.__init__(
21935 self,
21936 fill(
21938 typedef ${iterClass} itrType;
21939 RefPtr<itrType> result(new itrType(self,
21940 itrType::IterableIteratorType::${itrMethod},
21941 &${ifaceName}Iterator_Binding::Wrap));
21942 """,
21943 iterClass=iteratorNativeType(descriptor),
21944 ifaceName=descriptor.interface.identifier.name,
21945 itrMethod=methodName.title(),
21950 class GlobalGenRoots:
21952 Roots for global codegen.
21954 To generate code, call the method associated with the target, and then
21955 call the appropriate define/declare method.
21958 @staticmethod
21959 def GeneratedAtomList(config):
21960 # Atom enum
21961 dictionaries = config.dictionaries
21963 structs = []
21965 def memberToAtomCacheMember(binaryNameFor, m):
21966 binaryMemberName = binaryNameFor(m.identifier.name)
21967 return ClassMember(
21968 CGDictionary.makeIdName(binaryMemberName),
21969 "PinnedStringId",
21970 visibility="public",
21973 def buildAtomCacheStructure(idlobj, binaryNameFor, members):
21974 classMembers = [memberToAtomCacheMember(binaryNameFor, m) for m in members]
21975 structName = idlobj.identifier.name + "Atoms"
21976 return (
21977 structName,
21978 CGWrapper(
21979 CGClass(
21980 structName, bases=None, isStruct=True, members=classMembers
21982 post="\n",
21986 for dict in dictionaries:
21987 if len(dict.members) == 0:
21988 continue
21990 structs.append(buildAtomCacheStructure(dict, lambda x: x, dict.members))
21992 for d in config.getDescriptors(isJSImplemented=True) + config.getDescriptors(
21993 isCallback=True
21995 members = [m for m in d.interface.members if m.isAttr() or m.isMethod()]
21996 if d.interface.isJSImplemented() and d.interface.ctor():
21997 # We'll have an __init() method.
21998 members.append(FakeMember("__init"))
21999 if (
22000 d.interface.isJSImplemented()
22001 and d.interface.maplikeOrSetlikeOrIterable
22002 and d.interface.maplikeOrSetlikeOrIterable.isMaplike()
22004 # We'll have an __onget() method.
22005 members.append(FakeMember("__onget"))
22006 if d.interface.isJSImplemented() and d.interface.getExtendedAttribute(
22007 "WantsEventListenerHooks"
22009 members.append(FakeMember("eventListenerAdded"))
22010 members.append(FakeMember("eventListenerRemoved"))
22011 if len(members) == 0:
22012 continue
22014 structs.append(
22015 buildAtomCacheStructure(
22016 d.interface, lambda x: d.binaryNameFor(x), members
22020 structs.sort()
22021 generatedStructs = [struct for structName, struct in structs]
22022 structNames = [structName for structName, struct in structs]
22024 mainStruct = CGWrapper(
22025 CGClass(
22026 "PerThreadAtomCache",
22027 bases=[ClassBase(structName) for structName in structNames],
22028 isStruct=True,
22030 post="\n",
22033 structs = CGList(generatedStructs + [mainStruct])
22035 # Wrap all of that in our namespaces.
22036 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(structs, pre="\n"))
22037 curr = CGWrapper(curr, post="\n")
22039 # Add include statement for PinnedStringId.
22040 declareIncludes = ["mozilla/dom/PinnedStringId.h"]
22041 curr = CGHeaders([], [], [], [], declareIncludes, [], "GeneratedAtomList", curr)
22043 # Add include guards.
22044 curr = CGIncludeGuard("GeneratedAtomList", curr)
22046 # Add the auto-generated comment.
22047 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
22049 # Done.
22050 return curr
22052 @staticmethod
22053 def GeneratedEventList(config):
22054 eventList = CGList([])
22055 for generatedEvent in config.generatedEvents:
22056 eventList.append(
22057 CGGeneric(declare=("GENERATED_EVENT(%s)\n" % generatedEvent))
22059 return eventList
22061 @staticmethod
22062 def PrototypeList(config):
22064 # Prototype ID enum.
22065 descriptorsWithPrototype = config.getDescriptors(
22066 hasInterfacePrototypeObject=True
22068 protos = [d.name for d in descriptorsWithPrototype]
22069 idEnum = CGNamespacedEnum("id", "ID", ["_ID_Start"] + protos, [0, "_ID_Start"])
22070 idEnum = CGList([idEnum])
22072 def fieldSizeAssert(amount, jitInfoField, message):
22073 maxFieldValue = (
22074 "(uint64_t(1) << (sizeof(std::declval<JSJitInfo>().%s) * 8))"
22075 % jitInfoField
22077 return CGGeneric(
22078 define='static_assert(%s < %s, "%s");\n\n'
22079 % (amount, maxFieldValue, message)
22082 idEnum.append(
22083 fieldSizeAssert("id::_ID_Count", "protoID", "Too many prototypes!")
22086 # Wrap all of that in our namespaces.
22087 idEnum = CGNamespace.build(
22088 ["mozilla", "dom", "prototypes"], CGWrapper(idEnum, pre="\n")
22090 idEnum = CGWrapper(idEnum, post="\n")
22092 curr = CGList(
22094 CGGeneric(define="#include <stdint.h>\n"),
22095 CGGeneric(define="#include <type_traits>\n\n"),
22096 CGGeneric(define='#include "js/experimental/JitInfo.h"\n\n'),
22097 CGGeneric(define='#include "mozilla/dom/PrototypeList.h"\n\n'),
22098 idEnum,
22102 # Let things know the maximum length of the prototype chain.
22103 maxMacroName = "MAX_PROTOTYPE_CHAIN_LENGTH"
22104 maxMacro = CGGeneric(
22105 declare="#define " + maxMacroName + " " + str(config.maxProtoChainLength)
22107 curr.append(CGWrapper(maxMacro, post="\n\n"))
22108 curr.append(
22109 fieldSizeAssert(
22110 maxMacroName, "depth", "Some inheritance chain is too long!"
22114 # Constructor ID enum.
22115 constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)]
22116 idEnum = CGNamespacedEnum(
22117 "id",
22118 "ID",
22119 ["_ID_Start"] + constructors,
22120 ["prototypes::id::_ID_Count", "_ID_Start"],
22123 # Wrap all of that in our namespaces.
22124 idEnum = CGNamespace.build(
22125 ["mozilla", "dom", "constructors"], CGWrapper(idEnum, pre="\n")
22127 idEnum = CGWrapper(idEnum, post="\n")
22129 curr.append(idEnum)
22131 # Named properties object enum.
22132 namedPropertiesObjects = [
22133 d.name for d in config.getDescriptors(hasNamedPropertiesObject=True)
22135 idEnum = CGNamespacedEnum(
22136 "id",
22137 "ID",
22138 ["_ID_Start"] + namedPropertiesObjects,
22139 ["constructors::id::_ID_Count", "_ID_Start"],
22142 # Wrap all of that in our namespaces.
22143 idEnum = CGNamespace.build(
22144 ["mozilla", "dom", "namedpropertiesobjects"], CGWrapper(idEnum, pre="\n")
22146 idEnum = CGWrapper(idEnum, post="\n")
22148 curr.append(idEnum)
22150 traitsDecls = [
22151 CGGeneric(
22152 declare=dedent(
22154 template <prototypes::ID PrototypeID>
22155 struct PrototypeTraits;
22160 traitsDecls.extend(CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype)
22162 ifaceNamesWithProto = [
22163 d.interface.identifier.name for d in descriptorsWithPrototype
22165 traitsDecls.append(
22166 CGStringTable("NamesOfInterfacesWithProtos", ifaceNamesWithProto)
22169 traitsDecl = CGNamespace.build(["mozilla", "dom"], CGList(traitsDecls))
22171 curr.append(traitsDecl)
22173 # Add include guards.
22174 curr = CGIncludeGuard("PrototypeList", curr)
22176 # Add the auto-generated comment.
22177 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
22179 # Done.
22180 return curr
22182 @staticmethod
22183 def RegisterBindings(config):
22185 curr = CGNamespace.build(["mozilla", "dom"], CGGlobalNames(config))
22186 curr = CGWrapper(curr, post="\n")
22188 # Add the includes
22189 defineIncludes = [
22190 CGHeaders.getDeclarationFilename(desc.interface)
22191 for desc in config.getDescriptors(
22192 hasInterfaceObject=True, isExposedInWindow=True, register=True
22195 defineIncludes.append("mozilla/dom/WebIDLGlobalNameHash.h")
22196 defineIncludes.append("mozilla/dom/PrototypeList.h")
22197 defineIncludes.append("mozilla/PerfectHash.h")
22198 defineIncludes.append("js/String.h")
22199 curr = CGHeaders([], [], [], [], [], defineIncludes, "RegisterBindings", curr)
22201 # Add include guards.
22202 curr = CGIncludeGuard("RegisterBindings", curr)
22204 # Done.
22205 return curr
22207 @staticmethod
22208 def RegisterWorkerBindings(config):
22210 curr = CGRegisterWorkerBindings(config)
22212 # Wrap all of that in our namespaces.
22213 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
22214 curr = CGWrapper(curr, post="\n")
22216 # Add the includes
22217 defineIncludes = [
22218 CGHeaders.getDeclarationFilename(desc.interface)
22219 for desc in config.getDescriptors(
22220 hasInterfaceObject=True, register=True, isExposedInAnyWorker=True
22224 curr = CGHeaders(
22225 [], [], [], [], [], defineIncludes, "RegisterWorkerBindings", curr
22228 # Add include guards.
22229 curr = CGIncludeGuard("RegisterWorkerBindings", curr)
22231 # Done.
22232 return curr
22234 @staticmethod
22235 def RegisterWorkerDebuggerBindings(config):
22237 curr = CGRegisterWorkerDebuggerBindings(config)
22239 # Wrap all of that in our namespaces.
22240 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
22241 curr = CGWrapper(curr, post="\n")
22243 # Add the includes
22244 defineIncludes = [
22245 CGHeaders.getDeclarationFilename(desc.interface)
22246 for desc in config.getDescriptors(
22247 hasInterfaceObject=True, register=True, isExposedInWorkerDebugger=True
22251 curr = CGHeaders(
22252 [], [], [], [], [], defineIncludes, "RegisterWorkerDebuggerBindings", curr
22255 # Add include guards.
22256 curr = CGIncludeGuard("RegisterWorkerDebuggerBindings", curr)
22258 # Done.
22259 return curr
22261 @staticmethod
22262 def RegisterWorkletBindings(config):
22264 curr = CGRegisterWorkletBindings(config)
22266 # Wrap all of that in our namespaces.
22267 curr = CGNamespace.build(["mozilla", "dom"], CGWrapper(curr, post="\n"))
22268 curr = CGWrapper(curr, post="\n")
22270 # Add the includes
22271 defineIncludes = [
22272 CGHeaders.getDeclarationFilename(desc.interface)
22273 for desc in config.getDescriptors(
22274 hasInterfaceObject=True, register=True, isExposedInAnyWorklet=True
22278 curr = CGHeaders(
22279 [], [], [], [], [], defineIncludes, "RegisterWorkletBindings", curr
22282 # Add include guards.
22283 curr = CGIncludeGuard("RegisterWorkletBindings", curr)
22285 # Done.
22286 return curr
22288 @staticmethod
22289 def UnionTypes(config):
22290 unionTypes = UnionsForFile(config, None)
22292 includes,
22293 implincludes,
22294 declarations,
22295 traverseMethods,
22296 unlinkMethods,
22297 unionStructs,
22298 ) = UnionTypes(unionTypes, config)
22300 unions = CGList(
22301 traverseMethods
22302 + unlinkMethods
22303 + [CGUnionStruct(t, config) for t in unionStructs]
22304 + [CGUnionStruct(t, config, True) for t in unionStructs],
22305 "\n",
22308 includes.add("mozilla/OwningNonNull.h")
22309 includes.add("mozilla/dom/UnionMember.h")
22310 includes.add("mozilla/dom/BindingDeclarations.h")
22311 # BindingUtils.h is only needed for SetToObject.
22312 # If it stops being inlined or stops calling CallerSubsumes
22313 # both this bit and the bit in CGBindingRoot can be removed.
22314 includes.add("mozilla/dom/BindingUtils.h")
22316 # Wrap all of that in our namespaces.
22317 curr = CGNamespace.build(["mozilla", "dom"], unions)
22319 curr = CGWrapper(curr, post="\n")
22321 builder = ForwardDeclarationBuilder()
22322 for className, isStruct in declarations:
22323 builder.add(className, isStruct=isStruct)
22325 curr = CGList([builder.build(), curr], "\n")
22327 curr = CGHeaders([], [], [], [], includes, implincludes, "UnionTypes", curr)
22329 # Add include guards.
22330 curr = CGIncludeGuard("UnionTypes", curr)
22332 # Done.
22333 return curr
22335 @staticmethod
22336 def UnionConversions(config):
22337 unionTypes = []
22338 for l in six.itervalues(config.unionsPerFilename):
22339 unionTypes.extend(l)
22340 unionTypes.sort(key=lambda u: u.name)
22341 headers, unions = UnionConversions(unionTypes, config)
22343 # Wrap all of that in our namespaces.
22344 curr = CGNamespace.build(["mozilla", "dom"], unions)
22346 curr = CGWrapper(curr, post="\n")
22348 headers.update(["nsDebug.h", "mozilla/dom/UnionTypes.h"])
22349 curr = CGHeaders([], [], [], [], headers, [], "UnionConversions", curr)
22351 # Add include guards.
22352 curr = CGIncludeGuard("UnionConversions", curr)
22354 # Done.
22355 return curr
22357 @staticmethod
22358 def WebIDLPrefs(config):
22359 prefs = set()
22360 headers = set(["mozilla/dom/WebIDLPrefs.h"])
22361 for d in config.getDescriptors(hasInterfaceOrInterfacePrototypeObject=True):
22362 for m in d.interface.members:
22363 pref = PropertyDefiner.getStringAttr(m, "Pref")
22364 if pref:
22365 headers.add(prefHeader(pref))
22366 prefs.add((pref, prefIdentifier(pref)))
22367 prefs = sorted(prefs)
22368 declare = fill(
22370 enum class WebIDLPrefIndex : uint8_t {
22371 NoPref,
22372 $*{prefs}
22374 typedef bool (*WebIDLPrefFunc)();
22375 extern const WebIDLPrefFunc sWebIDLPrefs[${len}];
22376 """,
22377 prefs=",\n".join(map(lambda p: "// " + p[0] + "\n" + p[1], prefs)) + "\n",
22378 len=len(prefs) + 1,
22380 define = fill(
22382 const WebIDLPrefFunc sWebIDLPrefs[] = {
22383 nullptr,
22384 $*{prefs}
22386 """,
22387 prefs=",\n".join(
22388 map(lambda p: "// " + p[0] + "\nStaticPrefs::" + p[1], prefs)
22390 + "\n",
22392 prefFunctions = CGGeneric(declare=declare, define=define)
22394 # Wrap all of that in our namespaces.
22395 curr = CGNamespace.build(["mozilla", "dom"], prefFunctions)
22397 curr = CGWrapper(curr, post="\n")
22399 curr = CGHeaders([], [], [], [], [], headers, "WebIDLPrefs", curr)
22401 # Add include guards.
22402 curr = CGIncludeGuard("WebIDLPrefs", curr)
22404 # Done.
22405 return curr
22407 @staticmethod
22408 def WebIDLSerializable(config):
22409 # We need a declaration of StructuredCloneTags in the header.
22410 declareIncludes = set(
22412 "mozilla/dom/DOMJSClass.h",
22413 "mozilla/dom/StructuredCloneTags.h",
22414 "js/TypeDecls.h",
22417 defineIncludes = set(
22418 ["mozilla/dom/WebIDLSerializable.h", "mozilla/PerfectHash.h"]
22420 names = list()
22421 for d in config.getDescriptors(isSerializable=True):
22422 names.append(d.name)
22423 defineIncludes.add(CGHeaders.getDeclarationFilename(d.interface))
22425 if len(names) == 0:
22426 # We can't really create a PerfectHash out of this, but also there's
22427 # not much point to this file if we have no [Serializable] objects.
22428 # Just spit out an empty file.
22429 return CGIncludeGuard("WebIDLSerializable", CGGeneric(""))
22431 # If we had a lot of serializable things, it might be worth it to use a
22432 # PerfectHash here, or an array ordered by sctag value and binary
22433 # search. But setting those up would require knowing in this python
22434 # code the values of the various SCTAG_DOM_*. We could hardcode them
22435 # here and add static asserts that the values are right, or switch to
22436 # code-generating StructuredCloneTags.h or something. But in practice,
22437 # there's a pretty small number of serializable interfaces, and just
22438 # doing a linear walk is fine. It's not obviously worse than the
22439 # if-cascade we used to have. Let's just make sure we notice if we do
22440 # end up with a lot of serializable things here.
22442 # Also, in practice it looks like compilers compile this linear walk to
22443 # an out-of-bounds check followed by a direct index into an array, by
22444 # basically making a second copy of this array ordered by tag, with the
22445 # holes filled in. Again, worth checking whether this still happens if
22446 # we have too many serializable things.
22447 if len(names) > 20:
22448 raise TypeError(
22449 "We now have %s serializable interfaces. "
22450 "Double-check that the compiler is still "
22451 "generating a jump table." % len(names)
22454 entries = list()
22455 # Make sure we have stable ordering.
22456 for name in sorted(names):
22457 # Strip off trailing newline to make our formatting look right.
22458 entries.append(
22459 fill(
22462 /* mTag */ ${tag},
22463 /* mDeserialize */ ${name}_Binding::Deserialize
22465 """,
22466 tag=StructuredCloneTag(name),
22467 name=name,
22468 )[:-1]
22471 declare = dedent(
22473 WebIDLDeserializer LookupDeserializer(StructuredCloneTags aTag);
22476 define = fill(
22478 struct WebIDLSerializableEntry {
22479 StructuredCloneTags mTag;
22480 WebIDLDeserializer mDeserialize;
22483 static const WebIDLSerializableEntry sEntries[] = {
22484 $*{entries}
22487 WebIDLDeserializer LookupDeserializer(StructuredCloneTags aTag) {
22488 for (auto& entry : sEntries) {
22489 if (entry.mTag == aTag) {
22490 return entry.mDeserialize;
22493 return nullptr;
22495 """,
22496 entries=",\n".join(entries) + "\n",
22499 code = CGGeneric(declare=declare, define=define)
22501 # Wrap all of that in our namespaces.
22502 curr = CGNamespace.build(["mozilla", "dom"], code)
22504 curr = CGWrapper(curr, post="\n")
22506 curr = CGHeaders(
22507 [], [], [], [], declareIncludes, defineIncludes, "WebIDLSerializable", curr
22510 # Add include guards.
22511 curr = CGIncludeGuard("WebIDLSerializable", curr)
22513 # Done.
22514 return curr
22517 # Code generator for simple events
22518 class CGEventGetter(CGNativeMember):
22519 def __init__(self, descriptor, attr):
22520 ea = descriptor.getExtendedAttributes(attr, getter=True)
22521 CGNativeMember.__init__(
22522 self,
22523 descriptor,
22524 attr,
22525 CGSpecializedGetter.makeNativeName(descriptor, attr),
22526 (attr.type, []),
22528 resultNotAddRefed=not attr.type.isSequence(),
22530 self.body = self.getMethodBody()
22532 def getArgs(self, returnType, argList):
22533 if "needsErrorResult" in self.extendedAttrs:
22534 raise TypeError("Event code generator does not support [Throws]!")
22535 if "canOOM" in self.extendedAttrs:
22536 raise TypeError("Event code generator does not support [CanOOM]!")
22537 if not self.member.isAttr():
22538 raise TypeError("Event code generator does not support methods")
22539 if self.member.isStatic():
22540 raise TypeError("Event code generators does not support static attributes")
22541 return CGNativeMember.getArgs(self, returnType, argList)
22543 def getMethodBody(self):
22544 type = self.member.type
22545 memberName = CGDictionary.makeMemberName(self.member.identifier.name)
22546 if (
22547 (type.isPrimitive() and type.tag() in builtinNames)
22548 or type.isEnum()
22549 or type.isPromise()
22550 or type.isGeckoInterface()
22552 return "return " + memberName + ";\n"
22553 if type.isJSString():
22554 # https://bugzilla.mozilla.org/show_bug.cgi?id=1580167
22555 raise TypeError("JSString not supported as member of a generated event")
22556 if (
22557 type.isDOMString()
22558 or type.isByteString()
22559 or type.isUSVString()
22560 or type.isUTF8String()
22562 return "aRetVal = " + memberName + ";\n"
22563 if type.isSpiderMonkeyInterface() or type.isObject():
22564 return fill(
22566 if (${memberName}) {
22567 JS::ExposeObjectToActiveJS(${memberName});
22569 aRetVal.set(${memberName});
22570 return;
22571 """,
22572 memberName=memberName,
22574 if type.isAny():
22575 return fill(
22577 ${selfName}(aRetVal);
22578 """,
22579 selfName=self.name,
22581 if type.isUnion():
22582 return "aRetVal = " + memberName + ";\n"
22583 if type.isSequence():
22584 if type.nullable():
22585 return (
22586 "if ("
22587 + memberName
22588 + ".IsNull()) { aRetVal.SetNull(); } else { aRetVal.SetValue("
22589 + memberName
22590 + ".Value().Clone()); }\n"
22592 else:
22593 return "aRetVal = " + memberName + ".Clone();\n"
22594 raise TypeError("Event code generator does not support this type!")
22596 def declare(self, cgClass):
22597 if (
22598 getattr(self.member, "originatingInterface", cgClass.descriptor.interface)
22599 != cgClass.descriptor.interface
22601 return ""
22602 return CGNativeMember.declare(self, cgClass)
22604 def define(self, cgClass):
22605 if (
22606 getattr(self.member, "originatingInterface", cgClass.descriptor.interface)
22607 != cgClass.descriptor.interface
22609 return ""
22610 return CGNativeMember.define(self, cgClass)
22613 class CGEventSetter(CGNativeMember):
22614 def __init__(self):
22615 raise TypeError("Event code generator does not support setters!")
22618 class CGEventMethod(CGNativeMember):
22619 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
22620 self.isInit = False
22622 CGNativeMember.__init__(
22623 self,
22624 descriptor,
22625 method,
22626 CGSpecializedMethod.makeNativeName(descriptor, method),
22627 signature,
22628 descriptor.getExtendedAttributes(method),
22629 breakAfter=breakAfter,
22630 variadicIsSequence=True,
22632 self.originalArgs = list(self.args)
22634 iface = descriptor.interface
22635 allowed = isConstructor
22636 if not allowed and iface.getExtendedAttribute("LegacyEventInit"):
22637 # Allow it, only if it fits the initFooEvent profile exactly
22638 # We could check the arg types but it's not worth the effort.
22639 if (
22640 method.identifier.name == "init" + iface.identifier.name
22641 and signature[1][0].type.isDOMString()
22642 and signature[1][1].type.isBoolean()
22643 and signature[1][2].type.isBoolean()
22645 # -3 on the left to ignore the type, bubbles, and cancelable parameters
22646 # -1 on the right to ignore the .trusted property which bleeds through
22647 # here because it is [Unforgeable].
22648 len(signature[1]) - 3
22649 == len([x for x in iface.members if x.isAttr()]) - 1
22651 allowed = True
22652 self.isInit = True
22654 if not allowed:
22655 raise TypeError("Event code generator does not support methods!")
22657 def getArgs(self, returnType, argList):
22658 args = [self.getArg(arg) for arg in argList]
22659 return args
22661 def getArg(self, arg):
22662 decl, ref = self.getArgType(
22663 arg.type, arg.canHaveMissingValue(), "Variadic" if arg.variadic else False
22665 if ref:
22666 decl = CGWrapper(decl, pre="const ", post="&")
22668 name = arg.identifier.name
22669 name = "a" + name[0].upper() + name[1:]
22670 return Argument(decl.define(), name)
22672 def declare(self, cgClass):
22673 if self.isInit:
22674 constructorForNativeCaller = ""
22675 else:
22676 self.args = list(self.originalArgs)
22677 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
22678 constructorForNativeCaller = CGNativeMember.declare(self, cgClass)
22680 self.args = list(self.originalArgs)
22681 if needCx(None, self.arguments(), [], considerTypes=True, static=True):
22682 self.args.insert(0, Argument("JSContext*", "aCx"))
22683 if not self.isInit:
22684 self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
22686 return constructorForNativeCaller + CGNativeMember.declare(self, cgClass)
22688 def defineInit(self, cgClass):
22689 iface = self.descriptorProvider.interface
22690 members = ""
22691 while iface.identifier.name != "Event":
22692 i = 3 # Skip the boilerplate args: type, bubble,s cancelable.
22693 for m in iface.members:
22694 if m.isAttr():
22695 # We need to initialize all the member variables that do
22696 # not come from Event.
22697 if (
22698 getattr(m, "originatingInterface", iface).identifier.name
22699 == "Event"
22701 continue
22702 name = CGDictionary.makeMemberName(m.identifier.name)
22703 members += "%s = %s;\n" % (name, self.args[i].name)
22704 i += 1
22705 iface = iface.parent
22707 self.body = fill(
22709 InitEvent(${typeArg}, ${bubblesArg}, ${cancelableArg});
22710 ${members}
22711 """,
22712 typeArg=self.args[0].name,
22713 bubblesArg=self.args[1].name,
22714 cancelableArg=self.args[2].name,
22715 members=members,
22718 return CGNativeMember.define(self, cgClass)
22720 def define(self, cgClass):
22721 self.args = list(self.originalArgs)
22722 if self.isInit:
22723 return self.defineInit(cgClass)
22724 members = ""
22725 holdJS = ""
22726 iface = self.descriptorProvider.interface
22727 while iface.identifier.name != "Event":
22728 for m in self.descriptorProvider.getDescriptor(
22729 iface.identifier.name
22730 ).interface.members:
22731 if m.isAttr():
22732 # We initialize all the other member variables in the
22733 # Constructor except those ones coming from the Event.
22734 if (
22735 getattr(
22736 m, "originatingInterface", cgClass.descriptor.interface
22737 ).identifier.name
22738 == "Event"
22740 continue
22741 name = CGDictionary.makeMemberName(m.identifier.name)
22742 if m.type.isSequence():
22743 # For sequences we may not be able to do a simple
22744 # assignment because the underlying types may not match.
22745 # For example, the argument can be a
22746 # Sequence<OwningNonNull<SomeInterface>> while our
22747 # member is an nsTArray<RefPtr<SomeInterface>>. So
22748 # use AppendElements, which is actually a template on
22749 # the incoming type on nsTArray and does the right thing
22750 # for this case.
22751 target = name
22752 source = "%s.%s" % (self.args[1].name, name)
22753 sequenceCopy = "e->%s.AppendElements(%s);\n"
22754 if m.type.nullable():
22755 sequenceCopy = CGIfWrapper(
22756 CGGeneric(sequenceCopy), "!%s.IsNull()" % source
22757 ).define()
22758 target += ".SetValue()"
22759 source += ".Value()"
22760 members += sequenceCopy % (target, source)
22761 elif m.type.isSpiderMonkeyInterface():
22762 srcname = "%s.%s" % (self.args[1].name, name)
22763 if m.type.nullable():
22764 members += fill(
22766 if (${srcname}.IsNull()) {
22767 e->${varname} = nullptr;
22768 } else {
22769 e->${varname} = ${srcname}.Value().Obj();
22771 """,
22772 varname=name,
22773 srcname=srcname,
22775 else:
22776 members += fill(
22778 e->${varname}.set(${srcname}.Obj());
22779 """,
22780 varname=name,
22781 srcname=srcname,
22783 else:
22784 members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name)
22785 if (
22786 m.type.isAny()
22787 or m.type.isObject()
22788 or m.type.isSpiderMonkeyInterface()
22790 holdJS = "mozilla::HoldJSObjects(e.get());\n"
22791 iface = iface.parent
22793 self.body = fill(
22795 RefPtr<${nativeType}> e = new ${nativeType}(aOwner);
22796 bool trusted = e->Init(aOwner);
22797 e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable);
22798 $*{members}
22799 e->SetTrusted(trusted);
22800 e->SetComposed(${eventInit}.mComposed);
22801 $*{holdJS}
22802 return e.forget();
22803 """,
22804 nativeType=self.descriptorProvider.nativeType.split("::")[-1],
22805 eventType=self.args[0].name,
22806 eventInit=self.args[1].name,
22807 members=members,
22808 holdJS=holdJS,
22811 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
22812 constructorForNativeCaller = CGNativeMember.define(self, cgClass) + "\n"
22813 self.args = list(self.originalArgs)
22814 self.body = fill(
22816 nsCOMPtr<mozilla::dom::EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
22817 return Constructor(owner, ${arg0}, ${arg1});
22818 """,
22819 arg0=self.args[0].name,
22820 arg1=self.args[1].name,
22822 if needCx(None, self.arguments(), [], considerTypes=True, static=True):
22823 self.args.insert(0, Argument("JSContext*", "aCx"))
22824 self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
22825 return constructorForNativeCaller + CGNativeMember.define(self, cgClass)
22828 class CGEventClass(CGBindingImplClass):
22830 Codegen for the actual Event class implementation for this descriptor
22833 def __init__(self, descriptor):
22834 CGBindingImplClass.__init__(
22835 self,
22836 descriptor,
22837 CGEventMethod,
22838 CGEventGetter,
22839 CGEventSetter,
22840 False,
22841 "WrapObjectInternal",
22843 members = []
22844 extraMethods = []
22845 self.membersNeedingCC = []
22846 self.membersNeedingTrace = []
22848 for m in descriptor.interface.members:
22849 if (
22850 getattr(m, "originatingInterface", descriptor.interface)
22851 != descriptor.interface
22853 continue
22855 if m.isAttr():
22856 if m.type.isAny():
22857 self.membersNeedingTrace.append(m)
22858 # Add a getter that doesn't need a JSContext. Note that we
22859 # don't need to do this if our originating interface is not
22860 # the descriptor's interface, because in that case we
22861 # wouldn't generate the getter that _does_ need a JSContext
22862 # either.
22863 extraMethods.append(
22864 ClassMethod(
22865 CGSpecializedGetter.makeNativeName(descriptor, m),
22866 "void",
22867 [Argument("JS::MutableHandle<JS::Value>", "aRetVal")],
22868 const=True,
22869 body=fill(
22871 JS::ExposeValueToActiveJS(${memberName});
22872 aRetVal.set(${memberName});
22873 """,
22874 memberName=CGDictionary.makeMemberName(
22875 m.identifier.name
22880 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
22881 self.membersNeedingTrace.append(m)
22882 elif typeNeedsRooting(m.type):
22883 raise TypeError(
22884 "Need to implement tracing for event member of type %s" % m.type
22886 elif idlTypeNeedsCycleCollection(m.type):
22887 self.membersNeedingCC.append(m)
22889 nativeType = self.getNativeTypeForIDLType(m.type).define()
22890 members.append(
22891 ClassMember(
22892 CGDictionary.makeMemberName(m.identifier.name),
22893 nativeType,
22894 visibility="private",
22895 body="body",
22899 parent = self.descriptor.interface.parent
22900 self.parentType = self.descriptor.getDescriptor(
22901 parent.identifier.name
22902 ).nativeType.split("::")[-1]
22903 self.nativeType = self.descriptor.nativeType.split("::")[-1]
22905 if self.needCC():
22906 isupportsDecl = fill(
22908 NS_DECL_ISUPPORTS_INHERITED
22909 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(${nativeType}, ${parentType})
22910 """,
22911 nativeType=self.nativeType,
22912 parentType=self.parentType,
22914 else:
22915 isupportsDecl = fill(
22917 NS_INLINE_DECL_REFCOUNTING_INHERITED(${nativeType}, ${parentType})
22918 """,
22919 nativeType=self.nativeType,
22920 parentType=self.parentType,
22923 baseDeclarations = fill(
22925 public:
22926 $*{isupportsDecl}
22928 protected:
22929 virtual ~${nativeType}();
22930 explicit ${nativeType}(mozilla::dom::EventTarget* aOwner);
22932 """,
22933 isupportsDecl=isupportsDecl,
22934 nativeType=self.nativeType,
22935 parentType=self.parentType,
22938 className = self.nativeType
22939 asConcreteTypeMethod = ClassMethod(
22940 "As%s" % className,
22941 "%s*" % className,
22943 virtual=True,
22944 body="return this;\n",
22945 breakAfterReturnDecl=" ",
22946 override=True,
22948 extraMethods.append(asConcreteTypeMethod)
22950 CGClass.__init__(
22951 self,
22952 className,
22953 bases=[ClassBase(self.parentType)],
22954 methods=extraMethods + self.methodDecls,
22955 members=members,
22956 extradeclarations=baseDeclarations,
22959 def getWrapObjectBody(self):
22960 return (
22961 "return %s_Binding::Wrap(aCx, this, aGivenProto);\n" % self.descriptor.name
22964 def needCC(self):
22965 return len(self.membersNeedingCC) != 0 or len(self.membersNeedingTrace) != 0
22967 def implTraverse(self):
22968 retVal = ""
22969 for m in self.membersNeedingCC:
22970 retVal += (
22971 " NS_IMPL_CYCLE_COLLECTION_TRAVERSE(%s)\n"
22972 % CGDictionary.makeMemberName(m.identifier.name)
22974 return retVal
22976 def implUnlink(self):
22977 retVal = ""
22978 for m in self.membersNeedingCC:
22979 retVal += (
22980 " NS_IMPL_CYCLE_COLLECTION_UNLINK(%s)\n"
22981 % CGDictionary.makeMemberName(m.identifier.name)
22983 for m in self.membersNeedingTrace:
22984 name = CGDictionary.makeMemberName(m.identifier.name)
22985 if m.type.isAny():
22986 retVal += " tmp->" + name + ".setUndefined();\n"
22987 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
22988 retVal += " tmp->" + name + " = nullptr;\n"
22989 else:
22990 raise TypeError("Unknown traceable member type %s" % m.type)
22991 return retVal
22993 def implTrace(self):
22994 retVal = ""
22995 for m in self.membersNeedingTrace:
22996 retVal += (
22997 " NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(%s)\n"
22998 % CGDictionary.makeMemberName(m.identifier.name)
23000 return retVal
23002 def define(self):
23003 dropJS = ""
23004 for m in self.membersNeedingTrace:
23005 member = CGDictionary.makeMemberName(m.identifier.name)
23006 if m.type.isAny():
23007 dropJS += member + " = JS::UndefinedValue();\n"
23008 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
23009 dropJS += member + " = nullptr;\n"
23010 else:
23011 raise TypeError("Unknown traceable member type %s" % m.type)
23013 if dropJS != "":
23014 dropJS += "mozilla::DropJSObjects(this);\n"
23015 # Just override CGClass and do our own thing
23016 ctorParams = (
23017 "aOwner, nullptr, nullptr" if self.parentType == "Event" else "aOwner"
23020 if self.needCC():
23021 classImpl = fill(
23024 NS_IMPL_CYCLE_COLLECTION_CLASS(${nativeType})
23026 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
23027 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
23029 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(${nativeType}, ${parentType})
23030 $*{traverse}
23031 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
23033 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(${nativeType}, ${parentType})
23034 $*{trace}
23035 NS_IMPL_CYCLE_COLLECTION_TRACE_END
23037 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(${nativeType}, ${parentType})
23038 $*{unlink}
23039 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
23041 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
23042 NS_INTERFACE_MAP_END_INHERITING(${parentType})
23043 """,
23044 nativeType=self.nativeType,
23045 parentType=self.parentType,
23046 traverse=self.implTraverse(),
23047 unlink=self.implUnlink(),
23048 trace=self.implTrace(),
23050 else:
23051 classImpl = ""
23053 classImpl += fill(
23056 ${nativeType}::${nativeType}(mozilla::dom::EventTarget* aOwner)
23057 : ${parentType}(${ctorParams})
23061 ${nativeType}::~${nativeType}()
23063 $*{dropJS}
23066 """,
23067 nativeType=self.nativeType,
23068 ctorParams=ctorParams,
23069 parentType=self.parentType,
23070 dropJS=dropJS,
23073 return classImpl + CGBindingImplClass.define(self)
23075 def getNativeTypeForIDLType(self, type):
23076 if type.isPrimitive() and type.tag() in builtinNames:
23077 nativeType = CGGeneric(builtinNames[type.tag()])
23078 if type.nullable():
23079 nativeType = CGTemplatedType("Nullable", nativeType)
23080 elif type.isEnum():
23081 nativeType = CGGeneric(type.unroll().inner.identifier.name)
23082 if type.nullable():
23083 nativeType = CGTemplatedType("Nullable", nativeType)
23084 elif type.isJSString():
23085 nativeType = CGGeneric("JS::Heap<JSString*>")
23086 elif type.isDOMString() or type.isUSVString():
23087 nativeType = CGGeneric("nsString")
23088 elif type.isByteString() or type.isUTF8String():
23089 nativeType = CGGeneric("nsCString")
23090 elif type.isPromise():
23091 nativeType = CGGeneric("RefPtr<Promise>")
23092 elif type.isGeckoInterface():
23093 iface = type.unroll().inner
23094 nativeType = self.descriptor.getDescriptor(iface.identifier.name).nativeType
23095 # Now trim off unnecessary namespaces
23096 nativeType = nativeType.split("::")
23097 if nativeType[0] == "mozilla":
23098 nativeType.pop(0)
23099 if nativeType[0] == "dom":
23100 nativeType.pop(0)
23101 nativeType = CGWrapper(
23102 CGGeneric("::".join(nativeType)), pre="RefPtr<", post=">"
23104 elif type.isAny():
23105 nativeType = CGGeneric("JS::Heap<JS::Value>")
23106 elif type.isObject() or type.isSpiderMonkeyInterface():
23107 nativeType = CGGeneric("JS::Heap<JSObject*>")
23108 elif type.isUnion():
23109 nativeType = CGGeneric(CGUnionStruct.unionTypeDecl(type, True))
23110 elif type.isSequence():
23111 if type.nullable():
23112 innerType = type.inner.inner
23113 else:
23114 innerType = type.inner
23115 if (
23116 not innerType.isPrimitive()
23117 and not innerType.isEnum()
23118 and not innerType.isDOMString()
23119 and not innerType.isByteString()
23120 and not innerType.isUTF8String()
23121 and not innerType.isPromise()
23122 and not innerType.isGeckoInterface()
23124 raise TypeError(
23125 "Don't know how to properly manage GC/CC for "
23126 "event member of type %s" % type
23128 nativeType = CGTemplatedType(
23129 "nsTArray", self.getNativeTypeForIDLType(innerType)
23131 if type.nullable():
23132 nativeType = CGTemplatedType("Nullable", nativeType)
23133 else:
23134 raise TypeError("Don't know how to declare event member of type %s" % type)
23135 return nativeType
23138 class CGEventRoot(CGThing):
23139 def __init__(self, config, interfaceName):
23140 descriptor = config.getDescriptor(interfaceName)
23142 self.root = CGWrapper(CGEventClass(descriptor), pre="\n", post="\n")
23144 self.root = CGNamespace.build(["mozilla", "dom"], self.root)
23146 self.root = CGList(
23147 [CGClassForwardDeclare("JSContext", isStruct=True), self.root]
23150 parent = descriptor.interface.parent.identifier.name
23152 # Throw in our #includes
23153 self.root = CGHeaders(
23154 [descriptor],
23159 config.getDescriptor(parent).headerFile,
23160 "mozilla/Attributes.h",
23161 "mozilla/dom/%sBinding.h" % interfaceName,
23162 "mozilla/dom/BindingUtils.h",
23165 "%s.h" % interfaceName,
23166 "js/GCAPI.h",
23167 "mozilla/HoldDropJSObjects.h",
23168 "mozilla/dom/Nullable.h",
23171 self.root,
23172 config,
23175 # And now some include guards
23176 self.root = CGIncludeGuard(interfaceName, self.root)
23178 self.root = CGWrapper(
23179 self.root,
23180 pre=(
23181 AUTOGENERATED_WITH_SOURCE_WARNING_COMMENT
23182 % os.path.basename(descriptor.interface.filename())
23186 self.root = CGWrapper(
23187 self.root,
23188 pre=dedent(
23190 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
23191 /* vim:set ts=2 sw=2 sts=2 et cindent: */
23192 /* This Source Code Form is subject to the terms of the Mozilla Public
23193 * License, v. 2.0. If a copy of the MPL was not distributed with this
23194 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
23200 def declare(self):
23201 return self.root.declare()
23203 def define(self):
23204 return self.root.define()