1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "builtin/TestingFunctions.h"
9 #include "mozilla/Atomics.h"
10 #include "mozilla/Casting.h"
11 #include "mozilla/FloatingPoint.h"
12 #ifdef JS_HAS_INTL_API
13 # include "mozilla/intl/ICU4CLibrary.h"
14 # include "mozilla/intl/Locale.h"
15 # include "mozilla/intl/String.h"
16 # include "mozilla/intl/TimeZone.h"
18 #include "mozilla/Maybe.h"
19 #include "mozilla/RefPtr.h"
20 #include "mozilla/ScopeExit.h"
21 #include "mozilla/Span.h"
22 #include "mozilla/Sprintf.h"
23 #include "mozilla/StringBuffer.h"
24 #include "mozilla/TextUtils.h"
25 #include "mozilla/ThreadLocal.h"
34 #include <initializer_list>
38 #if defined(XP_UNIX) && !defined(XP_DARWIN)
46 #include "jsfriendapi.h"
48 #ifdef JS_HAS_INTL_API
49 # include "builtin/intl/CommonFunctions.h"
50 # include "builtin/intl/FormatBuffer.h"
51 # include "builtin/intl/SharedIntlData.h"
53 #include "builtin/BigInt.h"
54 #include "builtin/JSON.h"
55 #include "builtin/MapObject.h"
56 #include "builtin/Promise.h"
57 #include "builtin/TestingUtility.h" // js::ParseCompileOptions, js::ParseDebugMetadata
58 #include "ds/IdValuePair.h" // js::IdValuePair
59 #include "frontend/BytecodeCompiler.h" // frontend::{CompileGlobalScriptToExtensibleStencil,ParseModuleToExtensibleStencil}
60 #include "frontend/CompilationStencil.h" // frontend::CompilationStencil
61 #include "frontend/FrontendContext.h" // AutoReportFrontendContext
63 #include "gc/GCEnum.h"
64 #include "gc/GCLock.h"
66 #include "jit/BaselineJIT.h"
67 #include "jit/Disassemble.h"
68 #include "jit/InlinableNatives.h"
69 #include "jit/Invalidation.h"
71 #include "jit/JitOptions.h"
72 #include "jit/JitRuntime.h"
73 #include "jit/TrialInlining.h"
74 #include "js/Array.h" // JS::NewArrayObject
75 #include "js/ArrayBuffer.h" // JS::{DetachArrayBuffer,GetArrayBufferLengthAndData,NewArrayBufferWithContents}
76 #include "js/CallAndConstruct.h" // JS::Call, JS::IsCallable, JS::IsConstructor, JS_CallFunction
77 #include "js/CharacterEncoding.h"
78 #include "js/CompilationAndEvaluation.h"
79 #include "js/CompileOptions.h"
80 #include "js/Conversions.h"
82 #include "js/experimental/CodeCoverage.h" // js::GetCodeCoverageSummary
83 #include "js/experimental/CompileScript.h" // JS::ParseGlobalScript, JS::PrepareForInstantiate
84 #include "js/experimental/JSStencil.h" // JS::Stencil
85 #include "js/experimental/PCCountProfiling.h" // JS::{Start,Stop}PCCountProfiling, JS::PurgePCCounts, JS::GetPCCountScript{Count,Summary,Contents}
86 #include "js/experimental/TypedData.h" // JS_GetObjectAsUint8Array
87 #include "js/friend/DumpFunctions.h" // js::Dump{Backtrace,Heap,Object}, JS::FormatStackDump, js::IgnoreNurseryObjects
88 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
89 #include "js/friend/WindowProxy.h" // js::ToWindowProxyIfWindow
90 #include "js/GlobalObject.h"
91 #include "js/HashTable.h"
92 #include "js/Interrupt.h"
93 #include "js/LocaleSensitive.h"
95 #include "js/Printf.h"
96 #include "js/PropertyAndElement.h" // JS_DefineProperties, JS_DefineProperty, JS_DefinePropertyById, JS_Enumerate, JS_GetProperty, JS_GetPropertyById, JS_HasProperty, JS_SetElement, JS_SetProperty
97 #include "js/PropertySpec.h"
98 #include "js/SourceText.h"
99 #include "js/StableStringChars.h"
100 #include "js/Stack.h"
101 #include "js/String.h" // JS::GetLinearStringLength, JS::StringToLinearString
102 #include "js/StructuredClone.h"
103 #include "js/UbiNode.h"
104 #include "js/UbiNodeBreadthFirst.h"
105 #include "js/UbiNodeShortestPaths.h"
106 #include "js/UniquePtr.h"
107 #include "js/Vector.h"
108 #include "js/Wrapper.h"
109 #include "threading/CpuCount.h"
110 #include "util/DifferentialTesting.h"
111 #include "util/StringBuilder.h"
112 #include "util/Text.h"
113 #include "vm/BooleanObject.h"
114 #include "vm/DateObject.h"
115 #include "vm/DateTime.h"
116 #include "vm/ErrorObject.h"
117 #include "vm/GlobalObject.h"
118 #include "vm/HelperThreads.h"
119 #include "vm/HelperThreadState.h"
120 #include "vm/Interpreter.h"
121 #include "vm/JSContext.h"
122 #include "vm/JSObject.h"
123 #include "vm/NumberObject.h"
124 #include "vm/PlainObject.h" // js::PlainObject
125 #include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseSlot_*
126 #include "vm/ProxyObject.h"
127 #include "vm/RealmFuses.h"
128 #include "vm/SavedStacks.h"
129 #include "vm/ScopeKind.h"
130 #include "vm/Stack.h"
131 #include "vm/StencilCache.h" // DelazificationCache
132 #include "vm/StencilObject.h" // StencilObject, StencilXDRBufferObject
133 #include "vm/StringObject.h"
134 #include "vm/StringType.h"
135 #include "wasm/AsmJS.h"
136 #include "wasm/WasmBaselineCompile.h"
137 #include "wasm/WasmBuiltinModule.h"
138 #include "wasm/WasmFeatures.h"
139 #include "wasm/WasmGcObject.h"
140 #include "wasm/WasmInstance.h"
141 #include "wasm/WasmIonCompile.h"
142 #include "wasm/WasmJS.h"
143 #include "wasm/WasmModule.h"
144 #include "wasm/WasmValType.h"
145 #include "wasm/WasmValue.h"
147 #include "debugger/DebugAPI-inl.h"
148 #include "vm/Compartment-inl.h"
149 #include "vm/EnvironmentObject-inl.h"
150 #include "vm/JSContext-inl.h"
151 #include "vm/JSObject-inl.h"
152 #include "vm/NativeObject-inl.h"
153 #include "vm/ObjectFlags-inl.h"
154 #include "vm/StringType-inl.h"
155 #include "wasm/WasmInstance-inl.h"
159 using mozilla::AssertedCast
;
160 using mozilla::AsWritableChars
;
161 using mozilla::Maybe
;
164 using JS::AutoStableStringChars
;
165 using JS::CompileOptions
;
166 using JS::SliceBudget
;
167 using JS::SourceText
;
168 using JS::WorkBudget
;
170 // If fuzzingSafe is set, remove functionality that could cause problems with
171 // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
172 mozilla::Atomic
<bool> js::fuzzingSafe(false);
174 // If disableOOMFunctions is set, disable functionality that causes artificial
176 static mozilla::Atomic
<bool> disableOOMFunctions(false);
178 static bool EnvVarIsDefined(const char* name
) {
179 const char* value
= getenv(name
);
180 return value
&& *value
;
183 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
184 static bool EnvVarAsInt(const char* name
, int* valueOut
) {
185 if (!EnvVarIsDefined(name
)) {
189 *valueOut
= atoi(getenv(name
));
194 static bool GetRealmConfiguration(JSContext
* cx
, unsigned argc
, Value
* vp
) {
195 CallArgs args
= CallArgsFromVp(argc
, vp
);
196 RootedObject
callee(cx
, &args
.callee());
197 RootedObject
info(cx
, JS_NewPlainObject(cx
));
201 if (args
.length() > 1) {
202 ReportUsageErrorASCII(cx
, callee
, "Must have zero or one arguments");
205 if (args
.length() == 1 && !args
[0].isString()) {
206 ReportUsageErrorASCII(cx
, callee
, "Argument must be a string");
210 bool importAttributes
= cx
->options().importAttributes();
211 if (!JS_SetProperty(cx
, info
, "importAttributes",
212 importAttributes
? TrueHandleValue
: FalseHandleValue
)) {
216 if (args
.length() == 1) {
217 RootedString
str(cx
, ToString(cx
, args
[0]));
222 if (!JS_StringToId(cx
, str
, &id
)) {
227 if (JS_HasPropertyById(cx
, info
, id
, &hasProperty
) && hasProperty
) {
228 // Returning a true/false from GetProperty
229 return GetProperty(cx
, info
, info
, id
, args
.rval());
232 ReportUsageErrorASCII(cx
, callee
, "Invalid option name");
236 args
.rval().setObject(*info
);
240 static bool GetBuildConfiguration(JSContext
* cx
, unsigned argc
, Value
* vp
) {
241 CallArgs args
= CallArgsFromVp(argc
, vp
);
242 RootedObject
callee(cx
, &args
.callee());
243 RootedObject
info(cx
, JS_NewPlainObject(cx
));
247 if (args
.length() > 1) {
248 ReportUsageErrorASCII(cx
, callee
, "Must have zero or one arguments");
251 if (args
.length() == 1 && !args
[0].isString()) {
252 ReportUsageErrorASCII(cx
, callee
, "Argument must be a string");
256 if (!JS_SetProperty(cx
, info
, "rooting-analysis", FalseHandleValue
)) {
260 if (!JS_SetProperty(cx
, info
, "exact-rooting", TrueHandleValue
)) {
264 if (!JS_SetProperty(cx
, info
, "trace-jscalls-api", FalseHandleValue
)) {
268 if (!JS_SetProperty(cx
, info
, "incremental-gc", TrueHandleValue
)) {
272 if (!JS_SetProperty(cx
, info
, "generational-gc", TrueHandleValue
)) {
276 if (!JS_SetProperty(cx
, info
, "oom-backtraces", FalseHandleValue
)) {
280 RootedValue
value(cx
);
282 value
= BooleanValue(true);
284 value
= BooleanValue(false);
286 if (!JS_SetProperty(cx
, info
, "debug", value
)) {
290 #ifdef RELEASE_OR_BETA
291 value
= BooleanValue(true);
293 value
= BooleanValue(false);
295 if (!JS_SetProperty(cx
, info
, "release_or_beta", value
)) {
299 #ifdef EARLY_BETA_OR_EARLIER
300 value
= BooleanValue(true);
302 value
= BooleanValue(false);
304 if (!JS_SetProperty(cx
, info
, "early_beta_or_earlier", value
)) {
308 #ifdef MOZ_CODE_COVERAGE
309 value
= BooleanValue(true);
311 value
= BooleanValue(false);
313 if (!JS_SetProperty(cx
, info
, "coverage", value
)) {
318 value
= BooleanValue(true);
320 value
= BooleanValue(false);
322 if (!JS_SetProperty(cx
, info
, "has-ctypes", value
)) {
326 #if defined(_M_IX86) || defined(__i386__)
327 value
= BooleanValue(true);
329 value
= BooleanValue(false);
331 if (!JS_SetProperty(cx
, info
, "x86", value
)) {
335 #if defined(_M_X64) || defined(__x86_64__)
336 value
= BooleanValue(true);
338 value
= BooleanValue(false);
340 if (!JS_SetProperty(cx
, info
, "x64", value
)) {
344 #ifdef JS_CODEGEN_ARM
345 value
= BooleanValue(true);
347 value
= BooleanValue(false);
349 if (!JS_SetProperty(cx
, info
, "arm", value
)) {
353 #ifdef JS_SIMULATOR_ARM
354 value
= BooleanValue(true);
356 value
= BooleanValue(false);
358 if (!JS_SetProperty(cx
, info
, "arm-simulator", value
)) {
363 value
= BooleanValue(true);
365 value
= BooleanValue(false);
367 if (!JS_SetProperty(cx
, info
, "android", value
)) {
372 value
= BooleanValue(true);
374 value
= BooleanValue(false);
376 if (!JS_SetProperty(cx
, info
, "windows", value
)) {
381 value
= BooleanValue(true);
383 value
= BooleanValue(false);
385 if (!JS_SetProperty(cx
, info
, "osx", value
)) {
389 #ifdef JS_CODEGEN_ARM64
390 value
= BooleanValue(true);
392 value
= BooleanValue(false);
394 if (!JS_SetProperty(cx
, info
, "arm64", value
)) {
398 #ifdef JS_SIMULATOR_ARM64
399 value
= BooleanValue(true);
401 value
= BooleanValue(false);
403 if (!JS_SetProperty(cx
, info
, "arm64-simulator", value
)) {
407 #ifdef JS_CODEGEN_MIPS32
408 value
= BooleanValue(true);
410 value
= BooleanValue(false);
412 if (!JS_SetProperty(cx
, info
, "mips32", value
)) {
416 #ifdef JS_CODEGEN_MIPS64
417 value
= BooleanValue(true);
419 value
= BooleanValue(false);
421 if (!JS_SetProperty(cx
, info
, "mips64", value
)) {
425 #ifdef JS_SIMULATOR_MIPS32
426 value
= BooleanValue(true);
428 value
= BooleanValue(false);
430 if (!JS_SetProperty(cx
, info
, "mips32-simulator", value
)) {
434 #ifdef JS_SIMULATOR_MIPS64
435 value
= BooleanValue(true);
437 value
= BooleanValue(false);
439 if (!JS_SetProperty(cx
, info
, "mips64-simulator", value
)) {
444 value
= BooleanValue(true);
446 value
= BooleanValue(false);
448 if (!JS_SetProperty(cx
, info
, "simulator", value
)) {
453 value
= BooleanValue(true);
455 value
= BooleanValue(false);
457 if (!JS_SetProperty(cx
, info
, "wasi", value
)) {
461 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
462 value
= BooleanValue(true);
464 value
= BooleanValue(false);
466 if (!JS_SetProperty(cx
, info
, "pbl", value
)) {
470 #ifdef JS_CODEGEN_LOONG64
471 value
= BooleanValue(true);
473 value
= BooleanValue(false);
475 if (!JS_SetProperty(cx
, info
, "loong64", value
)) {
479 #ifdef JS_SIMULATOR_LOONG64
480 value
= BooleanValue(true);
482 value
= BooleanValue(false);
484 if (!JS_SetProperty(cx
, info
, "loong64-simulator", value
)) {
488 #ifdef JS_CODEGEN_RISCV64
489 value
= BooleanValue(true);
491 value
= BooleanValue(false);
493 if (!JS_SetProperty(cx
, info
, "riscv64", value
)) {
497 #ifdef JS_SIMULATOR_RISCV64
498 value
= BooleanValue(true);
500 value
= BooleanValue(false);
502 if (!JS_SetProperty(cx
, info
, "riscv64-simulator", value
)) {
507 value
= BooleanValue(true);
509 value
= BooleanValue(false);
511 if (!JS_SetProperty(cx
, info
, "asan", value
)) {
516 value
= BooleanValue(true);
518 value
= BooleanValue(false);
520 if (!JS_SetProperty(cx
, info
, "tsan", value
)) {
525 value
= BooleanValue(true);
527 value
= BooleanValue(false);
529 if (!JS_SetProperty(cx
, info
, "ubsan", value
)) {
534 value
= BooleanValue(true);
536 value
= BooleanValue(false);
538 if (!JS_SetProperty(cx
, info
, "has-gczeal", value
)) {
543 value
= BooleanValue(true);
545 value
= BooleanValue(false);
547 if (!JS_SetProperty(cx
, info
, "profiling", value
)) {
551 #ifdef INCLUDE_MOZILLA_DTRACE
552 value
= BooleanValue(true);
554 value
= BooleanValue(false);
556 if (!JS_SetProperty(cx
, info
, "dtrace", value
)) {
561 value
= BooleanValue(true);
563 value
= BooleanValue(false);
565 if (!JS_SetProperty(cx
, info
, "valgrind", value
)) {
569 #ifdef JS_HAS_INTL_API
570 value
= BooleanValue(true);
572 value
= BooleanValue(false);
574 if (!JS_SetProperty(cx
, info
, "intl-api", value
)) {
579 value
= BooleanValue(false);
581 value
= BooleanValue(true);
583 if (!JS_SetProperty(cx
, info
, "mapped-array-buffer", value
)) {
588 value
= BooleanValue(true);
590 value
= BooleanValue(false);
592 if (!JS_SetProperty(cx
, info
, "moz-memory", value
)) {
596 value
.setInt32(sizeof(void*));
597 if (!JS_SetProperty(cx
, info
, "pointer-byte-size", value
)) {
601 #ifdef ENABLE_DECORATORS
602 value
= BooleanValue(true);
604 value
= BooleanValue(false);
606 if (!JS_SetProperty(cx
, info
, "decorators", value
)) {
610 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT
611 value
= BooleanValue(true);
613 value
= BooleanValue(false);
615 if (!JS_SetProperty(cx
, info
, "explicit-resource-management", value
)) {
620 value
= BooleanValue(true);
622 value
= BooleanValue(false);
624 if (!JS_SetProperty(cx
, info
, "fuzzing-defined", value
)) {
628 value
= Int32Value(JSFatInlineString::MAX_LENGTH_LATIN1
);
629 if (!JS_SetProperty(cx
, info
, "inline-latin1-chars", value
)) {
633 value
= Int32Value(JSFatInlineString::MAX_LENGTH_TWO_BYTE
);
634 if (!JS_SetProperty(cx
, info
, "inline-two-byte-chars", value
)) {
638 value
= Int32Value(JSThinInlineString::MAX_LENGTH_LATIN1
);
639 if (!JS_SetProperty(cx
, info
, "thin-inline-latin1-chars", value
)) {
643 value
= Int32Value(JSThinInlineString::MAX_LENGTH_TWO_BYTE
);
644 if (!JS_SetProperty(cx
, info
, "thin-inline-two-byte-chars", value
)) {
648 if (js::ThinInlineAtom::EverInstantiated
) {
649 value
= Int32Value(js::ThinInlineAtom::MAX_LENGTH_LATIN1
);
650 if (!JS_SetProperty(cx
, info
, "thin-inline-atom-latin1-chars", value
)) {
654 value
= Int32Value(js::ThinInlineAtom::MAX_LENGTH_TWO_BYTE
);
655 if (!JS_SetProperty(cx
, info
, "thin-inline-atom-two-byte-chars", value
)) {
660 value
= Int32Value(js::FatInlineAtom::MAX_LENGTH_LATIN1
);
661 if (!JS_SetProperty(cx
, info
, "fat-inline-atom-latin1-chars", value
)) {
665 value
= Int32Value(js::FatInlineAtom::MAX_LENGTH_TWO_BYTE
);
666 if (!JS_SetProperty(cx
, info
, "fat-inline-atom-two-byte-chars", value
)) {
670 if (args
.length() == 1) {
671 RootedString
str(cx
, ToString(cx
, args
[0]));
676 if (!JS_StringToId(cx
, str
, &id
)) {
681 if (JS_HasPropertyById(cx
, info
, id
, &hasProperty
) && hasProperty
) {
682 // Returning a true/false from GetProperty
683 return GetProperty(cx
, info
, info
, id
, args
.rval());
686 ReportUsageErrorASCII(cx
, callee
, "Invalid option name");
690 args
.rval().setObject(*info
);
694 static bool IsLCovEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
695 CallArgs args
= CallArgsFromVp(argc
, vp
);
696 args
.rval().setBoolean(coverage::IsLCovEnabled());
700 static bool TrialInline(JSContext
* cx
, unsigned argc
, Value
* vp
) {
701 CallArgs args
= CallArgsFromVp(argc
, vp
);
702 args
.rval().setUndefined();
705 if (iter
.done() || !iter
.isBaseline() || iter
.realm() != cx
->realm()) {
709 jit::BaselineFrame
* frame
= iter
.abstractFramePtr().asBaselineFrame();
710 if (!jit::CanIonCompileScript(cx
, frame
->script())) {
714 return jit::DoTrialInlining(cx
, frame
);
717 static bool ReturnStringCopy(JSContext
* cx
, CallArgs
& args
,
718 const char* message
) {
719 JSString
* str
= JS_NewStringCopyZ(cx
, message
);
724 args
.rval().setString(str
);
728 static bool MaybeGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
729 CallArgs args
= CallArgsFromVp(argc
, vp
);
731 args
.rval().setUndefined();
735 static bool GC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
736 CallArgs args
= CallArgsFromVp(argc
, vp
);
739 * If the first argument is 'zone', we collect any zones previously
740 * scheduled for GC via schedulegc. If the first argument is an object, we
741 * collect the object's zone (and any other zones scheduled for
742 * GC). Otherwise, we collect all zones.
745 if (args
.length() >= 1) {
747 if (arg
.isString()) {
748 if (!JS_StringEqualsLiteral(cx
, arg
.toString(), "zone", &zone
)) {
751 } else if (arg
.isObject()) {
752 PrepareZoneForGC(cx
, UncheckedUnwrap(&arg
.toObject())->zone());
757 JS::GCOptions options
= JS::GCOptions::Normal
;
758 JS::GCReason reason
= JS::GCReason::API
;
759 if (args
.length() >= 2) {
761 if (arg
.isString()) {
762 bool shrinking
= false;
763 bool last_ditch
= false;
764 if (!JS_StringEqualsLiteral(cx
, arg
.toString(), "shrinking",
768 if (!JS_StringEqualsLiteral(cx
, arg
.toString(), "last-ditch",
773 options
= JS::GCOptions::Shrink
;
774 } else if (last_ditch
) {
775 options
= JS::GCOptions::Shrink
;
776 reason
= JS::GCReason::LAST_DITCH
;
781 size_t preBytes
= cx
->runtime()->gc
.heapSize
.bytes();
784 PrepareForDebugGC(cx
->runtime());
786 JS::PrepareForFullGC(cx
);
789 JS::NonIncrementalGC(cx
, options
, reason
);
791 char buf
[256] = {'\0'};
792 if (!js::SupportDifferentialTesting()) {
793 SprintfLiteral(buf
, "before %zu, after %zu\n", preBytes
,
794 cx
->runtime()->gc
.heapSize
.bytes());
796 return ReturnStringCopy(cx
, args
, buf
);
799 static bool MinorGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
800 CallArgs args
= CallArgsFromVp(argc
, vp
);
801 if (args
.get(0) == BooleanValue(true)) {
802 gc::GCRuntime
& gc
= cx
->runtime()->gc
;
803 if (gc
.nursery().isEnabled()) {
804 gc
.storeBuffer().setAboutToOverflow(JS::GCReason::FULL_GENERIC_BUFFER
);
808 cx
->minorGC(JS::GCReason::API
);
809 args
.rval().setUndefined();
813 #define PARAM_NAME_LIST_ENTRY(name, key, writable) " " name
814 #define GC_PARAMETER_ARGS_LIST FOR_EACH_GC_PARAM(PARAM_NAME_LIST_ENTRY)
816 static bool GCParameter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
817 CallArgs args
= CallArgsFromVp(argc
, vp
);
819 JSString
* str
= ToString(cx
, args
.get(0));
824 UniqueChars name
= EncodeLatin1(cx
, str
);
831 if (!GetGCParameterInfo(name
.get(), ¶m
, &writable
)) {
833 cx
, "the first argument must be one of:" GC_PARAMETER_ARGS_LIST
);
838 if (args
.length() == 1) {
839 uint32_t value
= JS_GetGCParameter(cx
, param
);
840 args
.rval().setNumber(value
);
845 JS_ReportErrorASCII(cx
, "Attempt to change read-only parameter %s",
851 // Some Params are not yet fuzzing safe and so we silently skip
852 // changing said parameters.
854 case JSGC_SEMISPACE_NURSERY_ENABLED
:
855 args
.rval().setUndefined();
862 if (disableOOMFunctions
) {
865 case JSGC_MAX_NURSERY_BYTES
:
866 args
.rval().setUndefined();
874 if (!ToNumber(cx
, args
[1], &d
)) {
878 if (d
< 0 || d
> UINT32_MAX
) {
879 JS_ReportErrorASCII(cx
, "Parameter value out of range");
883 uint32_t value
= floor(d
);
884 bool ok
= cx
->runtime()->gc
.setParameter(cx
, param
, value
);
886 JS_ReportErrorASCII(cx
, "Parameter value out of range");
890 args
.rval().setUndefined();
894 static bool FinishBackgroundFree(JSContext
* cx
, unsigned argc
, Value
* vp
) {
895 CallArgs args
= CallArgsFromVp(argc
, vp
);
896 cx
->runtime()->gc
.waitBackgroundFreeEnd();
897 args
.rval().setUndefined();
901 static bool RelazifyFunctions(JSContext
* cx
, unsigned argc
, Value
* vp
) {
902 // Relazifying functions on GC is usually only done for compartments that are
903 // not active. To aid fuzzing, this testing function allows us to relazify
904 // even if the compartment is active.
906 CallArgs args
= CallArgsFromVp(argc
, vp
);
908 // Disable relazification of all scripts on stack. It is a pervasive
909 // assumption in the engine that running scripts still have bytecode.
910 for (AllScriptFramesIter
i(cx
); !i
.done(); ++i
) {
911 i
.script()->clearAllowRelazify();
914 cx
->runtime()->allowRelazificationForTesting
= true;
916 JS::PrepareForFullGC(cx
);
917 JS::NonIncrementalGC(cx
, JS::GCOptions::Shrink
, JS::GCReason::API
);
919 cx
->runtime()->allowRelazificationForTesting
= false;
921 args
.rval().setUndefined();
925 static bool IsProxy(JSContext
* cx
, unsigned argc
, Value
* vp
) {
926 CallArgs args
= CallArgsFromVp(argc
, vp
);
927 if (args
.length() != 1) {
928 JS_ReportErrorASCII(cx
, "the function takes exactly one argument");
931 if (!args
[0].isObject()) {
932 args
.rval().setBoolean(false);
935 args
.rval().setBoolean(args
[0].toObject().is
<ProxyObject
>());
939 static bool WasmIsSupported(JSContext
* cx
, unsigned argc
, Value
* vp
) {
940 CallArgs args
= CallArgsFromVp(argc
, vp
);
941 args
.rval().setBoolean(wasm::HasSupport(cx
) &&
942 wasm::AnyCompilerAvailable(cx
));
946 static bool WasmIsSupportedByHardware(JSContext
* cx
, unsigned argc
, Value
* vp
) {
947 CallArgs args
= CallArgsFromVp(argc
, vp
);
948 args
.rval().setBoolean(wasm::HasPlatformSupport());
952 static bool WasmDebuggingEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
953 CallArgs args
= CallArgsFromVp(argc
, vp
);
954 args
.rval().setBoolean(wasm::HasSupport(cx
) && wasm::BaselineAvailable(cx
));
958 static bool WasmStreamingEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
959 CallArgs args
= CallArgsFromVp(argc
, vp
);
960 args
.rval().setBoolean(wasm::StreamingCompilationAvailable(cx
));
964 static bool WasmCachingEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
965 CallArgs args
= CallArgsFromVp(argc
, vp
);
966 args
.rval().setBoolean(wasm::CodeCachingAvailable(cx
));
970 static bool WasmHugeMemorySupported(JSContext
* cx
, unsigned argc
, Value
* vp
) {
971 CallArgs args
= CallArgsFromVp(argc
, vp
);
972 #ifdef WASM_SUPPORTS_HUGE_MEMORY
973 args
.rval().setBoolean(true);
975 args
.rval().setBoolean(false);
980 static bool WasmMaxMemoryPages(JSContext
* cx
, unsigned argc
, Value
* vp
) {
981 CallArgs args
= CallArgsFromVp(argc
, vp
);
982 if (args
.length() < 1) {
983 JS_ReportErrorASCII(cx
, "not enough arguments");
986 if (!args
.get(0).isString()) {
987 JS_ReportErrorASCII(cx
, "index type must be a string");
990 RootedString
s(cx
, args
.get(0).toString());
991 Rooted
<JSLinearString
*> ls(cx
, s
->ensureLinear(cx
));
995 if (StringEqualsLiteral(ls
, "i32")) {
996 args
.rval().setInt32(
997 int32_t(wasm::MaxMemoryPages(wasm::IndexType::I32
).value()));
1000 if (StringEqualsLiteral(ls
, "i64")) {
1001 #ifdef ENABLE_WASM_MEMORY64
1002 if (wasm::Memory64Available(cx
)) {
1003 args
.rval().setInt32(
1004 int32_t(wasm::MaxMemoryPages(wasm::IndexType::I64
).value()));
1008 JS_ReportErrorASCII(cx
, "memory64 not enabled");
1011 JS_ReportErrorASCII(cx
, "bad index type");
1015 static bool WasmThreadsEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1016 CallArgs args
= CallArgsFromVp(argc
, vp
);
1017 args
.rval().setBoolean(wasm::ThreadsAvailable(cx
));
1021 #define WASM_FEATURE(NAME, ...) \
1022 static bool Wasm##NAME##Enabled(JSContext* cx, unsigned argc, Value* vp) { \
1023 CallArgs args = CallArgsFromVp(argc, vp); \
1024 args.rval().setBoolean(wasm::NAME##Available(cx)); \
1027 JS_FOR_WASM_FEATURES(WASM_FEATURE
);
1030 static bool WasmSimdEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1031 CallArgs args
= CallArgsFromVp(argc
, vp
);
1032 args
.rval().setBoolean(wasm::SimdAvailable(cx
));
1036 static bool WasmCompilersPresent(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1037 CallArgs args
= CallArgsFromVp(argc
, vp
);
1041 if (wasm::BaselinePlatformSupport()) {
1042 strcat(buf
, "baseline");
1044 if (wasm::IonPlatformSupport()) {
1051 JSString
* result
= JS_NewStringCopyZ(cx
, buf
);
1056 args
.rval().setString(result
);
1060 static bool WasmCompileMode(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1061 CallArgs args
= CallArgsFromVp(argc
, vp
);
1063 // This triplet of predicates will select zero or one baseline compiler and
1064 // zero or one optimizing compiler.
1065 bool baseline
= wasm::BaselineAvailable(cx
);
1066 bool ion
= wasm::IonAvailable(cx
);
1067 bool none
= !baseline
&& !ion
;
1068 bool tiered
= baseline
&& ion
;
1070 JSStringBuilder
result(cx
);
1071 if (none
&& !result
.append("none")) {
1074 if (baseline
&& !result
.append("baseline")) {
1077 if (tiered
&& !result
.append("+")) {
1080 if (ion
&& !result
.append("ion")) {
1083 if (JSString
* str
= result
.finishString()) {
1084 args
.rval().setString(str
);
1090 static bool WasmBaselineDisabledByFeatures(JSContext
* cx
, unsigned argc
,
1092 CallArgs args
= CallArgsFromVp(argc
, vp
);
1093 bool isDisabled
= false;
1094 JSStringBuilder
reason(cx
);
1095 if (!wasm::BaselineDisabledByFeatures(cx
, &isDisabled
, &reason
)) {
1099 JSString
* result
= reason
.finishString();
1103 args
.rval().setString(result
);
1105 args
.rval().setBoolean(false);
1110 static bool WasmIonDisabledByFeatures(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1111 CallArgs args
= CallArgsFromVp(argc
, vp
);
1112 bool isDisabled
= false;
1113 JSStringBuilder
reason(cx
);
1114 if (!wasm::IonDisabledByFeatures(cx
, &isDisabled
, &reason
)) {
1118 JSString
* result
= reason
.finishString();
1122 args
.rval().setString(result
);
1124 args
.rval().setBoolean(false);
1129 #ifdef ENABLE_WASM_SIMD
1131 static char lastAnalysisResult
[1024];
1135 void ReportSimdAnalysis(const char* data
) {
1136 strncpy(lastAnalysisResult
, data
, sizeof(lastAnalysisResult
));
1137 lastAnalysisResult
[sizeof(lastAnalysisResult
) - 1] = 0;
1142 // Unstable API for white-box testing of SIMD optimizations.
1144 // Current API: takes no arguments, returns a string describing the last Simd
1145 // simplification applied.
1147 static bool WasmSimdAnalysis(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1148 CallArgs args
= CallArgsFromVp(argc
, vp
);
1150 JS_NewStringCopyZ(cx
, *lastAnalysisResult
? lastAnalysisResult
: "none");
1154 args
.rval().setString(result
);
1155 *lastAnalysisResult
= (char)0;
1161 static bool WasmGlobalFromArrayBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1162 if (!wasm::HasSupport(cx
)) {
1163 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1166 CallArgs args
= CallArgsFromVp(argc
, vp
);
1168 if (args
.length() < 2) {
1169 JS_ReportErrorASCII(cx
, "not enough arguments");
1173 // Get the type of the value
1174 wasm::ValType valType
;
1175 if (!wasm::ToValType(cx
, args
.get(0), &valType
)) {
1179 // Get the array buffer for the value
1180 if (!args
.get(1).isObject() ||
1181 !args
.get(1).toObject().is
<ArrayBufferObject
>()) {
1182 JS_ReportErrorASCII(cx
, "argument is not an array buffer");
1185 Rooted
<ArrayBufferObject
*> buffer(
1186 cx
, &args
.get(1).toObject().as
<ArrayBufferObject
>());
1188 // Only allow POD to be created from bytes
1189 switch (valType
.kind()) {
1190 case wasm::ValType::I32
:
1191 case wasm::ValType::I64
:
1192 case wasm::ValType::F32
:
1193 case wasm::ValType::F64
:
1194 case wasm::ValType::V128
:
1197 JS_ReportErrorASCII(
1198 cx
, "invalid valtype for creating WebAssembly.Global from bytes");
1202 // Check we have all the bytes we need
1203 if (valType
.size() != buffer
->byteLength()) {
1204 JS_ReportErrorASCII(cx
, "array buffer has incorrect size");
1208 // Copy the bytes from buffer into a tagged val
1209 wasm::RootedVal
val(cx
);
1210 val
.get().initFromRootedLocation(valType
, buffer
->dataPointer());
1212 // Create the global object
1214 cx
, GlobalObject::getOrCreatePrototype(cx
, JSProto_WasmGlobal
));
1218 Rooted
<WasmGlobalObject
*> result(
1219 cx
, WasmGlobalObject::create(cx
, val
, false, proto
));
1224 args
.rval().setObject(*result
.get());
1228 enum class LaneInterp
{
1235 size_t LaneInterpLanes(LaneInterp interp
) {
1237 case LaneInterp::I32x4
:
1239 case LaneInterp::I64x2
:
1241 case LaneInterp::F32x4
:
1243 case LaneInterp::F64x2
:
1246 MOZ_ASSERT_UNREACHABLE();
1251 static bool ToLaneInterp(JSContext
* cx
, HandleValue v
, LaneInterp
* out
) {
1252 RootedString
interpStr(cx
, ToString(cx
, v
));
1256 Rooted
<JSLinearString
*> interpLinearStr(cx
, interpStr
->ensureLinear(cx
));
1257 if (!interpLinearStr
) {
1261 if (StringEqualsLiteral(interpLinearStr
, "i32x4")) {
1262 *out
= LaneInterp::I32x4
;
1264 } else if (StringEqualsLiteral(interpLinearStr
, "i64x2")) {
1265 *out
= LaneInterp::I64x2
;
1267 } else if (StringEqualsLiteral(interpLinearStr
, "f32x4")) {
1268 *out
= LaneInterp::F32x4
;
1270 } else if (StringEqualsLiteral(interpLinearStr
, "f64x2")) {
1271 *out
= LaneInterp::F64x2
;
1275 JS_ReportErrorASCII(cx
, "invalid lane interpretation");
1279 static bool WasmGlobalExtractLane(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1280 if (!wasm::HasSupport(cx
)) {
1281 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1284 CallArgs args
= CallArgsFromVp(argc
, vp
);
1286 if (args
.length() < 3) {
1287 JS_ReportErrorASCII(cx
, "not enough arguments");
1291 // Get the global value
1292 if (!args
.get(0).isObject() ||
1293 !args
.get(0).toObject().is
<WasmGlobalObject
>()) {
1294 JS_ReportErrorASCII(cx
, "argument is not wasm value");
1297 Rooted
<WasmGlobalObject
*> global(
1298 cx
, &args
.get(0).toObject().as
<WasmGlobalObject
>());
1300 // Check that we have a v128 value
1301 if (global
->type().kind() != wasm::ValType::V128
) {
1302 JS_ReportErrorASCII(cx
, "global is not a v128 value");
1305 wasm::V128 v128
= global
->val().get().v128();
1307 // Get the passed interpretation of lanes
1309 if (!ToLaneInterp(cx
, args
.get(1), &interp
)) {
1313 // Get the lane to extract
1315 if (!ToInt32(cx
, args
.get(2), &lane
)) {
1319 // Check that the lane interp is valid
1320 if (lane
< 0 || size_t(lane
) >= LaneInterpLanes(interp
)) {
1321 JS_ReportErrorASCII(cx
, "invalid lane for interp");
1325 wasm::RootedVal
val(cx
);
1327 case LaneInterp::I32x4
: {
1329 v128
.extractLane
<uint32_t>(lane
, &i
);
1330 val
.set(wasm::Val(i
));
1333 case LaneInterp::I64x2
: {
1335 v128
.extractLane
<uint64_t>(lane
, &i
);
1336 val
.set(wasm::Val(i
));
1339 case LaneInterp::F32x4
: {
1341 v128
.extractLane
<float>(lane
, &f
);
1342 val
.set(wasm::Val(f
));
1345 case LaneInterp::F64x2
: {
1347 v128
.extractLane
<double>(lane
, &d
);
1348 val
.set(wasm::Val(d
));
1352 MOZ_ASSERT_UNREACHABLE();
1356 cx
, GlobalObject::getOrCreatePrototype(cx
, JSProto_WasmGlobal
));
1357 Rooted
<WasmGlobalObject
*> result(
1358 cx
, WasmGlobalObject::create(cx
, val
, false, proto
));
1359 args
.rval().setObject(*result
.get());
1363 static bool WasmGlobalsEqual(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1364 if (!wasm::HasSupport(cx
)) {
1365 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1368 CallArgs args
= CallArgsFromVp(argc
, vp
);
1370 if (args
.length() < 2) {
1371 JS_ReportErrorASCII(cx
, "not enough arguments");
1375 if (!args
.get(0).isObject() ||
1376 !args
.get(0).toObject().is
<WasmGlobalObject
>() ||
1377 !args
.get(1).isObject() ||
1378 !args
.get(1).toObject().is
<WasmGlobalObject
>()) {
1379 JS_ReportErrorASCII(cx
, "argument is not wasm value");
1383 Rooted
<WasmGlobalObject
*> a(cx
,
1384 &args
.get(0).toObject().as
<WasmGlobalObject
>());
1385 Rooted
<WasmGlobalObject
*> b(cx
,
1386 &args
.get(1).toObject().as
<WasmGlobalObject
>());
1388 if (a
->type().kind() != b
->type().kind()) {
1389 JS_ReportErrorASCII(cx
, "globals are of different kind");
1394 const wasm::Val
& aVal
= a
->val().get();
1395 const wasm::Val
& bVal
= b
->val().get();
1396 switch (a
->type().kind()) {
1397 case wasm::ValType::I32
: {
1398 result
= aVal
.i32() == bVal
.i32();
1401 case wasm::ValType::I64
: {
1402 result
= aVal
.i64() == bVal
.i64();
1405 case wasm::ValType::F32
: {
1406 result
= mozilla::BitwiseCast
<uint32_t>(aVal
.f32()) ==
1407 mozilla::BitwiseCast
<uint32_t>(bVal
.f32());
1410 case wasm::ValType::F64
: {
1411 result
= mozilla::BitwiseCast
<uint64_t>(aVal
.f64()) ==
1412 mozilla::BitwiseCast
<uint64_t>(bVal
.f64());
1415 case wasm::ValType::V128
: {
1416 // Don't know the interpretation of the v128, so we only can do an exact
1417 // bitwise equality. Testing code can use wasmGlobalExtractLane to
1418 // workaround this if needed.
1419 result
= aVal
.v128() == bVal
.v128();
1422 case wasm::ValType::Ref
: {
1423 result
= aVal
.ref() == bVal
.ref();
1427 JS_ReportErrorASCII(cx
, "unsupported type");
1430 args
.rval().setBoolean(result
);
1434 // Flavors of NaN values for WebAssembly.
1436 // https://webassembly.github.io/spec/core/syntax/values.html#floating-point.
1437 enum class NaNFlavor
{
1438 // A canonical NaN value.
1439 // - the sign bit is unspecified,
1440 // - the 8-bit exponent is set to all 1s
1441 // - the MSB of the payload is set to 1 (a quieted NaN) and all others to 0.
1443 // An arithmetic NaN. This is the same as a canonical NaN including that the
1444 // payload MSB is set to 1, but one or more of the remaining payload bits MAY
1445 // BE set to 1 (a canonical NaN specifies all 0s).
1449 static bool IsNaNFlavor(uint32_t bits
, NaNFlavor flavor
) {
1451 case NaNFlavor::Canonical
: {
1452 return (bits
& 0x7fffffff) == 0x7fc00000;
1454 case NaNFlavor::Arithmetic
: {
1455 const uint32_t ArithmeticNaN
= 0x7f800000;
1456 const uint32_t ArithmeticPayloadMSB
= 0x00400000;
1457 bool isNaN
= (bits
& ArithmeticNaN
) == ArithmeticNaN
;
1458 bool isMSBSet
= (bits
& ArithmeticPayloadMSB
) == ArithmeticPayloadMSB
;
1459 return isNaN
&& isMSBSet
;
1466 static bool IsNaNFlavor(uint64_t bits
, NaNFlavor flavor
) {
1468 case NaNFlavor::Canonical
: {
1469 return (bits
& 0x7fffffffffffffff) == 0x7ff8000000000000;
1471 case NaNFlavor::Arithmetic
: {
1472 uint64_t ArithmeticNaN
= 0x7ff0000000000000;
1473 uint64_t ArithmeticPayloadMSB
= 0x0008000000000000;
1474 bool isNaN
= (bits
& ArithmeticNaN
) == ArithmeticNaN
;
1475 bool isMsbSet
= (bits
& ArithmeticPayloadMSB
) == ArithmeticPayloadMSB
;
1476 return isNaN
&& isMsbSet
;
1483 static bool ToNaNFlavor(JSContext
* cx
, HandleValue v
, NaNFlavor
* out
) {
1484 RootedString
flavorStr(cx
, ToString(cx
, v
));
1488 Rooted
<JSLinearString
*> flavorLinearStr(cx
, flavorStr
->ensureLinear(cx
));
1489 if (!flavorLinearStr
) {
1493 if (StringEqualsLiteral(flavorLinearStr
, "canonical_nan")) {
1494 *out
= NaNFlavor::Canonical
;
1496 } else if (StringEqualsLiteral(flavorLinearStr
, "arithmetic_nan")) {
1497 *out
= NaNFlavor::Arithmetic
;
1501 JS_ReportErrorASCII(cx
, "invalid nan flavor");
1505 static bool WasmGlobalIsNaN(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1506 if (!wasm::HasSupport(cx
)) {
1507 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1510 CallArgs args
= CallArgsFromVp(argc
, vp
);
1512 if (args
.length() < 2) {
1513 JS_ReportErrorASCII(cx
, "not enough arguments");
1517 if (!args
.get(0).isObject() ||
1518 !args
.get(0).toObject().is
<WasmGlobalObject
>()) {
1519 JS_ReportErrorASCII(cx
, "argument is not wasm value");
1522 Rooted
<WasmGlobalObject
*> global(
1523 cx
, &args
.get(0).toObject().as
<WasmGlobalObject
>());
1526 if (!ToNaNFlavor(cx
, args
.get(1), &flavor
)) {
1531 const wasm::Val
& val
= global
->val().get();
1532 switch (global
->type().kind()) {
1533 case wasm::ValType::F32
: {
1534 result
= IsNaNFlavor(mozilla::BitwiseCast
<uint32_t>(val
.f32()), flavor
);
1537 case wasm::ValType::F64
: {
1538 result
= IsNaNFlavor(mozilla::BitwiseCast
<uint64_t>(val
.f64()), flavor
);
1542 JS_ReportErrorASCII(cx
, "global is not a floating point value");
1545 args
.rval().setBoolean(result
);
1549 static bool WasmGlobalToString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1550 if (!wasm::HasSupport(cx
)) {
1551 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1554 CallArgs args
= CallArgsFromVp(argc
, vp
);
1556 if (args
.length() < 1) {
1557 JS_ReportErrorASCII(cx
, "not enough arguments");
1560 if (!args
.get(0).isObject() ||
1561 !args
.get(0).toObject().is
<WasmGlobalObject
>()) {
1562 JS_ReportErrorASCII(cx
, "argument is not wasm value");
1565 Rooted
<WasmGlobalObject
*> global(
1566 cx
, &args
.get(0).toObject().as
<WasmGlobalObject
>());
1567 const wasm::Val
& globalVal
= global
->val().get();
1570 switch (globalVal
.type().kind()) {
1571 case wasm::ValType::I32
: {
1572 result
= JS_smprintf("i32:%" PRIx32
, globalVal
.i32());
1575 case wasm::ValType::I64
: {
1576 result
= JS_smprintf("i64:%" PRIx64
, globalVal
.i64());
1579 case wasm::ValType::F32
: {
1580 result
= JS_smprintf("f32:%f", globalVal
.f32());
1583 case wasm::ValType::F64
: {
1584 result
= JS_smprintf("f64:%lf", globalVal
.f64());
1587 case wasm::ValType::V128
: {
1588 wasm::V128 v128
= globalVal
.v128();
1589 result
= JS_smprintf(
1590 "v128:%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x", v128
.bytes
[0],
1591 v128
.bytes
[1], v128
.bytes
[2], v128
.bytes
[3], v128
.bytes
[4],
1592 v128
.bytes
[5], v128
.bytes
[6], v128
.bytes
[7], v128
.bytes
[8],
1593 v128
.bytes
[9], v128
.bytes
[10], v128
.bytes
[11], v128
.bytes
[12],
1594 v128
.bytes
[13], v128
.bytes
[14], v128
.bytes
[15]);
1597 case wasm::ValType::Ref
: {
1598 result
= JS_smprintf("ref:%" PRIxPTR
, globalVal
.ref().rawValue());
1602 MOZ_ASSERT_UNREACHABLE();
1605 args
.rval().setString(JS_NewStringCopyZ(cx
, result
.get()));
1609 static bool WasmLosslessInvoke(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1610 if (!wasm::HasSupport(cx
)) {
1611 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1614 CallArgs args
= CallArgsFromVp(argc
, vp
);
1616 if (args
.length() < 1) {
1617 JS_ReportErrorASCII(cx
, "not enough arguments");
1620 if (!args
.get(0).isObject() || !args
.get(0).toObject().is
<JSFunction
>()) {
1621 JS_ReportErrorASCII(cx
, "argument is not an object");
1625 RootedFunction
func(cx
, &args
[0].toObject().as
<JSFunction
>());
1626 if (!func
|| !wasm::IsWasmExportedFunction(func
)) {
1627 JS_ReportErrorASCII(cx
, "argument is not an exported wasm function");
1631 // Switch to the function's realm
1632 AutoRealm
ar(cx
, func
);
1634 // Get the instance and funcIndex for calling the function
1635 wasm::Instance
& instance
= wasm::ExportedFunctionToInstance(func
);
1636 uint32_t funcIndex
= wasm::ExportedFunctionToFuncIndex(func
);
1638 // Set up a modified call frame following the standard JS
1639 // [callee, this, arguments...] convention.
1640 RootedValueVector
wasmCallFrame(cx
);
1641 size_t len
= 2 + args
.length();
1642 if (!wasmCallFrame
.resize(len
)) {
1645 wasmCallFrame
[0].set(ObjectValue(*func
));
1646 wasmCallFrame
[1].set(args
.thisv());
1647 // Copy over the arguments needed to invoke the provided wasm function,
1648 // skipping the wasm function we're calling that is at `args.get(0)`.
1649 for (size_t i
= 1; i
< args
.length(); i
++) {
1650 size_t wasmArg
= i
- 1;
1651 wasmCallFrame
[2 + wasmArg
].set(args
.get(i
));
1653 size_t wasmArgc
= argc
- 1;
1654 CallArgs
wasmCallArgs(CallArgsFromVp(wasmArgc
, wasmCallFrame
.begin()));
1656 // Invoke the function with the new call frame
1657 bool result
= instance
.callExport(cx
, funcIndex
, wasmCallArgs
,
1658 wasm::CoercionLevel::Lossless
);
1659 // Assign the wasm rval to our rval
1660 args
.rval().set(wasmCallArgs
.rval());
1664 static bool ConvertToTier(JSContext
* cx
, HandleValue value
,
1665 const wasm::Code
& code
, wasm::Tier
* tier
) {
1666 RootedString
option(cx
, JS::ToString(cx
, value
));
1672 bool stableTier
= false;
1673 bool bestTier
= false;
1674 bool baselineTier
= false;
1675 bool ionTier
= false;
1677 if (!JS_StringEqualsLiteral(cx
, option
, "stable", &stableTier
) ||
1678 !JS_StringEqualsLiteral(cx
, option
, "best", &bestTier
) ||
1679 !JS_StringEqualsLiteral(cx
, option
, "baseline", &baselineTier
) ||
1680 !JS_StringEqualsLiteral(cx
, option
, "ion", &ionTier
)) {
1685 *tier
= code
.stableCompleteTier();
1686 } else if (bestTier
) {
1687 *tier
= code
.bestCompleteTier();
1688 } else if (baselineTier
) {
1689 *tier
= wasm::Tier::Baseline
;
1690 } else if (ionTier
) {
1691 *tier
= wasm::Tier::Optimized
;
1693 // You can omit the argument but you can't pass just anything you like
1700 static bool WasmExtractCode(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1701 if (!wasm::HasSupport(cx
)) {
1702 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1706 CallArgs args
= CallArgsFromVp(argc
, vp
);
1708 if (!args
.get(0).isObject()) {
1709 JS_ReportErrorASCII(cx
, "argument is not an object");
1713 Rooted
<WasmModuleObject
*> module(
1714 cx
, args
[0].toObject().maybeUnwrapIf
<WasmModuleObject
>());
1716 JS_ReportErrorASCII(cx
, "argument is not a WebAssembly.Module");
1720 wasm::Tier tier
= module
->module().code().stableCompleteTier();
1721 if (args
.length() > 1 &&
1722 !ConvertToTier(cx
, args
[1], module
->module().code(), &tier
)) {
1723 args
.rval().setNull();
1727 RootedValue
result(cx
);
1728 if (!module
->module().extractCode(cx
, tier
, &result
)) {
1732 args
.rval().set(result
);
1736 struct DisasmBuffer
{
1737 JSStringBuilder builder
;
1739 explicit DisasmBuffer(JSContext
* cx
) : builder(cx
), oom(false) {}
1742 static bool HasDisassembler(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1743 CallArgs args
= CallArgsFromVp(argc
, vp
);
1744 args
.rval().setBoolean(jit::HasDisassembler());
1748 MOZ_THREAD_LOCAL(DisasmBuffer
*) disasmBuf
;
1750 static void captureDisasmText(const char* text
) {
1751 DisasmBuffer
* buf
= disasmBuf
.get();
1752 if (!buf
->builder
.append(text
, strlen(text
)) || !buf
->builder
.append('\n')) {
1757 static bool DisassembleNative(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1758 CallArgs args
= CallArgsFromVp(argc
, vp
);
1759 args
.rval().setUndefined();
1761 if (args
.length() < 1) {
1762 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1763 JSMSG_MORE_ARGS_NEEDED
, "disnative", "1", "",
1768 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
1769 JS_ReportErrorASCII(cx
, "The first argument must be a function.");
1773 JSSprinter
sprinter(cx
);
1774 if (!sprinter
.init()) {
1778 RootedFunction
fun(cx
, &args
[0].toObject().as
<JSFunction
>());
1780 uint8_t* jit_begin
= nullptr;
1781 uint8_t* jit_end
= nullptr;
1783 if (fun
->isAsmJSNative() || fun
->isWasmWithJitEntry()) {
1784 if (fun
->isAsmJSNative()) {
1787 sprinter
.printf("; backend=asmjs\n");
1788 sprinter
.printf("; backend=wasm\n");
1790 js::wasm::Instance
& inst
= fun
->wasmInstance();
1791 const js::wasm::Code
& code
= inst
.code();
1792 const uint32_t funcIndex
= code
.getFuncIndex(&*fun
);
1793 const js::wasm::CodeBlock
& codeBlock
= inst
.code().funcCodeBlock(funcIndex
);
1794 const js::wasm::CodeSegment
& segment
= *codeBlock
.segment
;
1795 const js::wasm::FuncExport
& func
= codeBlock
.lookupFuncExport(funcIndex
);
1796 const js::wasm::CodeRange
& codeRange
= codeBlock
.codeRange(func
);
1798 jit_begin
= segment
.base() + codeRange
.begin();
1799 jit_end
= segment
.base() + codeRange
.end();
1800 } else if (fun
->hasJitScript()) {
1801 JSScript
* script
= fun
->nonLazyScript();
1802 if (script
== nullptr) {
1806 js::jit::IonScript
* ion
=
1807 script
->hasIonScript() ? script
->ionScript() : nullptr;
1808 js::jit::BaselineScript
* baseline
=
1809 script
->hasBaselineScript() ? script
->baselineScript() : nullptr;
1810 if (ion
&& ion
->method()) {
1811 sprinter
.printf("; backend=ion\n");
1812 jit_begin
= ion
->method()->raw();
1813 jit_end
= ion
->method()->rawEnd();
1814 } else if (baseline
) {
1815 sprinter
.printf("; backend=baseline\n");
1816 jit_begin
= baseline
->method()->raw();
1817 jit_end
= baseline
->method()->rawEnd();
1820 JS_ReportErrorASCII(cx
,
1821 "The function hasn't been warmed up, hence no JIT code "
1826 if (jit_begin
== nullptr || jit_end
== nullptr) {
1830 #ifdef JS_CODEGEN_ARM
1831 // The ARM32 disassembler is currently not fuzzing-safe because it doesn't
1832 // handle constant pools correctly (bug 1875363).
1834 JS_ReportErrorASCII(cx
, "disnative is not fuzzing-safe on ARM32");
1839 // Dump the raw code to a file before disassembling in case
1840 // finishString triggers a GC and discards the jitcode.
1841 if (!fuzzingSafe
&& args
.length() > 1 && args
[1].isString()) {
1842 RootedString
str(cx
, args
[1].toString());
1843 JS::UniqueChars fileNameBytes
= JS_EncodeStringToUTF8(cx
, str
);
1845 const char* fileName
= fileNameBytes
.get();
1847 ReportOutOfMemory(cx
);
1851 FILE* f
= fopen(fileName
, "w");
1853 JS_ReportErrorASCII(cx
, "Could not open file for writing.");
1857 uintptr_t expected_length
= reinterpret_cast<uintptr_t>(jit_end
) -
1858 reinterpret_cast<uintptr_t>(jit_begin
);
1859 if (expected_length
!= fwrite(jit_begin
, jit_end
- jit_begin
, 1, f
)) {
1860 JS_ReportErrorASCII(cx
, "Did not write all function bytes to the file.");
1867 DisasmBuffer
buf(cx
);
1868 disasmBuf
.set(&buf
);
1869 auto onFinish
= mozilla::MakeScopeExit([&] { disasmBuf
.set(nullptr); });
1871 jit::Disassemble(jit_begin
, jit_end
- jit_begin
, &captureDisasmText
);
1874 ReportOutOfMemory(cx
);
1877 JSString
* sresult
= buf
.builder
.finishString();
1879 ReportOutOfMemory(cx
);
1882 sprinter
.putString(cx
, sresult
);
1884 JSString
* str
= sprinter
.release(cx
);
1889 args
[0].setUndefined();
1890 args
.rval().setString(str
);
1895 static bool ComputeTier(JSContext
* cx
, const wasm::Code
& code
,
1896 HandleValue tierSelection
, wasm::Tier
* tier
) {
1897 *tier
= code
.stableCompleteTier();
1898 if (!tierSelection
.isUndefined() &&
1899 !ConvertToTier(cx
, tierSelection
, code
, tier
)) {
1900 JS_ReportErrorASCII(cx
, "invalid tier");
1907 template <typename DisasmFunction
>
1908 static bool DisassembleIt(JSContext
* cx
, bool asString
, MutableHandleValue rval
,
1909 DisasmFunction
&& disassembleIt
) {
1911 DisasmBuffer
buf(cx
);
1912 disasmBuf
.set(&buf
);
1913 auto onFinish
= mozilla::MakeScopeExit([&] { disasmBuf
.set(nullptr); });
1914 disassembleIt(captureDisasmText
);
1916 ReportOutOfMemory(cx
);
1919 JSString
* sresult
= buf
.builder
.finishString();
1921 ReportOutOfMemory(cx
);
1924 rval
.setString(sresult
);
1928 disassembleIt([](const char* text
) { fprintf(stderr
, "%s\n", text
); });
1932 static bool WasmDisassembleFunction(JSContext
* cx
, const HandleFunction
& func
,
1933 HandleValue tierSelection
, bool asString
,
1934 MutableHandleValue rval
) {
1935 wasm::Instance
& instance
= wasm::ExportedFunctionToInstance(func
);
1936 uint32_t funcIndex
= wasm::ExportedFunctionToFuncIndex(func
);
1939 if (!ComputeTier(cx
, instance
.code(), tierSelection
, &tier
)) {
1943 if (!instance
.code().funcHasTier(funcIndex
, tier
)) {
1944 JS_ReportErrorASCII(cx
, "function missing selected tier");
1948 return DisassembleIt(
1949 cx
, asString
, rval
, [&](void (*captureText
)(const char*)) {
1950 instance
.disassembleExport(cx
, funcIndex
, tier
, captureText
);
1954 static bool WasmDisassembleCode(JSContext
* cx
, const wasm::Code
& code
,
1955 HandleValue tierSelection
, int kindSelection
,
1956 bool asString
, MutableHandleValue rval
) {
1958 if (!ComputeTier(cx
, code
, tierSelection
, &tier
)) {
1962 return DisassembleIt(cx
, asString
, rval
,
1963 [&](void (*captureText
)(const char*)) {
1964 code
.disassemble(cx
, tier
, kindSelection
, captureText
);
1968 static bool WasmDisassemble(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1969 if (!wasm::HasSupport(cx
)) {
1970 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1974 CallArgs args
= CallArgsFromVp(argc
, vp
);
1976 args
.rval().set(UndefinedValue());
1978 if (!args
.get(0).isObject()) {
1979 JS_ReportErrorASCII(cx
, "argument is not an object");
1983 bool asString
= false;
1984 RootedValue
tierSelection(cx
);
1985 int kindSelection
= (1 << wasm::CodeRange::Function
);
1986 if (args
.length() > 1 && args
[1].isObject()) {
1987 RootedObject
options(cx
, &args
[1].toObject());
1988 RootedValue
val(cx
);
1990 if (!JS_GetProperty(cx
, options
, "asString", &val
)) {
1993 asString
= val
.isBoolean() && val
.toBoolean();
1995 if (!JS_GetProperty(cx
, options
, "tier", &tierSelection
)) {
1999 if (!JS_GetProperty(cx
, options
, "kinds", &val
)) {
2002 if (val
.isString() && val
.toString()->hasLatin1Chars()) {
2003 AutoStableStringChars
stable(cx
);
2004 if (!stable
.init(cx
, val
.toString())) {
2007 const char* p
= (const char*)(stable
.latin1Chars());
2008 const char* end
= p
+ val
.toString()->length();
2011 if (strncmp(p
, "Function", 8) == 0) {
2012 selection
|= (1 << wasm::CodeRange::Function
);
2014 } else if (strncmp(p
, "InterpEntry", 11) == 0) {
2015 selection
|= (1 << wasm::CodeRange::InterpEntry
);
2017 } else if (strncmp(p
, "JitEntry", 8) == 0) {
2018 selection
|= (1 << wasm::CodeRange::JitEntry
);
2020 } else if (strncmp(p
, "ImportInterpExit", 16) == 0) {
2021 selection
|= (1 << wasm::CodeRange::ImportInterpExit
);
2023 } else if (strncmp(p
, "ImportJitExit", 13) == 0) {
2024 selection
|= (1 << wasm::CodeRange::ImportJitExit
);
2026 } else if (strncmp(p
, "all", 3) == 0) {
2032 if (p
== end
|| *p
!= ',') {
2038 kindSelection
= selection
;
2040 JS_ReportErrorASCII(cx
, "argument object has invalid `kinds`");
2046 RootedFunction
func(cx
, args
[0].toObject().maybeUnwrapIf
<JSFunction
>());
2047 if (func
&& wasm::IsWasmExportedFunction(func
)) {
2048 return WasmDisassembleFunction(cx
, func
, tierSelection
, asString
,
2051 if (args
[0].toObject().is
<WasmModuleObject
>()) {
2052 return WasmDisassembleCode(
2053 cx
, args
[0].toObject().as
<WasmModuleObject
>().module().code(),
2054 tierSelection
, kindSelection
, asString
, args
.rval());
2056 if (args
[0].toObject().is
<WasmInstanceObject
>()) {
2057 return WasmDisassembleCode(
2058 cx
, args
[0].toObject().as
<WasmInstanceObject
>().instance().code(),
2059 tierSelection
, kindSelection
, asString
, args
.rval());
2061 JS_ReportErrorASCII(
2062 cx
, "argument is not an exported wasm function or a wasm module");
2066 static bool WasmFunctionTier(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2067 if (!wasm::HasSupport(cx
)) {
2068 JS_ReportErrorASCII(cx
, "wasm support unavailable");
2072 CallArgs args
= CallArgsFromVp(argc
, vp
);
2074 args
.rval().set(UndefinedValue());
2076 if (!args
.get(0).isObject()) {
2077 JS_ReportErrorASCII(cx
, "argument is not an object");
2081 RootedFunction
func(cx
, args
[0].toObject().maybeUnwrapIf
<JSFunction
>());
2082 if (func
&& wasm::IsWasmExportedFunction(func
)) {
2083 uint32_t funcIndex
= wasm::ExportedFunctionToFuncIndex(func
);
2084 wasm::Instance
& instance
= wasm::ExportedFunctionToInstance(func
);
2085 wasm::Tier tier
= instance
.code().funcTier(funcIndex
);
2086 RootedString
tierString(cx
, JS_NewStringCopyZ(cx
, wasm::ToString(tier
)));
2088 ReportOutOfMemory(cx
);
2091 args
.rval().set(StringValue(tierString
));
2094 JS_ReportErrorASCII(cx
, "argument is not an exported wasm function");
2098 static bool ToIonDumpContents(JSContext
* cx
, HandleValue value
,
2099 wasm::IonDumpContents
* contents
) {
2100 RootedString
option(cx
, JS::ToString(cx
, value
));
2106 bool isEqual
= false;
2107 if (!JS_StringEqualsLiteral(cx
, option
, "mir", &isEqual
) || isEqual
) {
2108 *contents
= wasm::IonDumpContents::UnoptimizedMIR
;
2110 } else if (!JS_StringEqualsLiteral(cx
, option
, "unopt-mir", &isEqual
) ||
2112 *contents
= wasm::IonDumpContents::UnoptimizedMIR
;
2114 } else if (!JS_StringEqualsLiteral(cx
, option
, "opt-mir", &isEqual
) ||
2116 *contents
= wasm::IonDumpContents::OptimizedMIR
;
2118 } else if (!JS_StringEqualsLiteral(cx
, option
, "lir", &isEqual
) || isEqual
) {
2119 *contents
= wasm::IonDumpContents::LIR
;
2126 static bool WasmDumpIon(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2127 if (!wasm::HasSupport(cx
)) {
2128 JS_ReportErrorASCII(cx
, "wasm support unavailable");
2132 CallArgs args
= CallArgsFromVp(argc
, vp
);
2134 args
.rval().set(UndefinedValue());
2136 if (!args
.get(0).isObject()) {
2137 JS_ReportErrorASCII(cx
, "argument is not an object");
2141 uint32_t targetFuncIndex
;
2142 if (!ToUint32(cx
, args
.get(1), &targetFuncIndex
)) {
2143 JS_ReportErrorASCII(cx
, "argument is not a func index");
2147 wasm::IonDumpContents contents
= wasm::IonDumpContents::Default
;
2148 if (args
.length() > 2 && !ToIonDumpContents(cx
, args
.get(2), &contents
)) {
2149 JS_ReportErrorASCII(cx
, "argument is not a valid dump contents");
2153 SharedMem
<uint8_t*> dataPointer
;
2155 if (!IsBufferSource(args
.get(0).toObjectOrNull(), &dataPointer
,
2157 JS_ReportErrorASCII(cx
, "argument is not a buffer source");
2161 wasm::MutableBytes bytecode
= cx
->new_
<wasm::ShareableBytes
>();
2165 if (!bytecode
->append(dataPointer
.unwrap(), byteLength
)) {
2166 ReportOutOfMemory(cx
);
2173 ReportOutOfMemory(cx
);
2177 if (!wasm::DumpIonFunctionInModule(*bytecode
, targetFuncIndex
, contents
, out
,
2180 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
2181 JSMSG_WASM_COMPILE_ERROR
, error
.get());
2184 ReportOutOfMemory(cx
);
2188 JSString
* str
= out
.release(cx
);
2190 ReportOutOfMemory(cx
);
2193 args
.rval().set(StringValue(str
));
2197 enum class Flag
{ Tier2Complete
, Deserialized
, ParsedBranchHints
};
2199 static bool WasmReturnFlag(JSContext
* cx
, unsigned argc
, Value
* vp
, Flag flag
) {
2200 CallArgs args
= CallArgsFromVp(argc
, vp
);
2202 if (!args
.get(0).isObject()) {
2203 JS_ReportErrorASCII(cx
, "argument is not an object");
2207 Rooted
<WasmModuleObject
*> module(
2208 cx
, args
[0].toObject().maybeUnwrapIf
<WasmModuleObject
>());
2210 JS_ReportErrorASCII(cx
, "argument is not a WebAssembly.Module");
2216 case Flag::Tier2Complete
:
2217 b
= !module
->module().testingTier2Active();
2219 case Flag::Deserialized
:
2220 b
= module
->module().loggingDeserialized();
2222 case Flag::ParsedBranchHints
:
2223 b
= !module
->module().codeMeta().branchHints
.failedParse();
2227 args
.rval().set(BooleanValue(b
));
2231 static bool wasmMetadataAnalysis(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2232 CallArgs args
= CallArgsFromVp(argc
, vp
);
2234 if (!args
.get(0).isObject()) {
2235 JS_ReportErrorASCII(cx
, "argument is not an object");
2239 if (args
[0].toObject().is
<WasmModuleObject
>()) {
2240 HashMap
<const char*, uint32_t, mozilla::CStringHasher
, SystemAllocPolicy
>
2243 .as
<WasmModuleObject
>()
2246 .metadataAnalysis(cx
);
2247 if (hashmap
.empty()) {
2248 JS_ReportErrorASCII(cx
, "Metadata analysis has failed");
2252 // metadataAnalysis returned a map of {key, value} with various statistics
2253 // convert it into a dictionary to be used by JS
2254 Rooted
<IdValueVector
> props(cx
, IdValueVector(cx
));
2256 for (auto iter
= hashmap
.iter(); !iter
.done(); iter
.next()) {
2257 const auto* key
= iter
.get().key();
2258 auto value
= iter
.get().value();
2260 JSString
* string
= JS_NewStringCopyZ(cx
, key
);
2266 IdValuePair(NameToId(string
->asLinear().toPropertyName(cx
)),
2267 NumberValue(value
)))) {
2272 JSObject
* results
= NewPlainObjectWithUniqueNames(cx
, props
);
2276 args
.rval().setObject(*results
);
2281 JS_ReportErrorASCII(
2282 cx
, "argument is not an exported wasm function or a wasm module");
2287 static bool WasmHasTier2CompilationCompleted(JSContext
* cx
, unsigned argc
,
2289 return WasmReturnFlag(cx
, argc
, vp
, Flag::Tier2Complete
);
2292 static bool WasmLoadedFromCache(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2293 return WasmReturnFlag(cx
, argc
, vp
, Flag::Deserialized
);
2296 #ifdef ENABLE_WASM_BRANCH_HINTING
2297 static bool WasmParsedBranchHints(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2298 return WasmReturnFlag(cx
, argc
, vp
, Flag::ParsedBranchHints
);
2300 #endif // ENABLE_WASM_BRANCH_HINTING
2302 static bool WasmBuiltinI8VecMul(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2303 if (!wasm::HasSupport(cx
)) {
2304 JS_ReportErrorASCII(cx
, "wasm support unavailable");
2308 CallArgs args
= CallArgsFromVp(argc
, vp
);
2310 Rooted
<WasmModuleObject
*> module(cx
);
2311 if (!wasm::CompileBuiltinModule(cx
, wasm::BuiltinModuleId::SelfTest
,
2315 args
.rval().set(ObjectValue(*module
.get()));
2319 #ifdef ENABLE_WASM_GC
2320 static bool WasmGcReadField(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2321 CallArgs args
= CallArgsFromVp(argc
, vp
);
2322 RootedObject
callee(cx
, &args
.callee());
2324 if (!args
.requireAtLeast(cx
, "wasmGcReadField", 2)) {
2328 if (!args
[0].isObject() || !args
[0].toObject().is
<WasmGcObject
>()) {
2329 ReportUsageErrorASCII(cx
, callee
,
2330 "First argument must be a WebAssembly GC object");
2335 if (!JS::ToInt32(cx
, args
[1], &fieldIndex
) || fieldIndex
< 0) {
2336 ReportUsageErrorASCII(cx
, callee
,
2337 "Second argument must be a non-negative integer");
2341 Rooted
<WasmGcObject
*> gcObject(cx
, &args
[0].toObject().as
<WasmGcObject
>());
2342 Rooted
<Value
> gcValue(cx
);
2343 if (!WasmGcObject::loadValue(cx
, gcObject
, jsid::Int(int32_t(fieldIndex
)),
2348 args
.rval().set(gcValue
);
2352 static bool WasmGcArrayLength(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2353 CallArgs args
= CallArgsFromVp(argc
, vp
);
2354 RootedObject
callee(cx
, &args
.callee());
2356 if (!args
.requireAtLeast(cx
, "wasmGcArrayLength", 1)) {
2360 if (!args
[0].isObject() || !args
[0].toObject().is
<WasmArrayObject
>()) {
2361 ReportUsageErrorASCII(cx
, callee
,
2362 "First argument must be a WebAssembly GC array");
2366 WasmArrayObject
& arr
= args
[0].toObject().as
<WasmArrayObject
>();
2367 args
.rval().setInt32(int32_t(arr
.numElements_
));
2370 #endif // ENABLE_WASM_GC
2372 static bool LargeArrayBufferSupported(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2373 CallArgs args
= CallArgsFromVp(argc
, vp
);
2374 args
.rval().setBoolean(ArrayBufferObject::ByteLengthLimit
>
2375 ArrayBufferObject::ByteLengthLimitForSmallBuffer
);
2379 static bool IsLazyFunction(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2380 CallArgs args
= CallArgsFromVp(argc
, vp
);
2381 if (args
.length() != 1) {
2382 JS_ReportErrorASCII(cx
, "The function takes exactly one argument.");
2385 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
2386 JS_ReportErrorASCII(cx
, "The first argument should be a function.");
2389 JSFunction
* fun
= &args
[0].toObject().as
<JSFunction
>();
2390 args
.rval().setBoolean(fun
->isInterpreted() && !fun
->hasBytecode());
2394 static bool IsRelazifiableFunction(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2395 CallArgs args
= CallArgsFromVp(argc
, vp
);
2396 if (args
.length() != 1) {
2397 JS_ReportErrorASCII(cx
, "The function takes exactly one argument.");
2400 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
2401 JS_ReportErrorASCII(cx
, "The first argument should be a function.");
2405 JSFunction
* fun
= &args
[0].toObject().as
<JSFunction
>();
2406 args
.rval().setBoolean(fun
->hasBytecode() &&
2407 fun
->nonLazyScript()->allowRelazify());
2411 static bool IsInStencilCache(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2412 CallArgs args
= CallArgsFromVp(argc
, vp
);
2413 if (args
.length() != 1) {
2414 JS_ReportErrorASCII(cx
, "The function takes exactly one argument.");
2418 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
2419 JS_ReportErrorASCII(cx
, "The first argument should be a function.");
2424 // When running code concurrently to fill-up the stencil cache, the content
2425 // is not garanteed to be present.
2426 args
.rval().setBoolean(false);
2430 JSFunction
* fun
= &args
[0].toObject().as
<JSFunction
>();
2431 BaseScript
* script
= fun
->baseScript();
2432 RefPtr
<ScriptSource
> ss
= script
->scriptSource();
2433 DelazificationCache
& cache
= DelazificationCache::getSingleton();
2434 auto guard
= cache
.isSourceCached(ss
);
2436 args
.rval().setBoolean(false);
2440 StencilContext
key(ss
, script
->extent());
2441 frontend::CompilationStencil
* stencil
= cache
.lookup(guard
, key
);
2442 args
.rval().setBoolean(bool(stencil
));
2446 static bool WaitForStencilCache(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2447 CallArgs args
= CallArgsFromVp(argc
, vp
);
2448 if (args
.length() != 1) {
2449 JS_ReportErrorASCII(cx
, "The function takes exactly one argument.");
2453 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
2454 JS_ReportErrorASCII(cx
, "The first argument should be a function.");
2457 args
.rval().setUndefined();
2459 JSFunction
* fun
= &args
[0].toObject().as
<JSFunction
>();
2460 BaseScript
* script
= fun
->baseScript();
2461 RefPtr
<ScriptSource
> ss
= script
->scriptSource();
2462 DelazificationCache
& cache
= DelazificationCache::getSingleton();
2463 StencilContext
key(ss
, script
->extent());
2465 AutoLockHelperThreadState lock
;
2466 if (!HelperThreadState().isInitialized(lock
)) {
2472 // This capture a Mutex that we have to release before using the wait
2474 auto guard
= cache
.isSourceCached(ss
);
2479 frontend::CompilationStencil
* stencil
= cache
.lookup(guard
, key
);
2485 HelperThreadState().wait(lock
);
2490 static bool HasSameBytecodeData(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2491 CallArgs args
= CallArgsFromVp(argc
, vp
);
2492 if (args
.length() != 2) {
2493 JS_ReportErrorASCII(cx
, "The function takes exactly two argument.");
2497 auto GetSharedData
= [](JSContext
* cx
,
2498 HandleValue v
) -> SharedImmutableScriptData
* {
2499 if (!v
.isObject()) {
2500 JS_ReportErrorASCII(cx
, "The arguments must be interpreted functions.");
2504 RootedObject
obj(cx
, CheckedUnwrapDynamic(&v
.toObject(), cx
));
2509 if (!obj
->is
<JSFunction
>() || !obj
->as
<JSFunction
>().isInterpreted()) {
2510 JS_ReportErrorASCII(cx
, "The arguments must be interpreted functions.");
2514 AutoRealm
ar(cx
, obj
);
2515 RootedFunction
fun(cx
, &obj
->as
<JSFunction
>());
2516 RootedScript
script(cx
, JSFunction::getOrCreateScript(cx
, fun
));
2521 MOZ_ASSERT(script
->sharedData());
2522 return script
->sharedData();
2525 // NOTE: We use RefPtr below to keep the data alive across possible GC since
2526 // the functions may be in different Zones.
2528 RefPtr
<SharedImmutableScriptData
> sharedData1
= GetSharedData(cx
, args
[0]);
2533 RefPtr
<SharedImmutableScriptData
> sharedData2
= GetSharedData(cx
, args
[1]);
2538 args
.rval().setBoolean(sharedData1
== sharedData2
);
2542 static bool InternalConst(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2543 CallArgs args
= CallArgsFromVp(argc
, vp
);
2544 if (args
.length() == 0) {
2545 JS_ReportErrorASCII(cx
, "the function takes exactly one argument");
2549 JSString
* str
= ToString(cx
, args
[0]);
2553 JSLinearString
* linear
= JS_EnsureLinearString(cx
, str
);
2558 if (JS_LinearStringEqualsLiteral(linear
, "MARK_STACK_BASE_CAPACITY")) {
2559 args
.rval().setNumber(uint32_t(js::MARK_STACK_BASE_CAPACITY
));
2561 JS_ReportErrorASCII(cx
, "unknown const name");
2567 static bool GCPreserveCode(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2568 CallArgs args
= CallArgsFromVp(argc
, vp
);
2570 if (args
.length() != 0) {
2571 RootedObject
callee(cx
, &args
.callee());
2572 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2576 cx
->runtime()->gc
.setAlwaysPreserveCode();
2578 args
.rval().setUndefined();
2584 static bool ParseGCZealMode(JSContext
* cx
, const CallArgs
& args
,
2587 if (!ToUint32(cx
, args
.get(0), &value
)) {
2591 if (value
> uint32_t(gc::ZealMode::Limit
)) {
2592 JS_ReportErrorASCII(cx
, "gczeal argument out of range");
2596 *zeal
= static_cast<uint8_t>(value
);
2600 static bool GCZeal(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2601 CallArgs args
= CallArgsFromVp(argc
, vp
);
2603 if (args
.length() > 2) {
2604 RootedObject
callee(cx
, &args
.callee());
2605 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2609 if (args
.length() == 0) {
2610 uint32_t zealBits
, unused1
, unused2
;
2611 cx
->runtime()->gc
.getZealBits(&zealBits
, &unused1
, &unused2
);
2612 args
.rval().setNumber(zealBits
);
2617 if (!ParseGCZealMode(cx
, args
, &zeal
)) {
2621 uint32_t frequency
= JS::ShellDefaultGCZealFrequency
;
2622 if (args
.length() >= 2) {
2623 if (!ToUint32(cx
, args
.get(1), &frequency
)) {
2628 JS::SetGCZeal(cx
, zeal
, frequency
);
2629 args
.rval().setUndefined();
2633 static bool UnsetGCZeal(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2634 CallArgs args
= CallArgsFromVp(argc
, vp
);
2636 if (args
.length() > 1) {
2637 RootedObject
callee(cx
, &args
.callee());
2638 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2643 if (!ParseGCZealMode(cx
, args
, &zeal
)) {
2647 JS::UnsetGCZeal(cx
, zeal
);
2648 args
.rval().setUndefined();
2652 static bool ScheduleGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2653 CallArgs args
= CallArgsFromVp(argc
, vp
);
2655 if (args
.length() > 1) {
2656 RootedObject
callee(cx
, &args
.callee());
2657 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2661 if (args
.length() == 0) {
2662 /* Fetch next zeal trigger only. */
2663 } else if (args
[0].isNumber()) {
2664 /* Schedule a GC to happen after |arg| allocations. */
2665 JS::ScheduleGC(cx
, std::max(int(args
[0].toNumber()), 0));
2667 RootedObject
callee(cx
, &args
.callee());
2668 ReportUsageErrorASCII(cx
, callee
, "Bad argument - expecting number");
2675 JS::GetGCZealBits(cx
, &zealBits
, &freq
, &next
);
2676 args
.rval().setInt32(next
);
2680 static bool SelectForGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2681 CallArgs args
= CallArgsFromVp(argc
, vp
);
2684 * The selectedForMarking set is intended to be manually marked at slice
2685 * start to detect missing pre-barriers. It is invalid for nursery things
2686 * to be in the set, so evict the nursery before adding items.
2688 cx
->runtime()->gc
.evictNursery();
2690 for (unsigned i
= 0; i
< args
.length(); i
++) {
2691 if (args
[i
].isObject()) {
2692 if (!cx
->runtime()->gc
.selectForMarking(&args
[i
].toObject())) {
2698 args
.rval().setUndefined();
2702 static bool VerifyPreBarriers(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2703 CallArgs args
= CallArgsFromVp(argc
, vp
);
2705 if (args
.length() > 0) {
2706 RootedObject
callee(cx
, &args
.callee());
2707 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2711 gc::VerifyBarriers(cx
->runtime(), gc::PreBarrierVerifier
);
2712 args
.rval().setUndefined();
2716 static bool VerifyPostBarriers(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2717 // This is a no-op since the post barrier verifier was removed.
2718 CallArgs args
= CallArgsFromVp(argc
, vp
);
2719 if (args
.length()) {
2720 RootedObject
callee(cx
, &args
.callee());
2721 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2724 args
.rval().setUndefined();
2728 static bool CurrentGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2729 CallArgs args
= CallArgsFromVp(argc
, vp
);
2731 if (args
.length() != 0) {
2732 RootedObject
callee(cx
, &args
.callee());
2733 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2737 RootedObject
result(cx
, JS_NewPlainObject(cx
));
2742 js::gc::GCRuntime
& gc
= cx
->runtime()->gc
;
2743 const char* state
= StateName(gc
.state());
2745 RootedString
str(cx
, JS_NewStringCopyZ(cx
, state
));
2749 RootedValue
val(cx
, StringValue(str
));
2750 if (!JS_DefineProperty(cx
, result
, "incrementalState", val
,
2751 JSPROP_ENUMERATE
)) {
2755 if (gc
.state() == js::gc::State::Sweep
) {
2756 val
= Int32Value(gc
.getCurrentSweepGroupIndex());
2757 if (!JS_DefineProperty(cx
, result
, "sweepGroup", val
, JSPROP_ENUMERATE
)) {
2762 val
= BooleanValue(gc
.isIncrementalGCInProgress() && gc
.isShrinkingGC());
2763 if (!JS_DefineProperty(cx
, result
, "isShrinking", val
, JSPROP_ENUMERATE
)) {
2767 val
= Int32Value(gc
.gcNumber());
2768 if (!JS_DefineProperty(cx
, result
, "number", val
, JSPROP_ENUMERATE
)) {
2772 val
= Int32Value(gc
.minorGCCount());
2773 if (!JS_DefineProperty(cx
, result
, "minorCount", val
, JSPROP_ENUMERATE
)) {
2777 val
= Int32Value(gc
.majorGCCount());
2778 if (!JS_DefineProperty(cx
, result
, "majorCount", val
, JSPROP_ENUMERATE
)) {
2782 val
= BooleanValue(gc
.isFullGc());
2783 if (!JS_DefineProperty(cx
, result
, "isFull", val
, JSPROP_ENUMERATE
)) {
2787 val
= BooleanValue(gc
.isCompactingGc());
2788 if (!JS_DefineProperty(cx
, result
, "isCompacting", val
, JSPROP_ENUMERATE
)) {
2793 val
= Int32Value(gc
.testMarkQueuePos());
2794 if (!JS_DefineProperty(cx
, result
, "queuePos", val
, JSPROP_ENUMERATE
)) {
2799 args
.rval().setObject(*result
);
2803 static bool DeterministicGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2804 CallArgs args
= CallArgsFromVp(argc
, vp
);
2806 if (args
.length() != 1) {
2807 RootedObject
callee(cx
, &args
.callee());
2808 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2812 cx
->runtime()->gc
.setDeterministic(ToBoolean(args
[0]));
2813 args
.rval().setUndefined();
2817 static bool DumpGCArenaInfo(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2818 CallArgs args
= CallArgsFromVp(argc
, vp
);
2819 js::gc::DumpArenaInfo();
2820 args
.rval().setUndefined();
2824 static bool SetMarkStackLimit(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2825 CallArgs args
= CallArgsFromVp(argc
, vp
);
2826 if (args
.length() != 1) {
2827 RootedObject
callee(cx
, &args
.callee());
2828 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2833 if (!ToInt32(cx
, args
[0], &value
) || value
<= 0) {
2834 JS_ReportErrorASCII(cx
, "Bad argument to SetMarkStackLimit");
2838 if (JS::IsIncrementalGCInProgress(cx
)) {
2839 JS_ReportErrorASCII(
2840 cx
, "Attempt to set markStackLimit while a GC is in progress");
2844 JSRuntime
* runtime
= cx
->runtime();
2845 AutoLockGC
lock(runtime
);
2846 runtime
->gc
.setMarkStackLimit(value
, lock
);
2847 args
.rval().setUndefined();
2851 #endif /* JS_GC_ZEAL */
2853 static bool SetMallocMaxDirtyPageModifier(JSContext
* cx
, unsigned argc
,
2855 CallArgs args
= CallArgsFromVp(argc
, vp
);
2856 if (args
.length() != 1) {
2857 RootedObject
callee(cx
, &args
.callee());
2858 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2862 constexpr int32_t MinSupportedValue
= -5;
2863 constexpr int32_t MaxSupportedValue
= 16;
2866 if (!ToInt32(cx
, args
[0], &value
)) {
2869 if (value
< MinSupportedValue
|| value
> MaxSupportedValue
) {
2870 JS_ReportErrorASCII(cx
, "Bad argument to setMallocMaxDirtyPageModifier");
2874 moz_set_max_dirty_page_modifier(value
);
2876 args
.rval().setUndefined();
2880 static bool GCState(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2881 CallArgs args
= CallArgsFromVp(argc
, vp
);
2883 if (args
.length() > 1) {
2884 RootedObject
callee(cx
, &args
.callee());
2885 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2891 if (args
.length() == 1) {
2892 if (!args
[0].isObject()) {
2893 RootedObject
callee(cx
, &args
.callee());
2894 ReportUsageErrorASCII(cx
, callee
, "Expected object");
2898 JSObject
* obj
= UncheckedUnwrap(&args
[0].toObject());
2899 state
= gc::StateName(obj
->zone()->gcState());
2901 state
= gc::StateName(cx
->runtime()->gc
.state());
2904 return ReturnStringCopy(cx
, args
, state
);
2907 static bool ScheduleZoneForGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2908 CallArgs args
= CallArgsFromVp(argc
, vp
);
2910 if (args
.length() != 1) {
2911 RootedObject
callee(cx
, &args
.callee());
2912 ReportUsageErrorASCII(cx
, callee
, "Expecting a single argument");
2916 if (args
[0].isObject()) {
2917 // Ensure that |zone| is collected during the next GC.
2918 Zone
* zone
= UncheckedUnwrap(&args
[0].toObject())->zone();
2919 PrepareZoneForGC(cx
, zone
);
2920 } else if (args
[0].isString()) {
2921 // This allows us to schedule the atoms zone for GC.
2922 Zone
* zone
= args
[0].toString()->zoneFromAnyThread();
2923 if (!CurrentThreadCanAccessZone(zone
)) {
2924 RootedObject
callee(cx
, &args
.callee());
2925 ReportUsageErrorASCII(cx
, callee
, "Specified zone not accessible for GC");
2928 PrepareZoneForGC(cx
, zone
);
2930 RootedObject
callee(cx
, &args
.callee());
2931 ReportUsageErrorASCII(cx
, callee
,
2932 "Bad argument - expecting object or string");
2936 args
.rval().setUndefined();
2940 static bool StartGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2941 CallArgs args
= CallArgsFromVp(argc
, vp
);
2943 if (args
.length() > 2) {
2944 RootedObject
callee(cx
, &args
.callee());
2945 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2949 auto budget
= SliceBudget::unlimited();
2950 if (args
.length() >= 1) {
2952 if (!ToUint32(cx
, args
[0], &work
)) {
2955 budget
= SliceBudget(WorkBudget(work
));
2958 bool shrinking
= false;
2959 if (args
.length() >= 2) {
2960 Value arg
= args
[1];
2961 if (arg
.isString()) {
2962 if (!JS_StringEqualsLiteral(cx
, arg
.toString(), "shrinking",
2969 JSRuntime
* rt
= cx
->runtime();
2970 if (rt
->gc
.isIncrementalGCInProgress()) {
2971 RootedObject
callee(cx
, &args
.callee());
2972 JS_ReportErrorASCII(cx
, "Incremental GC already in progress");
2976 JS::GCOptions options
=
2977 shrinking
? JS::GCOptions::Shrink
: JS::GCOptions::Normal
;
2978 rt
->gc
.startDebugGC(options
, budget
);
2980 args
.rval().setUndefined();
2984 static bool FinishGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2985 CallArgs args
= CallArgsFromVp(argc
, vp
);
2987 if (args
.length() > 0) {
2988 RootedObject
callee(cx
, &args
.callee());
2989 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2993 JSRuntime
* rt
= cx
->runtime();
2994 if (rt
->gc
.isIncrementalGCInProgress()) {
2995 rt
->gc
.finishGC(JS::GCReason::DEBUG_GC
);
2998 args
.rval().setUndefined();
3002 static bool GCSlice(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3003 CallArgs args
= CallArgsFromVp(argc
, vp
);
3005 if (args
.length() > 2) {
3006 RootedObject
callee(cx
, &args
.callee());
3007 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
3011 auto budget
= SliceBudget::unlimited();
3012 if (args
.length() >= 1) {
3014 if (!ToUint32(cx
, args
[0], &work
)) {
3017 budget
= SliceBudget(WorkBudget(work
));
3020 bool dontStart
= false;
3021 if (args
.get(1).isObject()) {
3022 RootedObject
options(cx
, &args
[1].toObject());
3024 if (!JS_GetProperty(cx
, options
, "dontStart", &v
)) {
3027 dontStart
= ToBoolean(v
);
3030 JSRuntime
* rt
= cx
->runtime();
3031 if (rt
->gc
.isIncrementalGCInProgress()) {
3032 rt
->gc
.debugGCSlice(budget
);
3033 } else if (!dontStart
) {
3034 rt
->gc
.startDebugGC(JS::GCOptions::Normal
, budget
);
3037 args
.rval().setUndefined();
3041 static bool AbortGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3042 CallArgs args
= CallArgsFromVp(argc
, vp
);
3044 if (args
.length() != 0) {
3045 RootedObject
callee(cx
, &args
.callee());
3046 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
3050 JS::AbortIncrementalGC(cx
);
3051 args
.rval().setUndefined();
3055 static bool FullCompartmentChecks(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3056 CallArgs args
= CallArgsFromVp(argc
, vp
);
3058 if (args
.length() != 1) {
3059 RootedObject
callee(cx
, &args
.callee());
3060 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
3064 cx
->runtime()->gc
.setFullCompartmentChecks(ToBoolean(args
[0]));
3065 args
.rval().setUndefined();
3069 static bool NondeterministicGetWeakMapKeys(JSContext
* cx
, unsigned argc
,
3071 CallArgs args
= CallArgsFromVp(argc
, vp
);
3073 if (args
.length() != 1) {
3074 RootedObject
callee(cx
, &args
.callee());
3075 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
3078 if (!args
[0].isObject()) {
3079 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3080 JSMSG_NOT_EXPECTED_TYPE
,
3081 "nondeterministicGetWeakMapKeys", "WeakMap",
3082 InformalValueTypeName(args
[0]));
3085 RootedObject
arr(cx
);
3086 RootedObject
mapObj(cx
, &args
[0].toObject());
3087 if (!JS_NondeterministicGetWeakMapKeys(cx
, mapObj
, &arr
)) {
3091 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
3092 JSMSG_NOT_EXPECTED_TYPE
,
3093 "nondeterministicGetWeakMapKeys", "WeakMap",
3094 args
[0].toObject().getClass()->name
);
3097 args
.rval().setObject(*arr
);
3101 class HasChildTracer final
: public JS::CallbackTracer
{
3105 void onChild(JS::GCCellPtr thing
, const char* name
) override
{
3106 if (thing
.asCell() == child_
.toGCThing()) {
3112 HasChildTracer(JSContext
* cx
, HandleValue child
)
3113 : JS::CallbackTracer(cx
, JS::TracerKind::Callback
,
3114 JS::WeakMapTraceAction::TraceKeysAndValues
),
3118 bool found() const { return found_
; }
3121 static bool HasChild(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3122 CallArgs args
= CallArgsFromVp(argc
, vp
);
3123 RootedValue
parent(cx
, args
.get(0));
3124 RootedValue
child(cx
, args
.get(1));
3126 if (!parent
.isGCThing() || !child
.isGCThing()) {
3127 args
.rval().setBoolean(false);
3131 HasChildTracer
trc(cx
, child
);
3132 TraceChildren(&trc
, JS::GCCellPtr(parent
.toGCThing(), parent
.traceKind()));
3133 args
.rval().setBoolean(trc
.found());
3137 static bool SetSavedStacksRNGState(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3138 CallArgs args
= CallArgsFromVp(argc
, vp
);
3139 if (!args
.requireAtLeast(cx
, "setSavedStacksRNGState", 1)) {
3144 if (!ToInt32(cx
, args
[0], &seed
)) {
3148 // Either one or the other of the seed arguments must be non-zero;
3149 // make this true no matter what value 'seed' has.
3150 cx
->realm()->savedStacks().setRNGState(seed
, (seed
+ 1) * 33);
3154 static bool GetSavedFrameCount(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3155 CallArgs args
= CallArgsFromVp(argc
, vp
);
3156 args
.rval().setNumber(cx
->realm()->savedStacks().count());
3160 static bool ClearSavedFrames(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3161 CallArgs args
= CallArgsFromVp(argc
, vp
);
3163 js::SavedStacks
& savedStacks
= cx
->realm()->savedStacks();
3164 savedStacks
.clear();
3166 for (ActivationIterator
iter(cx
); !iter
.done(); ++iter
) {
3167 iter
->clearLiveSavedFrameCache();
3170 args
.rval().setUndefined();
3174 static bool SaveStack(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3175 CallArgs args
= CallArgsFromVp(argc
, vp
);
3177 JS::StackCapture
capture((JS::AllFrames()));
3178 if (args
.length() >= 1) {
3180 if (!ToNumber(cx
, args
[0], &maxDouble
)) {
3183 if (std::isnan(maxDouble
) || maxDouble
< 0 || maxDouble
> UINT32_MAX
) {
3184 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[0],
3185 nullptr, "not a valid maximum frame count");
3188 uint32_t max
= uint32_t(maxDouble
);
3190 capture
= JS::StackCapture(JS::MaxFrames(max
));
3194 RootedObject
compartmentObject(cx
);
3195 if (args
.length() >= 2) {
3196 if (!args
[1].isObject()) {
3197 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[0],
3198 nullptr, "not an object");
3201 compartmentObject
= UncheckedUnwrap(&args
[1].toObject());
3202 if (!compartmentObject
) {
3207 RootedObject
stack(cx
);
3209 Maybe
<AutoRealm
> ar
;
3210 if (compartmentObject
) {
3211 ar
.emplace(cx
, compartmentObject
);
3213 if (!JS::CaptureCurrentStack(cx
, &stack
, std::move(capture
))) {
3218 if (stack
&& !cx
->compartment()->wrap(cx
, &stack
)) {
3222 args
.rval().setObjectOrNull(stack
);
3226 static bool CaptureFirstSubsumedFrame(JSContext
* cx
, unsigned argc
,
3228 CallArgs args
= CallArgsFromVp(argc
, vp
);
3229 if (!args
.requireAtLeast(cx
, "captureFirstSubsumedFrame", 1)) {
3233 if (!args
[0].isObject()) {
3234 JS_ReportErrorASCII(cx
, "The argument must be an object");
3238 RootedObject
obj(cx
, &args
[0].toObject());
3239 obj
= CheckedUnwrapStatic(obj
);
3241 JS_ReportErrorASCII(cx
, "Denied permission to object.");
3245 JS::StackCapture
capture(
3246 JS::FirstSubsumedFrame(cx
, obj
->nonCCWRealm()->principals()));
3247 if (args
.length() > 1) {
3248 capture
.as
<JS::FirstSubsumedFrame
>().ignoreSelfHosted
=
3249 JS::ToBoolean(args
[1]);
3252 JS::RootedObject
capturedStack(cx
);
3253 if (!JS::CaptureCurrentStack(cx
, &capturedStack
, std::move(capture
))) {
3257 args
.rval().setObjectOrNull(capturedStack
);
3261 static bool CallFunctionFromNativeFrame(JSContext
* cx
, unsigned argc
,
3263 CallArgs args
= CallArgsFromVp(argc
, vp
);
3265 if (args
.length() != 1) {
3266 JS_ReportErrorASCII(cx
, "The function takes exactly one argument.");
3269 if (!args
[0].isObject() || !IsCallable(args
[0])) {
3270 JS_ReportErrorASCII(cx
, "The first argument should be a function.");
3274 RootedObject
function(cx
, &args
[0].toObject());
3275 return Call(cx
, UndefinedHandleValue
, function
, JS::HandleValueArray::empty(),
3279 static bool CallFunctionWithAsyncStack(JSContext
* cx
, unsigned argc
,
3281 CallArgs args
= CallArgsFromVp(argc
, vp
);
3283 if (args
.length() != 3) {
3284 JS_ReportErrorASCII(cx
, "The function takes exactly three arguments.");
3287 if (!args
[0].isObject() || !IsCallable(args
[0])) {
3288 JS_ReportErrorASCII(cx
, "The first argument should be a function.");
3291 if (!args
[1].isObject() || !args
[1].toObject().is
<SavedFrame
>()) {
3292 JS_ReportErrorASCII(cx
, "The second argument should be a SavedFrame.");
3295 if (!args
[2].isString() || args
[2].toString()->empty()) {
3296 JS_ReportErrorASCII(cx
, "The third argument should be a non-empty string.");
3300 RootedObject
function(cx
, &args
[0].toObject());
3301 RootedObject
stack(cx
, &args
[1].toObject());
3302 RootedString
asyncCause(cx
, args
[2].toString());
3303 UniqueChars utf8Cause
= JS_EncodeStringToUTF8(cx
, asyncCause
);
3305 MOZ_ASSERT(cx
->isExceptionPending());
3309 JS::AutoSetAsyncStackForNewCalls
sas(
3310 cx
, stack
, utf8Cause
.get(),
3311 JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT
);
3312 return Call(cx
, UndefinedHandleValue
, function
, JS::HandleValueArray::empty(),
3316 static bool EnableTrackAllocations(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3317 SetAllocationMetadataBuilder(cx
, &SavedStacks::metadataBuilder
);
3321 static bool DisableTrackAllocations(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3322 SetAllocationMetadataBuilder(cx
, nullptr);
3326 static bool SetTestFilenameValidationCallback(JSContext
* cx
, unsigned argc
,
3328 CallArgs args
= CallArgsFromVp(argc
, vp
);
3330 // Accept all filenames that start with "safe". In system code also accept
3331 // filenames starting with "system".
3332 auto testCb
= [](JSContext
* cx
, const char* filename
) -> bool {
3333 if (strstr(filename
, "safe") == filename
) {
3336 if (cx
->realm()->isSystem() && strstr(filename
, "system") == filename
) {
3342 JS::SetFilenameValidationCallback(testCb
);
3344 args
.rval().setUndefined();
3348 static JSAtom
* GetPropertiesAddedName(JSContext
* cx
) {
3349 const char* propName
= "_propertiesAdded";
3350 return Atomize(cx
, propName
, strlen(propName
));
3353 static bool NewObjectWithAddPropertyHook(JSContext
* cx
, unsigned argc
,
3355 CallArgs args
= CallArgsFromVp(argc
, vp
);
3357 auto addPropHook
= [](JSContext
* cx
, HandleObject obj
, HandleId id
,
3358 HandleValue v
) -> bool {
3359 Rooted
<JSAtom
*> propName(cx
, GetPropertiesAddedName(cx
));
3363 // Don't do anything if we're adding the _propertiesAdded property.
3364 RootedId
propId(cx
, AtomToId(propName
));
3368 // Increment _propertiesAdded.
3369 RootedValue
val(cx
);
3370 if (!JS_GetPropertyById(cx
, obj
, propId
, &val
)) {
3373 if (!val
.isInt32() || val
.toInt32() == INT32_MAX
) {
3376 val
.setInt32(val
.toInt32() + 1);
3377 return JS_DefinePropertyById(cx
, obj
, propId
, val
, 0);
3380 static const JSClassOps classOps
= {
3381 addPropHook
, // addProperty
3382 nullptr, // delProperty
3383 nullptr, // enumerate
3384 nullptr, // newEnumerate
3386 nullptr, // mayResolve
3387 nullptr, // finalize
3389 nullptr, // construct
3392 static const JSClass cls
= {
3393 "ObjectWithAddPropHook",
3398 RootedObject
obj(cx
, JS_NewObject(cx
, &cls
));
3403 // Initialize _propertiesAdded to 0.
3404 Rooted
<JSAtom
*> propName(cx
, GetPropertiesAddedName(cx
));
3408 RootedId
propId(cx
, AtomToId(propName
));
3409 RootedValue
val(cx
, Int32Value(0));
3410 if (!JS_DefinePropertyById(cx
, obj
, propId
, val
, 0)) {
3414 args
.rval().setObject(*obj
);
3418 static bool NewObjectWithCallHook(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3419 CallArgs args
= CallArgsFromVp(argc
, vp
);
3421 static auto hookShared
= [](JSContext
* cx
, CallArgs
& args
) {
3422 Rooted
<PlainObject
*> obj(cx
, NewPlainObject(cx
));
3427 // Define |this|. We can't expose the MagicValue to JS, so we use
3428 // "<is_constructing>" in that case.
3429 Rooted
<Value
> thisv(cx
, args
.thisv());
3430 if (thisv
.isMagic(JS_IS_CONSTRUCTING
)) {
3431 JSString
* str
= NewStringCopyZ
<CanGC
>(cx
, "<is_constructing>");
3435 thisv
.setString(str
);
3437 if (!DefineDataProperty(cx
, obj
, cx
->names().this_
, thisv
,
3438 JSPROP_ENUMERATE
)) {
3443 if (!DefineDataProperty(cx
, obj
, cx
->names().callee
, args
.calleev(),
3444 JSPROP_ENUMERATE
)) {
3448 // Define |arguments| array.
3449 Rooted
<ArrayObject
*> arr(
3450 cx
, NewDenseCopiedArray(cx
, args
.length(), args
.array()));
3454 Rooted
<Value
> arrVal(cx
, ObjectValue(*arr
));
3455 if (!DefineDataProperty(cx
, obj
, cx
->names().arguments
, arrVal
,
3456 JSPROP_ENUMERATE
)) {
3460 // Define |newTarget| if constructing.
3461 if (args
.isConstructing()) {
3462 const char* propName
= "newTarget";
3463 Rooted
<JSAtom
*> name(cx
, Atomize(cx
, propName
, strlen(propName
)));
3467 Rooted
<PropertyKey
> key(cx
, NameToId(name
->asPropertyName()));
3468 if (!DefineDataProperty(cx
, obj
, key
, args
.newTarget(),
3469 JSPROP_ENUMERATE
)) {
3474 args
.rval().setObject(*obj
);
3478 static auto callHook
= [](JSContext
* cx
, unsigned argc
, Value
* vp
) {
3479 CallArgs args
= CallArgsFromVp(argc
, vp
);
3480 MOZ_ASSERT(!args
.isConstructing());
3481 return hookShared(cx
, args
);
3483 static auto constructHook
= [](JSContext
* cx
, unsigned argc
, Value
* vp
) {
3484 CallArgs args
= CallArgsFromVp(argc
, vp
);
3485 MOZ_ASSERT(args
.isConstructing());
3486 return hookShared(cx
, args
);
3489 static const JSClassOps classOps
= {
3490 nullptr, // addProperty
3491 nullptr, // delProperty
3492 nullptr, // enumerate
3493 nullptr, // newEnumerate
3495 nullptr, // mayResolve
3496 nullptr, // finalize
3498 constructHook
, // construct
3501 static const JSClass cls
= {
3502 "ObjectWithCallHook",
3507 Rooted
<JSObject
*> obj(cx
, JS_NewObject(cx
, &cls
));
3512 args
.rval().setObject(*obj
);
3516 static constexpr JSClass ObjectWithManyReservedSlotsClass
= {
3517 "ObjectWithManyReservedSlots",
3518 JSCLASS_HAS_RESERVED_SLOTS(40),
3521 static bool NewObjectWithManyReservedSlots(JSContext
* cx
, unsigned argc
,
3523 CallArgs args
= CallArgsFromVp(argc
, vp
);
3525 static constexpr size_t NumReservedSlots
=
3526 JSCLASS_RESERVED_SLOTS(&ObjectWithManyReservedSlotsClass
);
3527 static_assert(NumReservedSlots
> NativeObject::MAX_FIXED_SLOTS
);
3529 RootedObject
obj(cx
, JS_NewObject(cx
, &ObjectWithManyReservedSlotsClass
));
3534 for (size_t i
= 0; i
< NumReservedSlots
; i
++) {
3535 JS_SetReservedSlot(obj
, i
, Int32Value(i
));
3538 args
.rval().setObject(*obj
);
3542 static bool CheckObjectWithManyReservedSlots(JSContext
* cx
, unsigned argc
,
3544 CallArgs args
= CallArgsFromVp(argc
, vp
);
3546 if (args
.length() != 1 || !args
[0].isObject() ||
3547 args
[0].toObject().getClass() != &ObjectWithManyReservedSlotsClass
) {
3548 JS_ReportErrorASCII(cx
,
3549 "Expected object from newObjectWithManyReservedSlots");
3553 JSObject
* obj
= &args
[0].toObject();
3555 static constexpr size_t NumReservedSlots
=
3556 JSCLASS_RESERVED_SLOTS(&ObjectWithManyReservedSlotsClass
);
3558 for (size_t i
= 0; i
< NumReservedSlots
; i
++) {
3559 MOZ_RELEASE_ASSERT(JS::GetReservedSlot(obj
, i
).toInt32() == int32_t(i
));
3562 args
.rval().setUndefined();
3566 static bool GetWatchtowerLog(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3567 CallArgs args
= CallArgsFromVp(argc
, vp
);
3569 Rooted
<GCVector
<Value
>> values(cx
, GCVector
<Value
>(cx
));
3571 if (auto* log
= cx
->runtime()->watchtowerTestingLog
.ref().get()) {
3572 Rooted
<JSObject
*> elem(cx
);
3573 for (PlainObject
* obj
: *log
) {
3575 if (!cx
->compartment()->wrap(cx
, &elem
)) {
3578 if (!values
.append(ObjectValue(*elem
))) {
3582 log
->clearAndFree();
3585 ArrayObject
* arr
= NewDenseCopiedArray(cx
, values
.length(), values
.begin());
3590 args
.rval().setObject(*arr
);
3594 static bool AddWatchtowerTarget(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3595 CallArgs args
= CallArgsFromVp(argc
, vp
);
3597 if (args
.length() != 1 || !args
[0].isObject()) {
3598 JS_ReportErrorASCII(cx
, "Expected a single object argument.");
3602 if (!cx
->runtime()->watchtowerTestingLog
.ref()) {
3603 auto vec
= cx
->make_unique
<JSRuntime::RootedPlainObjVec
>(cx
);
3607 cx
->runtime()->watchtowerTestingLog
= std::move(vec
);
3610 RootedObject
obj(cx
, &args
[0].toObject());
3611 if (!JSObject::setUseWatchtowerTestingLog(cx
, obj
)) {
3615 args
.rval().setUndefined();
3619 struct TestExternalString
: public JSExternalStringCallbacks
{
3620 void finalize(JS::Latin1Char
* chars
) const override
{ js_free(chars
); }
3621 void finalize(char16_t
* chars
) const override
{ js_free(chars
); }
3622 size_t sizeOfBuffer(const JS::Latin1Char
* chars
,
3623 mozilla::MallocSizeOf mallocSizeOf
) const override
{
3624 return mallocSizeOf(chars
);
3626 size_t sizeOfBuffer(const char16_t
* chars
,
3627 mozilla::MallocSizeOf mallocSizeOf
) const override
{
3628 return mallocSizeOf(chars
);
3632 static constexpr TestExternalString TestExternalStringCallbacks
;
3634 static bool NewString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3635 CallArgs args
= CallArgsFromVp(argc
, vp
);
3637 RootedString
src(cx
, ToString(cx
, args
.get(0)));
3642 gc::Heap heap
= gc::Heap::Default
;
3643 bool wantTwoByte
= false;
3644 bool forceExternal
= false;
3645 bool maybeExternal
= false;
3646 bool newStringBuffer
= false;
3647 bool shareStringBuffer
= false;
3648 uint32_t capacity
= 0;
3650 if (args
.get(1).isObject()) {
3651 RootedObject
options(cx
, &args
[1].toObject());
3653 bool requestTenured
= false;
3654 struct BoolSetting
{
3658 for (auto [name
, setting
] :
3659 {BoolSetting
{"tenured", &requestTenured
},
3660 BoolSetting
{"twoByte", &wantTwoByte
},
3661 BoolSetting
{"external", &forceExternal
},
3662 BoolSetting
{"maybeExternal", &maybeExternal
},
3663 BoolSetting
{"newStringBuffer", &newStringBuffer
},
3664 BoolSetting
{"shareStringBuffer", &shareStringBuffer
}}) {
3665 if (!JS_GetProperty(cx
, options
, name
, &v
)) {
3668 *setting
= ToBoolean(v
); // false if not given (or otherwise undefined)
3670 struct Uint32Setting
{
3674 for (auto [name
, setting
] : {Uint32Setting
{"capacity", &capacity
}}) {
3675 if (!JS_GetProperty(cx
, options
, name
, &v
)) {
3679 if (!ToInt32(cx
, v
, &i32
)) {
3683 JS_ReportErrorASCII(cx
, "nonnegative value required");
3686 *setting
= static_cast<uint32_t>(i32
);
3689 heap
= requestTenured
? gc::Heap::Tenured
: gc::Heap::Default
;
3690 if (forceExternal
|| maybeExternal
) {
3693 unsigned kinds
= forceExternal
+ maybeExternal
+ (capacity
!= 0) +
3694 newStringBuffer
+ shareStringBuffer
;
3696 JS_ReportErrorASCII(cx
,
3697 "external, capacity, and stringBuffer options can "
3698 "not be used at the same time");
3703 auto len
= src
->length();
3704 RootedString
dest(cx
);
3706 if (forceExternal
|| maybeExternal
) {
3707 auto buf
= cx
->make_pod_array
<char16_t
>(len
);
3712 if (!JS_CopyStringChars(cx
, mozilla::Range
<char16_t
>(buf
.get(), len
),
3717 bool isExternal
= true;
3718 if (forceExternal
) {
3719 dest
= JSExternalString::new_(cx
, buf
.get(), len
,
3720 &TestExternalStringCallbacks
);
3722 dest
= NewMaybeExternalString(
3723 cx
, buf
.get(), len
, &TestExternalStringCallbacks
, &isExternal
, heap
);
3725 if (dest
&& isExternal
) {
3726 (void)buf
.release(); // Ownership was transferred.
3728 } else if (shareStringBuffer
) {
3729 if (!src
->isLinear() || !src
->asLinear().hasStringBuffer()) {
3730 JS_ReportErrorASCII(cx
, "source string must have a string buffer");
3733 RefPtr
<mozilla::StringBuffer
> buffer
= src
->asLinear().stringBuffer();
3734 if (src
->hasLatin1Chars()) {
3735 Rooted
<JSString::OwnedChars
<Latin1Char
>> owned(cx
, std::move(buffer
),
3738 JSLinearString::newValidLength
<CanGC
, Latin1Char
>(cx
, &owned
, heap
);
3740 Rooted
<JSString::OwnedChars
<char16_t
>> owned(cx
, std::move(buffer
), len
);
3741 dest
= JSLinearString::newValidLength
<CanGC
, char16_t
>(cx
, &owned
, heap
);
3744 AutoStableStringChars
stable(cx
);
3745 if (!wantTwoByte
&& src
->hasLatin1Chars()) {
3746 if (!stable
.init(cx
, src
)) {
3750 if (!stable
.initTwoByte(cx
, src
)) {
3754 if (newStringBuffer
) {
3755 auto allocString
= [&](const auto* chars
) -> JSLinearString
* {
3757 std::remove_const_t
<std::remove_pointer_t
<decltype(chars
)>>;
3759 if (JSInlineString::lengthFits
<CharT
>(len
)) {
3760 JS_ReportErrorASCII(cx
, "Cannot create small non-inline strings");
3764 RefPtr
<mozilla::StringBuffer
> buffer
=
3765 mozilla::StringBuffer::Create(chars
, len
);
3767 ReportOutOfMemory(cx
);
3771 Rooted
<JSString::OwnedChars
<CharT
>> owned(cx
, std::move(buffer
), len
);
3772 return JSLinearString::newValidLength
<CanGC
, CharT
>(cx
, &owned
, heap
);
3774 if (stable
.isLatin1()) {
3775 dest
= allocString(stable
.latin1Chars());
3777 dest
= allocString(stable
.twoByteChars());
3779 } else if (capacity
) {
3780 if (capacity
< len
) {
3784 JS_ReportErrorASCII(cx
, "Cannot set capacity of empty string");
3788 auto createLinearString
= [&](const auto* chars
) -> JSLinearString
* {
3790 std::remove_const_t
<std::remove_pointer_t
<decltype(chars
)>>;
3792 if (JSInlineString::lengthFits
<CharT
>(len
)) {
3793 JS_ReportErrorASCII(cx
, "Cannot create small non-inline strings");
3798 cx
->make_pod_arena_array
<CharT
>(js::StringBufferArena
, capacity
);
3802 mozilla::PodCopy(news
.get(), chars
, len
);
3803 Rooted
<JSString::OwnedChars
<CharT
>> owned(cx
, std::move(news
), len
);
3804 return JSLinearString::newValidLength
<CanGC
, CharT
>(cx
, &owned
, heap
);
3807 if (stable
.isLatin1()) {
3808 dest
= createLinearString(stable
.latin1Chars());
3810 dest
= createLinearString(stable
.twoByteChars());
3813 dest
->asLinear().makeExtensible(capacity
);
3815 } else if (wantTwoByte
) {
3816 dest
= NewStringCopyNDontDeflate
<CanGC
>(cx
, stable
.twoByteChars(), len
,
3818 } else if (stable
.isLatin1()) {
3819 dest
= NewStringCopyN
<CanGC
>(cx
, stable
.latin1Chars(), len
, heap
);
3821 // Normal behavior: auto-deflate to latin1 if possible.
3822 dest
= NewStringCopyN
<CanGC
>(cx
, stable
.twoByteChars(), len
, heap
);
3830 args
.rval().setString(dest
);
3834 static bool NewDependentString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3835 CallArgs args
= CallArgsFromVp(argc
, vp
);
3837 RootedString
src(cx
, ToString(cx
, args
.get(0)));
3842 uint64_t indexStart
= 0;
3843 mozilla::Maybe
<uint64_t> indexEnd
;
3844 gc::Heap heap
= gc::Heap::Default
;
3845 mozilla::Maybe
<gc::Heap
> requiredHeap
;
3847 if (!ToIndex(cx
, args
.get(1), &indexStart
)) {
3851 Rooted
<Value
> options(cx
);
3852 if (args
.get(2).isObject()) {
3856 if (args
.hasDefined(2)) {
3857 if (!ToIndex(cx
, args
.get(2), &idx
)) {
3860 indexEnd
.emplace(idx
);
3862 options
= args
.get(3);
3865 if (options
.isObject()) {
3866 Rooted
<Value
> v(cx
);
3867 Rooted
<JSObject
*> optObj(cx
, &options
.toObject());
3868 if (!JS_GetProperty(cx
, optObj
, "tenured", &v
)) {
3871 if (v
.isBoolean()) {
3872 requiredHeap
.emplace(v
.toBoolean() ? gc::Heap::Tenured
3873 : gc::Heap::Default
);
3874 heap
= *requiredHeap
;
3878 if (indexEnd
.isNothing()) {
3879 // Read the length now that no more JS code can run.
3880 indexEnd
.emplace(src
->length());
3882 if (indexStart
> src
->length() || *indexEnd
> src
->length() ||
3883 indexStart
>= *indexEnd
) {
3884 JS_ReportErrorASCII(cx
, "invalid dependent string bounds");
3887 if (!src
->ensureLinear(cx
)) {
3890 Rooted
<JSString
*> result(
3891 cx
, js::NewDependentString(cx
, src
, indexStart
, *indexEnd
- indexStart
,
3896 if (!result
->isDependent()) {
3897 JS_ReportErrorASCII(cx
, "resulting string is not dependent (too short?)");
3901 if (requiredHeap
.isSome()) {
3902 if ((*requiredHeap
== gc::Heap::Tenured
) != result
->isTenured()) {
3903 if (result
->isTenured()) {
3904 JS_ReportErrorASCII(cx
, "nursery string created in tenured heap");
3907 JS_ReportErrorASCII(cx
, "tenured string created in nursery heap");
3913 args
.rval().setString(result
);
3917 // Warning! This will let you create ropes that I'm not sure would be possible
3918 // otherwise, specifically:
3920 // - a rope with a zero-length child
3921 // - a rope that would fit into an inline string
3923 static bool NewRope(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3924 CallArgs args
= CallArgsFromVp(argc
, vp
);
3926 if (!args
.get(0).isString() || !args
.get(1).isString()) {
3927 JS_ReportErrorASCII(cx
, "newRope requires two string arguments.");
3931 gc::Heap heap
= js::gc::Heap::Default
;
3932 if (args
.get(2).isObject()) {
3933 RootedObject
options(cx
, &args
[2].toObject());
3935 if (!JS_GetProperty(cx
, options
, "nursery", &v
)) {
3938 if (!v
.isUndefined() && !ToBoolean(v
)) {
3939 heap
= js::gc::Heap::Tenured
;
3943 RootedString
left(cx
, args
[0].toString());
3944 RootedString
right(cx
, args
[1].toString());
3945 size_t length
= JS_GetStringLength(left
) + JS_GetStringLength(right
);
3946 if (length
> JSString::MAX_LENGTH
) {
3947 JS_ReportErrorASCII(cx
, "rope length exceeds maximum string length");
3951 // Disallow creating ropes where one side is empty.
3952 if (left
->empty() || right
->empty()) {
3953 JS_ReportErrorASCII(cx
, "rope child mustn't be the empty string");
3957 // Disallow creating ropes which fit into inline strings.
3958 if (left
->hasLatin1Chars() && right
->hasLatin1Chars()) {
3959 if (JSInlineString::lengthFits
<JS::Latin1Char
>(length
)) {
3960 JS_ReportErrorASCII(cx
, "Cannot create small non-inline ropes");
3964 if (JSInlineString::lengthFits
<char16_t
>(length
)) {
3965 JS_ReportErrorASCII(cx
, "Cannot create small non-inline ropes");
3970 auto* str
= JSRope::new_
<CanGC
>(cx
, left
, right
, length
, heap
);
3975 args
.rval().setString(str
);
3979 static bool IsRope(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3980 CallArgs args
= CallArgsFromVp(argc
, vp
);
3982 if (!args
.get(0).isString()) {
3983 JS_ReportErrorASCII(cx
, "isRope requires a string argument.");
3987 JSString
* str
= args
[0].toString();
3988 args
.rval().setBoolean(str
->isRope());
3992 static bool EnsureLinearString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3993 CallArgs args
= CallArgsFromVp(argc
, vp
);
3995 if (args
.length() != 1 || !args
[0].isString()) {
3996 JS_ReportErrorASCII(
3997 cx
, "ensureLinearString takes exactly one string argument.");
4001 JSLinearString
* linear
= args
[0].toString()->ensureLinear(cx
);
4006 args
.rval().setString(linear
);
4010 static bool RepresentativeStringArray(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4011 CallArgs args
= CallArgsFromVp(argc
, vp
);
4013 RootedObject
array(cx
, JS::NewArrayObject(cx
, 0));
4018 if (!JSString::fillWithRepresentatives(cx
, array
.as
<ArrayObject
>())) {
4022 args
.rval().setObject(*array
);
4026 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
4028 static bool OOMThreadTypes(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4029 CallArgs args
= CallArgsFromVp(argc
, vp
);
4030 args
.rval().setInt32(js::THREAD_TYPE_MAX
);
4034 static bool CheckCanSimulateOOM(JSContext
* cx
) {
4035 if (js::oom::GetThreadType() != js::THREAD_TYPE_MAIN
) {
4036 JS_ReportErrorASCII(
4037 cx
, "Simulated OOM failure is only supported on the main thread");
4044 static bool SetupOOMFailure(JSContext
* cx
, bool failAlways
, unsigned argc
,
4046 CallArgs args
= CallArgsFromVp(argc
, vp
);
4048 if (disableOOMFunctions
) {
4049 args
.rval().setUndefined();
4053 if (args
.length() < 1) {
4054 JS_ReportErrorASCII(cx
, "Count argument required");
4058 if (args
.length() > 2) {
4059 JS_ReportErrorASCII(cx
, "Too many arguments");
4064 if (!JS::ToInt32(cx
, args
.get(0), &count
)) {
4069 JS_ReportErrorASCII(cx
, "OOM cutoff should be positive");
4073 uint32_t targetThread
= js::THREAD_TYPE_MAIN
;
4074 if (args
.length() > 1 && !ToUint32(cx
, args
[1], &targetThread
)) {
4078 if (targetThread
== js::THREAD_TYPE_NONE
||
4079 targetThread
== js::THREAD_TYPE_WORKER
||
4080 targetThread
>= js::THREAD_TYPE_MAX
) {
4081 JS_ReportErrorASCII(cx
, "Invalid thread type specified");
4085 if (!CheckCanSimulateOOM(cx
)) {
4089 js::oom::simulator
.simulateFailureAfter(js::oom::FailureSimulator::Kind::OOM
,
4090 count
, targetThread
, failAlways
);
4091 args
.rval().setUndefined();
4095 static bool OOMAfterAllocations(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4096 return SetupOOMFailure(cx
, true, argc
, vp
);
4099 static bool OOMAtAllocation(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4100 return SetupOOMFailure(cx
, false, argc
, vp
);
4103 static bool ResetOOMFailure(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4104 CallArgs args
= CallArgsFromVp(argc
, vp
);
4106 if (!CheckCanSimulateOOM(cx
)) {
4110 args
.rval().setBoolean(js::oom::HadSimulatedOOM());
4111 js::oom::simulator
.reset();
4115 static size_t CountCompartments(JSContext
* cx
) {
4117 for (auto zone
: cx
->runtime()->gc
.zones()) {
4118 count
+= zone
->compartments().length();
4123 // Iterative failure testing: test a function by simulating failures at indexed
4124 // locations throughout the normal execution path and checking that the
4125 // resulting state of the environment is consistent with the error result.
4127 // For example, trigger OOM at every allocation point and test that the function
4128 // either recovers and succeeds or raises an exception and fails.
4130 class MOZ_STACK_CLASS IterativeFailureTest
{
4132 struct FailureSimulator
{
4133 virtual void setup(JSContext
* cx
) {}
4134 virtual void teardown(JSContext
* cx
) {}
4135 virtual void startSimulating(JSContext
* cx
, unsigned iteration
,
4136 unsigned thread
, bool keepFailing
) = 0;
4137 virtual bool stopSimulating() = 0;
4138 virtual void cleanup(JSContext
* cx
) {}
4141 IterativeFailureTest(JSContext
* cx
, FailureSimulator
& simulator
);
4142 bool initParams(const CallArgs
& args
);
4147 bool testThread(unsigned thread
);
4148 bool testIteration(unsigned thread
, unsigned iteration
,
4149 bool& failureWasSimulated
, MutableHandleValue exception
);
4153 JSContext
* const cx
;
4154 FailureSimulator
& simulator
;
4155 size_t compartmentCount
;
4157 // Test parameters set by initParams.
4158 RootedFunction testFunction
;
4159 unsigned threadStart
= 0;
4160 unsigned threadEnd
= 0;
4161 bool expectExceptionOnFailure
= true;
4162 bool keepFailing
= false;
4163 bool verbose
= false;
4166 bool RunIterativeFailureTest(
4167 JSContext
* cx
, const CallArgs
& args
,
4168 IterativeFailureTest::FailureSimulator
& simulator
) {
4169 IterativeFailureTest
test(cx
, simulator
);
4170 return test
.initParams(args
) && test
.test();
4173 IterativeFailureTest::IterativeFailureTest(JSContext
* cx
,
4174 FailureSimulator
& simulator
)
4175 : cx(cx
), simulator(simulator
), testFunction(cx
) {}
4177 bool IterativeFailureTest::test() {
4178 if (disableOOMFunctions
) {
4186 auto onExit
= mozilla::MakeScopeExit([this] { teardown(); });
4188 for (unsigned thread
= threadStart
; thread
<= threadEnd
; thread
++) {
4189 if (!testThread(thread
)) {
4197 bool IterativeFailureTest::setup() {
4198 if (!CheckCanSimulateOOM(cx
)) {
4202 // Disallow nested tests.
4203 if (cx
->runningOOMTest
) {
4204 JS_ReportErrorASCII(
4205 cx
, "Nested call to iterative failure test is not allowed.");
4208 cx
->runningOOMTest
= true;
4210 MOZ_ASSERT(!cx
->isExceptionPending());
4213 JS::SetGCZeal(cx
, 0, JS::ShellDefaultGCZealFrequency
);
4216 // Delazify the function here if necessary so we don't end up testing that.
4217 if (testFunction
->isInterpreted() &&
4218 !JSFunction::getOrCreateScript(cx
, testFunction
)) {
4222 compartmentCount
= CountCompartments(cx
);
4224 simulator
.setup(cx
);
4229 bool IterativeFailureTest::testThread(unsigned thread
) {
4231 fprintf(stderr
, "thread %u\n", thread
);
4234 RootedValue
exception(cx
);
4236 unsigned iteration
= 1;
4237 bool failureWasSimulated
;
4239 if (!testIteration(thread
, iteration
, failureWasSimulated
, &exception
)) {
4244 } while (failureWasSimulated
);
4247 fprintf(stderr
, " finished after %u iterations\n", iteration
- 1);
4248 if (!exception
.isUndefined()) {
4249 RootedString
str(cx
, JS::ToString(cx
, exception
));
4251 fprintf(stderr
, " error while trying to print exception, giving up\n");
4254 UniqueChars
bytes(JS_EncodeStringToUTF8(cx
, str
));
4258 fprintf(stderr
, " threw %s\n", bytes
.get());
4265 bool IterativeFailureTest::testIteration(unsigned thread
, unsigned iteration
,
4266 bool& failureWasSimulated
,
4267 MutableHandleValue exception
) {
4269 fprintf(stderr
, " iteration %u\n", iteration
);
4272 MOZ_RELEASE_ASSERT(!cx
->isExceptionPending());
4274 simulator
.startSimulating(cx
, iteration
, thread
, keepFailing
);
4276 RootedValue
result(cx
);
4277 bool ok
= JS_CallFunction(cx
, cx
->global(), testFunction
,
4278 HandleValueArray::empty(), &result
);
4280 failureWasSimulated
= simulator
.stopSimulating();
4282 if (ok
&& cx
->isExceptionPending()) {
4284 "Thunk execution succeeded but an exception was raised - missing error "
4288 if (!ok
&& !cx
->isExceptionPending() && expectExceptionOnFailure
) {
4290 "Thunk execution failed but no exception was raised - missing call to "
4291 "js::ReportOutOfMemory()?");
4294 // Note that it is possible that the function throws an exception unconnected
4295 // to the simulated failure, in which case we ignore it. More correct would be
4296 // to have the caller pass some kind of exception specification and to check
4297 // the exception against it.
4298 if (!failureWasSimulated
&& cx
->isExceptionPending()) {
4299 if (!cx
->getPendingException(exception
)) {
4303 cx
->clearPendingException();
4310 void IterativeFailureTest::cleanup() {
4311 simulator
.cleanup(cx
);
4315 // Some tests create a new compartment or zone on every iteration. Our GC is
4316 // triggered by GC allocations and not by number of compartments or zones, so
4317 // these won't normally get cleaned up. The check here stops some tests
4318 // running out of memory. ("Gentlemen, you can't fight in here! This is the
4320 if (CountCompartments(cx
) > compartmentCount
+ 100) {
4322 compartmentCount
= CountCompartments(cx
);
4326 void IterativeFailureTest::teardown() {
4327 simulator
.teardown(cx
);
4329 cx
->runningOOMTest
= false;
4332 bool IterativeFailureTest::initParams(const CallArgs
& args
) {
4333 if (args
.length() < 1 || args
.length() > 2) {
4334 JS_ReportErrorASCII(cx
, "function takes between 1 and 2 arguments.");
4338 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
4339 JS_ReportErrorASCII(cx
, "The first argument must be the function to test.");
4342 testFunction
= &args
[0].toObject().as
<JSFunction
>();
4344 if (args
.length() == 2) {
4345 if (args
[1].isBoolean()) {
4346 expectExceptionOnFailure
= args
[1].toBoolean();
4347 } else if (args
[1].isObject()) {
4348 RootedObject
options(cx
, &args
[1].toObject());
4349 RootedValue
value(cx
);
4351 if (!JS_GetProperty(cx
, options
, "expectExceptionOnFailure", &value
)) {
4354 if (!value
.isUndefined()) {
4355 expectExceptionOnFailure
= ToBoolean(value
);
4358 if (!JS_GetProperty(cx
, options
, "keepFailing", &value
)) {
4361 if (!value
.isUndefined()) {
4362 keepFailing
= ToBoolean(value
);
4365 JS_ReportErrorASCII(
4366 cx
, "The optional second argument must be an object or a boolean.");
4371 // There are some places where we do fail without raising an exception, so
4372 // we can't expose this to the fuzzers by default.
4374 expectExceptionOnFailure
= false;
4377 // Test all threads by default except worker threads.
4378 threadStart
= oom::FirstThreadTypeToTest
;
4379 threadEnd
= oom::LastThreadTypeToTest
;
4381 // Test a single thread type if specified by the OOM_THREAD environment
4383 int threadOption
= 0;
4384 if (EnvVarAsInt("OOM_THREAD", &threadOption
)) {
4385 if (threadOption
< oom::FirstThreadTypeToTest
||
4386 threadOption
> oom::LastThreadTypeToTest
) {
4387 JS_ReportErrorASCII(cx
, "OOM_THREAD value out of range.");
4391 threadStart
= threadOption
;
4392 threadEnd
= threadOption
;
4395 verbose
= EnvVarIsDefined("OOM_VERBOSE");
4400 struct OOMSimulator
: public IterativeFailureTest::FailureSimulator
{
4401 void setup(JSContext
* cx
) override
{ cx
->runtime()->hadOutOfMemory
= false; }
4403 void startSimulating(JSContext
* cx
, unsigned i
, unsigned thread
,
4404 bool keepFailing
) override
{
4405 MOZ_ASSERT(!cx
->runtime()->hadOutOfMemory
);
4406 js::oom::simulator
.simulateFailureAfter(
4407 js::oom::FailureSimulator::Kind::OOM
, i
, thread
, keepFailing
);
4410 bool stopSimulating() override
{
4411 bool handledOOM
= js::oom::HadSimulatedOOM();
4412 js::oom::simulator
.reset();
4416 void cleanup(JSContext
* cx
) override
{
4417 cx
->runtime()->hadOutOfMemory
= false;
4421 static bool OOMTest(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4422 CallArgs args
= CallArgsFromVp(argc
, vp
);
4424 OOMSimulator simulator
;
4425 if (!RunIterativeFailureTest(cx
, args
, simulator
)) {
4429 args
.rval().setUndefined();
4433 struct StackOOMSimulator
: public IterativeFailureTest::FailureSimulator
{
4434 void startSimulating(JSContext
* cx
, unsigned i
, unsigned thread
,
4435 bool keepFailing
) override
{
4436 js::oom::simulator
.simulateFailureAfter(
4437 js::oom::FailureSimulator::Kind::StackOOM
, i
, thread
, keepFailing
);
4440 bool stopSimulating() override
{
4441 bool handledOOM
= js::oom::HadSimulatedStackOOM();
4442 js::oom::simulator
.reset();
4447 static bool StackTest(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4448 CallArgs args
= CallArgsFromVp(argc
, vp
);
4450 StackOOMSimulator simulator
;
4451 if (!RunIterativeFailureTest(cx
, args
, simulator
)) {
4455 args
.rval().setUndefined();
4459 struct FailingIterruptSimulator
4460 : public IterativeFailureTest::FailureSimulator
{
4461 JSInterruptCallback
* prevEnd
= nullptr;
4463 static bool failingInterruptCallback(JSContext
* cx
) { return false; }
4465 void setup(JSContext
* cx
) override
{
4466 prevEnd
= cx
->interruptCallbacks().end();
4467 JS_AddInterruptCallback(cx
, failingInterruptCallback
);
4470 void teardown(JSContext
* cx
) override
{
4471 cx
->interruptCallbacks().erase(prevEnd
, cx
->interruptCallbacks().end());
4474 void startSimulating(JSContext
* cx
, unsigned i
, unsigned thread
,
4475 bool keepFailing
) override
{
4476 js::oom::simulator
.simulateFailureAfter(
4477 js::oom::FailureSimulator::Kind::Interrupt
, i
, thread
, keepFailing
);
4480 bool stopSimulating() override
{
4481 bool handledInterrupt
= js::oom::HadSimulatedInterrupt();
4482 js::oom::simulator
.reset();
4483 return handledInterrupt
;
4487 static bool InterruptTest(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4488 CallArgs args
= CallArgsFromVp(argc
, vp
);
4490 FailingIterruptSimulator simulator
;
4491 if (!RunIterativeFailureTest(cx
, args
, simulator
)) {
4495 args
.rval().setUndefined();
4499 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
4501 static bool SettlePromiseNow(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4502 CallArgs args
= CallArgsFromVp(argc
, vp
);
4503 if (!args
.requireAtLeast(cx
, "settlePromiseNow", 1)) {
4506 if (!args
[0].isObject() || !args
[0].toObject().is
<PromiseObject
>()) {
4507 JS_ReportErrorASCII(cx
, "first argument must be a Promise object");
4511 Rooted
<PromiseObject
*> promise(cx
, &args
[0].toObject().as
<PromiseObject
>());
4512 if (IsPromiseForAsyncFunctionOrGenerator(promise
)) {
4513 JS_ReportErrorASCII(
4514 cx
, "async function/generator's promise shouldn't be manually settled");
4518 if (promise
->state() != JS::PromiseState::Pending
) {
4519 JS_ReportErrorASCII(cx
, "cannot settle an already-resolved promise");
4523 if (IsPromiseWithDefaultResolvingFunction(promise
)) {
4524 SetAlreadyResolvedPromiseWithDefaultResolvingFunction(promise
);
4527 int32_t flags
= promise
->flags();
4528 promise
->setFixedSlot(
4530 Int32Value(flags
| PROMISE_FLAG_RESOLVED
| PROMISE_FLAG_FULFILLED
));
4531 promise
->setFixedSlot(PromiseSlot_ReactionsOrResult
, UndefinedValue());
4533 DebugAPI::onPromiseSettled(cx
, promise
);
4537 static bool GetWaitForAllPromise(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4538 CallArgs args
= CallArgsFromVp(argc
, vp
);
4539 if (!args
.requireAtLeast(cx
, "getWaitForAllPromise", 1)) {
4542 if (!args
[0].isObject() || !args
[0].toObject().is
<ArrayObject
>() ||
4543 args
[0].toObject().as
<NativeObject
>().isIndexed()) {
4544 JS_ReportErrorASCII(
4545 cx
, "first argument must be a dense Array of Promise objects");
4548 Rooted
<NativeObject
*> list(cx
, &args
[0].toObject().as
<NativeObject
>());
4549 RootedObjectVector
promises(cx
);
4550 uint32_t count
= list
->getDenseInitializedLength();
4551 if (!promises
.resize(count
)) {
4555 for (uint32_t i
= 0; i
< count
; i
++) {
4556 RootedValue
elem(cx
, list
->getDenseElement(i
));
4557 if (!elem
.isObject() || !elem
.toObject().is
<PromiseObject
>()) {
4558 JS_ReportErrorASCII(
4559 cx
, "Each entry in the passed-in Array must be a Promise");
4562 promises
[i
].set(&elem
.toObject());
4565 RootedObject
resultPromise(cx
, JS::GetWaitForAllPromise(cx
, promises
));
4566 if (!resultPromise
) {
4570 args
.rval().set(ObjectValue(*resultPromise
));
4574 static bool ResolvePromise(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4575 CallArgs args
= CallArgsFromVp(argc
, vp
);
4576 if (!args
.requireAtLeast(cx
, "resolvePromise", 2)) {
4579 if (!args
[0].isObject() ||
4580 !UncheckedUnwrap(&args
[0].toObject())->is
<PromiseObject
>()) {
4581 JS_ReportErrorASCII(
4582 cx
, "first argument must be a maybe-wrapped Promise object");
4586 RootedObject
promise(cx
, &args
[0].toObject());
4587 RootedValue
resolution(cx
, args
[1]);
4588 mozilla::Maybe
<AutoRealm
> ar
;
4589 if (IsWrapper(promise
)) {
4590 promise
= UncheckedUnwrap(promise
);
4591 ar
.emplace(cx
, promise
);
4592 if (!cx
->compartment()->wrap(cx
, &resolution
)) {
4597 if (IsPromiseForAsyncFunctionOrGenerator(promise
)) {
4598 JS_ReportErrorASCII(
4600 "async function/generator's promise shouldn't be manually resolved");
4604 bool result
= JS::ResolvePromise(cx
, promise
, resolution
);
4606 args
.rval().setUndefined();
4611 static bool RejectPromise(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4612 CallArgs args
= CallArgsFromVp(argc
, vp
);
4613 if (!args
.requireAtLeast(cx
, "rejectPromise", 2)) {
4616 if (!args
[0].isObject() ||
4617 !UncheckedUnwrap(&args
[0].toObject())->is
<PromiseObject
>()) {
4618 JS_ReportErrorASCII(
4619 cx
, "first argument must be a maybe-wrapped Promise object");
4623 RootedObject
promise(cx
, &args
[0].toObject());
4624 RootedValue
reason(cx
, args
[1]);
4625 mozilla::Maybe
<AutoRealm
> ar
;
4626 if (IsWrapper(promise
)) {
4627 promise
= UncheckedUnwrap(promise
);
4628 ar
.emplace(cx
, promise
);
4629 if (!cx
->compartment()->wrap(cx
, &reason
)) {
4634 if (IsPromiseForAsyncFunctionOrGenerator(promise
)) {
4635 JS_ReportErrorASCII(
4637 "async function/generator's promise shouldn't be manually rejected");
4641 bool result
= JS::RejectPromise(cx
, promise
, reason
);
4643 args
.rval().setUndefined();
4648 static unsigned finalizeCount
= 0;
4650 static void finalize_counter_finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
4654 static const JSClassOps FinalizeCounterClassOps
= {
4655 nullptr, // addProperty
4656 nullptr, // delProperty
4657 nullptr, // enumerate
4658 nullptr, // newEnumerate
4660 nullptr, // mayResolve
4661 finalize_counter_finalize
, // finalize
4663 nullptr, // construct
4667 static const JSClass FinalizeCounterClass
= {
4669 JSCLASS_FOREGROUND_FINALIZE
,
4670 &FinalizeCounterClassOps
,
4673 static bool MakeFinalizeObserver(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4674 CallArgs args
= CallArgsFromVp(argc
, vp
);
4677 JS_NewObjectWithGivenProto(cx
, &FinalizeCounterClass
, nullptr);
4682 args
.rval().setObject(*obj
);
4686 static bool FinalizeCount(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4687 CallArgs args
= CallArgsFromVp(argc
, vp
);
4688 args
.rval().setInt32(finalizeCount
);
4692 static bool ResetFinalizeCount(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4693 CallArgs args
= CallArgsFromVp(argc
, vp
);
4695 args
.rval().setUndefined();
4699 static bool DumpHeap(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4700 CallArgs args
= CallArgsFromVp(argc
, vp
);
4702 FILE* dumpFile
= stdout
;
4703 auto closeFile
= mozilla::MakeScopeExit([&dumpFile
] {
4704 if (dumpFile
&& dumpFile
!= stdout
) {
4709 if (args
.length() > 1) {
4710 RootedObject
callee(cx
, &args
.callee());
4711 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
4715 if (!args
.get(0).isUndefined()) {
4716 RootedString
str(cx
, ToString(cx
, args
[0]));
4721 UniqueChars fileNameBytes
= JS_EncodeStringToUTF8(cx
, str
);
4722 if (!fileNameBytes
) {
4726 UniqueWideChars wideFileNameBytes
=
4727 JS::EncodeUtf8ToWide(cx
, fileNameBytes
.get());
4728 if (!wideFileNameBytes
) {
4731 dumpFile
= _wfopen(wideFileNameBytes
.get(), L
"w");
4733 UniqueChars narrowFileNameBytes
=
4734 JS::EncodeUtf8ToNarrow(cx
, fileNameBytes
.get());
4735 if (!narrowFileNameBytes
) {
4738 dumpFile
= fopen(narrowFileNameBytes
.get(), "w");
4741 JS_ReportErrorUTF8(cx
, "can't open %s", fileNameBytes
.get());
4747 js::DumpHeap(cx
, dumpFile
, js::IgnoreNurseryObjects
);
4749 args
.rval().setUndefined();
4753 static bool Terminate(JSContext
* cx
, unsigned arg
, Value
* vp
) {
4754 // Print a message to stderr in differential testing to help jsfunfuzz
4755 // find uncatchable-exception bugs.
4756 if (js::SupportDifferentialTesting()) {
4757 fprintf(stderr
, "terminate called\n");
4760 JS_ClearPendingException(cx
);
4764 static bool ReadGeckoProfilingStack(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4765 CallArgs args
= CallArgsFromVp(argc
, vp
);
4766 args
.rval().setUndefined();
4768 // Return boolean 'false' if profiler is not enabled.
4769 if (!cx
->runtime()->geckoProfiler().enabled()) {
4770 args
.rval().setBoolean(false);
4774 // Array holding physical jit stack frames.
4775 RootedObject
stack(cx
, NewDenseEmptyArray(cx
));
4780 // If profiler sampling has been suppressed, return an empty
4782 if (!cx
->isProfilerSamplingEnabled()) {
4783 args
.rval().setObject(*stack
);
4787 struct InlineFrameInfo
{
4788 InlineFrameInfo(const char* kind
, UniqueChars label
)
4789 : kind(kind
), label(std::move(label
)) {}
4794 Vector
<Vector
<InlineFrameInfo
, 0, TempAllocPolicy
>, 0, TempAllocPolicy
>
4797 JS::ProfilingFrameIterator::RegisterState state
;
4798 for (JS::ProfilingFrameIterator
i(cx
, state
); !i
.done(); ++i
) {
4799 MOZ_ASSERT(i
.stackAddress() != nullptr);
4801 if (!frameInfo
.emplaceBack(cx
)) {
4805 const size_t MaxInlineFrames
= 16;
4806 JS::ProfilingFrameIterator::Frame frames
[MaxInlineFrames
];
4807 uint32_t nframes
= i
.extractStack(frames
, 0, MaxInlineFrames
);
4808 MOZ_ASSERT(nframes
<= MaxInlineFrames
);
4809 for (uint32_t i
= 0; i
< nframes
; i
++) {
4810 const char* frameKindStr
= nullptr;
4811 switch (frames
[i
].kind
) {
4812 case JS::ProfilingFrameIterator::Frame_BaselineInterpreter
:
4813 frameKindStr
= "baseline-interpreter";
4815 case JS::ProfilingFrameIterator::Frame_Baseline
:
4816 frameKindStr
= "baseline-jit";
4818 case JS::ProfilingFrameIterator::Frame_Ion
:
4819 frameKindStr
= "ion";
4821 case JS::ProfilingFrameIterator::Frame_WasmBaseline
:
4822 case JS::ProfilingFrameIterator::Frame_WasmIon
:
4823 case JS::ProfilingFrameIterator::Frame_WasmOther
:
4824 frameKindStr
= "wasm";
4827 frameKindStr
= "unknown";
4831 DuplicateStringToArena(js::StringBufferArena
, cx
, frames
[i
].label
);
4836 if (!frameInfo
.back().emplaceBack(frameKindStr
, std::move(label
))) {
4842 RootedObject
inlineFrameInfo(cx
);
4843 RootedString
frameKind(cx
);
4844 RootedString
frameLabel(cx
);
4847 const unsigned propAttrs
= JSPROP_ENUMERATE
;
4849 uint32_t physicalFrameNo
= 0;
4850 for (auto& frame
: frameInfo
) {
4851 // Array holding all inline frames in a single physical jit stack frame.
4852 RootedObject
inlineStack(cx
, NewDenseEmptyArray(cx
));
4857 uint32_t inlineFrameNo
= 0;
4858 for (auto& inlineFrame
: frame
) {
4859 // Object holding frame info.
4860 RootedObject
inlineFrameInfo(cx
, NewPlainObject(cx
));
4861 if (!inlineFrameInfo
) {
4865 frameKind
= NewStringCopyZ
<CanGC
>(cx
, inlineFrame
.kind
);
4870 if (!JS_DefineProperty(cx
, inlineFrameInfo
, "kind", frameKind
,
4875 frameLabel
= NewLatin1StringZ(cx
, std::move(inlineFrame
.label
));
4880 if (!JS_DefineProperty(cx
, inlineFrameInfo
, "label", frameLabel
,
4885 idx
= PropertyKey::Int(inlineFrameNo
);
4886 if (!JS_DefinePropertyById(cx
, inlineStack
, idx
, inlineFrameInfo
, 0)) {
4893 // Push inline array into main array.
4894 idx
= PropertyKey::Int(physicalFrameNo
);
4895 if (!JS_DefinePropertyById(cx
, stack
, idx
, inlineStack
, 0)) {
4902 args
.rval().setObject(*stack
);
4906 static bool ReadGeckoInterpProfilingStack(JSContext
* cx
, unsigned argc
,
4908 CallArgs args
= CallArgsFromVp(argc
, vp
);
4909 args
.rval().setUndefined();
4911 // Return boolean 'false' if profiler is not enabled.
4912 if (!cx
->runtime()->geckoProfiler().enabled()) {
4913 args
.rval().setBoolean(false);
4917 // Array with information about each frame.
4918 Rooted
<JSObject
*> stack(cx
, NewDenseEmptyArray(cx
));
4922 uint32_t stackIndex
= 0;
4924 ProfilingStack
* profStack
= cx
->geckoProfiler().getProfilingStack();
4925 MOZ_ASSERT(profStack
);
4927 for (size_t i
= 0; i
< profStack
->stackSize(); i
++) {
4928 const auto& frame
= profStack
->frames
[i
];
4929 if (!frame
.isJsFrame()) {
4933 // Skip fake JS frame pushed for js::RunScript by GeckoProfilerEntryMarker.
4934 const char* dynamicStr
= frame
.dynamicString();
4939 Rooted
<PlainObject
*> frameInfo(cx
, NewPlainObject(cx
));
4944 Rooted
<JSString
*> dynamicString(
4945 cx
, JS_NewStringCopyUTF8Z(
4946 cx
, JS::ConstUTF8CharsZ(dynamicStr
, strlen(dynamicStr
))));
4947 if (!dynamicString
) {
4950 if (!JS_DefineProperty(cx
, frameInfo
, "dynamicString", dynamicString
,
4951 JSPROP_ENUMERATE
)) {
4955 if (!JS_DefineElement(cx
, stack
, stackIndex
, frameInfo
, JSPROP_ENUMERATE
)) {
4961 args
.rval().setObject(*stack
);
4965 static bool EnableOsiPointRegisterChecks(JSContext
*, unsigned argc
, Value
* vp
) {
4966 CallArgs args
= CallArgsFromVp(argc
, vp
);
4967 #ifdef CHECK_OSIPOINT_REGISTERS
4968 jit::JitOptions
.checkOsiPointRegisters
= true;
4970 args
.rval().setUndefined();
4974 static bool DisplayName(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4975 CallArgs args
= CallArgsFromVp(argc
, vp
);
4976 if (!args
.get(0).isObject() || !args
[0].toObject().is
<JSFunction
>()) {
4977 RootedObject
arg(cx
, &args
.callee());
4978 ReportUsageErrorASCII(cx
, arg
, "Must have one function argument");
4982 JSFunction
* fun
= &args
[0].toObject().as
<JSFunction
>();
4983 JS::Rooted
<JSAtom
*> str(cx
);
4984 if (!fun
->getDisplayAtom(cx
, &str
)) {
4987 args
.rval().setString(str
? str
: cx
->runtime()->emptyString
.ref());
4991 static bool IsAvxPresent(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4992 CallArgs args
= CallArgsFromVp(argc
, vp
);
4993 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
4995 if (argc
> 0 && args
.get(0).isNumber()) {
4996 minVersion
= std::max(1, int(args
[0].toNumber()));
4998 switch (minVersion
) {
5000 args
.rval().setBoolean(jit::Assembler::HasAVX());
5003 args
.rval().setBoolean(jit::Assembler::HasAVX2());
5007 args
.rval().setBoolean(false);
5011 class ShellAllocationMetadataBuilder
: public AllocationMetadataBuilder
{
5013 ShellAllocationMetadataBuilder() = default;
5015 virtual JSObject
* build(JSContext
* cx
, HandleObject
,
5016 AutoEnterOOMUnsafeRegion
& oomUnsafe
) const override
;
5018 static const ShellAllocationMetadataBuilder metadataBuilder
;
5021 JSObject
* ShellAllocationMetadataBuilder::build(
5022 JSContext
* cx
, HandleObject
, AutoEnterOOMUnsafeRegion
& oomUnsafe
) const {
5023 RootedObject
obj(cx
, NewPlainObject(cx
));
5025 oomUnsafe
.crash("ShellAllocationMetadataBuilder::build");
5028 RootedObject
stack(cx
, NewDenseEmptyArray(cx
));
5030 oomUnsafe
.crash("ShellAllocationMetadataBuilder::build");
5033 static int createdIndex
= 0;
5036 if (!JS_DefineProperty(cx
, obj
, "index", createdIndex
, 0)) {
5037 oomUnsafe
.crash("ShellAllocationMetadataBuilder::build");
5040 if (!JS_DefineProperty(cx
, obj
, "stack", stack
, 0)) {
5041 oomUnsafe
.crash("ShellAllocationMetadataBuilder::build");
5046 RootedValue
callee(cx
);
5047 for (NonBuiltinScriptFrameIter
iter(cx
); !iter
.done(); ++iter
) {
5048 if (iter
.isFunctionFrame() && iter
.compartment() == cx
->compartment()) {
5049 id
= PropertyKey::Int(stackIndex
);
5050 RootedObject
callee(cx
, iter
.callee(cx
));
5051 if (!JS_DefinePropertyById(cx
, stack
, id
, callee
, JSPROP_ENUMERATE
)) {
5052 oomUnsafe
.crash("ShellAllocationMetadataBuilder::build");
5061 const ShellAllocationMetadataBuilder
5062 ShellAllocationMetadataBuilder::metadataBuilder
;
5064 static bool EnableShellAllocationMetadataBuilder(JSContext
* cx
, unsigned argc
,
5066 CallArgs args
= CallArgsFromVp(argc
, vp
);
5068 SetAllocationMetadataBuilder(
5069 cx
, &ShellAllocationMetadataBuilder::metadataBuilder
);
5071 args
.rval().setUndefined();
5075 static bool GetAllocationMetadata(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5076 CallArgs args
= CallArgsFromVp(argc
, vp
);
5077 if (args
.length() != 1 || !args
[0].isObject()) {
5078 JS_ReportErrorASCII(cx
, "Argument must be an object");
5082 args
.rval().setObjectOrNull(GetAllocationMetadata(&args
[0].toObject()));
5086 static bool testingFunc_bailout(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5087 CallArgs args
= CallArgsFromVp(argc
, vp
);
5089 // NOP when not in IonMonkey
5090 args
.rval().setUndefined();
5094 static bool testingFunc_bailAfter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5095 CallArgs args
= CallArgsFromVp(argc
, vp
);
5096 if (args
.length() != 1 || !args
[0].isInt32() || args
[0].toInt32() < 0) {
5097 JS_ReportErrorASCII(
5098 cx
, "Argument must be a positive number that fits in an int32");
5103 if (auto* jitRuntime
= cx
->runtime()->jitRuntime()) {
5104 uint32_t bailAfter
= args
[0].toInt32();
5105 bool enableBailAfter
= bailAfter
> 0;
5106 if (jitRuntime
->ionBailAfterEnabled() != enableBailAfter
) {
5107 // Force JIT code to be recompiled with (or without) instrumentation.
5108 ReleaseAllJITCode(cx
->gcContext());
5109 jitRuntime
->setIonBailAfterEnabled(enableBailAfter
);
5111 jitRuntime
->setIonBailAfterCounter(bailAfter
);
5115 args
.rval().setUndefined();
5119 static bool testingFunc_invalidate(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5120 CallArgs args
= CallArgsFromVp(argc
, vp
);
5122 // If the topmost frame is Ion/Warp, find the IonScript and invalidate it.
5124 if (!iter
.done() && iter
.isIon()) {
5125 while (!iter
.isPhysicalJitFrame()) {
5128 if (iter
.script()->hasIonScript()) {
5129 js::jit::Invalidate(cx
, iter
.script());
5133 args
.rval().setUndefined();
5137 static constexpr unsigned JitWarmupResetLimit
= 20;
5138 static_assert(JitWarmupResetLimit
<=
5139 unsigned(JSScript::MutableFlags::WarmupResets_MASK
),
5140 "JitWarmupResetLimit exceeds max value");
5142 static bool testingFunc_inJit(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5143 CallArgs args
= CallArgsFromVp(argc
, vp
);
5145 if (!jit::IsBaselineJitEnabled(cx
)) {
5146 return ReturnStringCopy(cx
, args
, "Baseline is disabled.");
5149 // Use frame iterator to inspect caller.
5152 // We may be invoked directly, not in a JS context, e.g. if inJit is added as
5153 // a callback on the event queue.
5155 args
.rval().setBoolean(false);
5159 if (iter
.hasScript()) {
5160 // Detect repeated attempts to compile, resetting the counter if inJit
5161 // succeeds. Note: This script may have be inlined into its caller.
5162 if (iter
.isJSJit()) {
5163 iter
.script()->resetWarmUpResetCounter();
5164 } else if (iter
.script()->getWarmUpResetCount() >= JitWarmupResetLimit
) {
5165 return ReturnStringCopy(
5166 cx
, args
, "Compilation is being repeatedly prevented. Giving up.");
5170 // Returns true for any JIT (including WASM).
5171 MOZ_ASSERT_IF(iter
.isJSJit(), cx
->currentlyRunningInJit());
5172 args
.rval().setBoolean(cx
->currentlyRunningInJit());
5176 static bool testingFunc_inIon(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5177 CallArgs args
= CallArgsFromVp(argc
, vp
);
5179 if (!jit::IsIonEnabled(cx
)) {
5180 return ReturnStringCopy(cx
, args
, "Ion is disabled.");
5183 // Use frame iterator to inspect caller.
5186 // We may be invoked directly, not in a JS context, e.g. if inIon is added as
5187 // a callback on the event queue.
5189 args
.rval().setBoolean(false);
5193 if (iter
.hasScript()) {
5194 // Detect repeated attempts to compile, resetting the counter if inIon
5195 // succeeds. Note: This script may have be inlined into its caller.
5197 iter
.script()->resetWarmUpResetCounter();
5198 } else if (!iter
.script()->canIonCompile()) {
5199 return ReturnStringCopy(cx
, args
, "Unable to Ion-compile this script.");
5200 } else if (iter
.script()->getWarmUpResetCount() >= JitWarmupResetLimit
) {
5201 return ReturnStringCopy(
5202 cx
, args
, "Compilation is being repeatedly prevented. Giving up.");
5206 args
.rval().setBoolean(iter
.isIon());
5210 bool js::testingFunc_assertFloat32(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5211 CallArgs args
= CallArgsFromVp(argc
, vp
);
5212 if (args
.length() != 2) {
5213 JS_ReportErrorASCII(cx
, "Expects only 2 arguments");
5217 // NOP when not in IonMonkey
5218 args
.rval().setUndefined();
5222 static bool TestingFunc_assertJitStackInvariants(JSContext
* cx
, unsigned argc
,
5224 CallArgs args
= CallArgsFromVp(argc
, vp
);
5226 jit::AssertJitStackInvariants(cx
);
5227 args
.rval().setUndefined();
5231 bool js::testingFunc_assertRecoveredOnBailout(JSContext
* cx
, unsigned argc
,
5233 CallArgs args
= CallArgsFromVp(argc
, vp
);
5234 if (args
.length() != 2) {
5235 JS_ReportErrorASCII(cx
, "Expects only 2 arguments");
5239 // NOP when not in IonMonkey
5240 args
.rval().setUndefined();
5244 static bool GetJitCompilerOptions(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5245 CallArgs args
= CallArgsFromVp(argc
, vp
);
5246 RootedObject
info(cx
, JS_NewPlainObject(cx
));
5251 uint32_t intValue
= 0;
5252 RootedValue
value(cx
);
5254 #define JIT_COMPILER_MATCH(key, string) \
5255 opt = JSJITCOMPILER_##key; \
5256 if (JS_GetGlobalJitCompilerOption(cx, opt, &intValue)) { \
5257 value.setInt32(intValue); \
5258 if (!JS_SetProperty(cx, info, string, value)) return false; \
5261 JSJitCompilerOption opt
= JSJITCOMPILER_NOT_AN_OPTION
;
5262 JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH
);
5263 #undef JIT_COMPILER_MATCH
5265 args
.rval().setObject(*info
);
5269 static bool SetIonCheckGraphCoherency(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5270 CallArgs args
= CallArgsFromVp(argc
, vp
);
5271 jit::JitOptions
.checkGraphConsistency
= ToBoolean(args
.get(0));
5272 args
.rval().setUndefined();
5276 // A JSObject that holds structured clone data, similar to the C++ class
5277 // JSAutoStructuredCloneBuffer.
5278 class CloneBufferObject
: public NativeObject
{
5279 static const JSPropertySpec props_
[3];
5281 static const size_t DATA_SLOT
= 0;
5282 static const size_t SYNTHETIC_SLOT
= 1;
5283 static const size_t NUM_SLOTS
= 2;
5286 static const JSClass class_
;
5288 static CloneBufferObject
* Create(JSContext
* cx
) {
5289 RootedObject
obj(cx
, JS_NewObject(cx
, &class_
));
5293 obj
->as
<CloneBufferObject
>().setReservedSlot(DATA_SLOT
,
5294 PrivateValue(nullptr));
5295 obj
->as
<CloneBufferObject
>().setReservedSlot(SYNTHETIC_SLOT
,
5296 BooleanValue(false));
5298 if (!JS_DefineProperties(cx
, obj
, props_
)) {
5302 return &obj
->as
<CloneBufferObject
>();
5305 static CloneBufferObject
* Create(JSContext
* cx
,
5306 JSAutoStructuredCloneBuffer
* buffer
) {
5307 Rooted
<CloneBufferObject
*> obj(cx
, Create(cx
));
5311 auto data
= js::MakeUnique
<JSStructuredCloneData
>(buffer
->scope());
5313 ReportOutOfMemory(cx
);
5316 buffer
->giveTo(data
.get());
5317 obj
->setData(data
.release(), false);
5321 JSStructuredCloneData
* data() const {
5322 return static_cast<JSStructuredCloneData
*>(
5323 getReservedSlot(DATA_SLOT
).toPrivate());
5326 bool isSynthetic() const {
5327 return getReservedSlot(SYNTHETIC_SLOT
).toBoolean();
5330 void setData(JSStructuredCloneData
* aData
, bool synthetic
) {
5331 MOZ_ASSERT(!data());
5332 setReservedSlot(DATA_SLOT
, PrivateValue(aData
));
5333 setReservedSlot(SYNTHETIC_SLOT
, BooleanValue(synthetic
));
5336 // Discard an owned clone buffer.
5339 setReservedSlot(DATA_SLOT
, PrivateValue(nullptr));
5342 static bool setCloneBuffer_impl(JSContext
* cx
, const CallArgs
& args
) {
5343 Rooted
<CloneBufferObject
*> obj(
5344 cx
, &args
.thisv().toObject().as
<CloneBufferObject
>());
5346 const char* data
= nullptr;
5347 UniqueChars dataOwner
;
5350 if (args
.get(0).isObject() && args
[0].toObject().is
<ArrayBufferObject
>()) {
5351 ArrayBufferObject
* buffer
= &args
[0].toObject().as
<ArrayBufferObject
>();
5352 bool isSharedMemory
;
5353 uint8_t* dataBytes
= nullptr;
5354 JS::GetArrayBufferLengthAndData(buffer
, &nbytes
, &isSharedMemory
,
5356 MOZ_ASSERT(!isSharedMemory
);
5357 data
= reinterpret_cast<char*>(dataBytes
);
5359 JSString
* str
= JS::ToString(cx
, args
.get(0));
5363 dataOwner
= JS_EncodeStringToLatin1(cx
, str
);
5367 data
= dataOwner
.get();
5368 nbytes
= JS_GetStringLength(str
);
5371 if (nbytes
== 0 || (nbytes
% sizeof(uint64_t) != 0)) {
5372 JS_ReportErrorASCII(cx
, "Invalid length for clonebuffer data");
5376 auto buf
= js::MakeUnique
<JSStructuredCloneData
>(
5377 JS::StructuredCloneScope::DifferentProcess
);
5378 if (!buf
|| !buf
->Init(nbytes
)) {
5379 ReportOutOfMemory(cx
);
5383 MOZ_ALWAYS_TRUE(buf
->AppendBytes(data
, nbytes
));
5385 obj
->setData(buf
.release(), true);
5387 args
.rval().setUndefined();
5391 static bool is(HandleValue v
) {
5392 return v
.isObject() && v
.toObject().is
<CloneBufferObject
>();
5395 static bool setCloneBuffer(JSContext
* cx
, unsigned int argc
, JS::Value
* vp
) {
5396 CallArgs args
= CallArgsFromVp(argc
, vp
);
5397 return CallNonGenericMethod
<is
, setCloneBuffer_impl
>(cx
, args
);
5400 static bool getData(JSContext
* cx
, Handle
<CloneBufferObject
*> obj
,
5401 JSStructuredCloneData
** data
) {
5407 bool hasTransferable
;
5408 if (!JS_StructuredCloneHasTransferables(*obj
->data(), &hasTransferable
)) {
5412 if (hasTransferable
) {
5413 JS_ReportErrorASCII(
5414 cx
, "cannot retrieve structured clone buffer with transferables");
5418 *data
= obj
->data();
5422 static bool getCloneBuffer_impl(JSContext
* cx
, const CallArgs
& args
) {
5423 Rooted
<CloneBufferObject
*> obj(
5424 cx
, &args
.thisv().toObject().as
<CloneBufferObject
>());
5425 MOZ_ASSERT(args
.length() == 0);
5427 JSStructuredCloneData
* data
;
5428 if (!getData(cx
, obj
, &data
)) {
5432 if (data
== nullptr) {
5433 args
.rval().setUndefined();
5437 size_t size
= data
->Size();
5438 UniqueChars
buffer(js_pod_malloc
<char>(size
));
5440 ReportOutOfMemory(cx
);
5443 auto iter
= data
->Start();
5444 if (!data
->ReadBytes(iter
, buffer
.get(), size
)) {
5445 ReportOutOfMemory(cx
);
5448 JSString
* str
= JS_NewStringCopyN(cx
, buffer
.get(), size
);
5452 args
.rval().setString(str
);
5456 static bool getCloneBuffer(JSContext
* cx
, unsigned int argc
, JS::Value
* vp
) {
5457 CallArgs args
= CallArgsFromVp(argc
, vp
);
5458 return CallNonGenericMethod
<is
, getCloneBuffer_impl
>(cx
, args
);
5461 static bool getCloneBufferAsArrayBuffer_impl(JSContext
* cx
,
5462 const CallArgs
& args
) {
5463 Rooted
<CloneBufferObject
*> obj(
5464 cx
, &args
.thisv().toObject().as
<CloneBufferObject
>());
5465 MOZ_ASSERT(args
.length() == 0);
5467 JSStructuredCloneData
* data
;
5468 if (!getData(cx
, obj
, &data
)) {
5472 if (data
== nullptr) {
5473 args
.rval().setUndefined();
5477 size_t size
= data
->Size();
5478 UniqueChars
buffer(js_pod_malloc
<char>(size
));
5480 ReportOutOfMemory(cx
);
5483 auto iter
= data
->Start();
5484 if (!data
->ReadBytes(iter
, buffer
.get(), size
)) {
5485 ReportOutOfMemory(cx
);
5489 JSObject
* arrayBuffer
=
5490 JS::NewArrayBufferWithContents(cx
, size
, std::move(buffer
));
5495 args
.rval().setObject(*arrayBuffer
);
5499 static bool getCloneBufferAsArrayBuffer(JSContext
* cx
, unsigned int argc
,
5501 CallArgs args
= CallArgsFromVp(argc
, vp
);
5502 return CallNonGenericMethod
<is
, getCloneBufferAsArrayBuffer_impl
>(cx
, args
);
5505 static void Finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
5506 obj
->as
<CloneBufferObject
>().discard();
5510 static const JSClassOps CloneBufferObjectClassOps
= {
5511 nullptr, // addProperty
5512 nullptr, // delProperty
5513 nullptr, // enumerate
5514 nullptr, // newEnumerate
5516 nullptr, // mayResolve
5517 CloneBufferObject::Finalize
, // finalize
5519 nullptr, // construct
5523 const JSClass
CloneBufferObject::class_
= {
5525 JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS
) |
5526 JSCLASS_FOREGROUND_FINALIZE
,
5527 &CloneBufferObjectClassOps
,
5530 const JSPropertySpec
CloneBufferObject::props_
[] = {
5531 JS_PSGS("clonebuffer", getCloneBuffer
, setCloneBuffer
, 0),
5532 JS_PSGS("arraybuffer", getCloneBufferAsArrayBuffer
, setCloneBuffer
, 0),
5536 static mozilla::Maybe
<JS::StructuredCloneScope
> ParseCloneScope(
5537 JSContext
* cx
, HandleString str
) {
5538 mozilla::Maybe
<JS::StructuredCloneScope
> scope
;
5540 JSLinearString
* scopeStr
= str
->ensureLinear(cx
);
5545 if (StringEqualsLiteral(scopeStr
, "SameProcess")) {
5546 scope
.emplace(JS::StructuredCloneScope::SameProcess
);
5547 } else if (StringEqualsLiteral(scopeStr
, "DifferentProcess")) {
5548 scope
.emplace(JS::StructuredCloneScope::DifferentProcess
);
5549 } else if (StringEqualsLiteral(scopeStr
, "DifferentProcessForIndexedDB")) {
5550 scope
.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB
);
5556 // A custom object that is serializable and transferable using
5557 // the engine's custom hooks. The callbacks log their activity
5558 // to a JSRuntime-wide log (tagging actions with IDs to distinguish them).
5559 class CustomSerializableObject
: public NativeObject
{
5560 static const size_t ID_SLOT
= 0;
5561 static const size_t DETACHED_SLOT
= 1;
5562 static const size_t BEHAVIOR_SLOT
= 2;
5563 static const size_t NUM_SLOTS
= 3;
5565 static constexpr size_t MAX_LOG_LEN
= 100;
5567 // The activity log should be specific to a JSRuntime.
5568 struct ActivityLog
{
5569 uint32_t buffer
[MAX_LOG_LEN
];
5572 static MOZ_THREAD_LOCAL(ActivityLog
*) self
;
5573 static ActivityLog
* getThreadLog() {
5574 if (!self
.initialized() || !self
.get()) {
5575 self
.infallibleInit();
5576 AutoEnterOOMUnsafeRegion oomUnsafe
;
5577 self
.set(js_new
<ActivityLog
>());
5579 oomUnsafe
.crash("allocating activity log");
5581 if (!TlsContext
.get()->runtime()->atExit(
5583 auto* log
= static_cast<ActivityLog
*>(vpData
);
5587 oomUnsafe
.crash("atExit");
5593 static bool log(int32_t id
, char action
) {
5594 return getThreadLog()->logImpl(id
, action
);
5597 bool logImpl(int32_t id
, char action
) {
5598 if (length
+ 2 > MAX_LOG_LEN
) {
5601 buffer
[length
++] = id
;
5602 buffer
[length
++] = uint32_t(action
);
5608 enum class Behavior
{
5610 FailDuringReadTransfer
= 1,
5614 static constexpr JSClass class_
= {
5615 "CustomSerializable",
5616 JSCLASS_HAS_RESERVED_SLOTS(NUM_SLOTS
),
5619 static bool is(HandleValue v
) {
5620 return v
.isObject() && v
.toObject().is
<CustomSerializableObject
>();
5623 static CustomSerializableObject
* Create(JSContext
* cx
, int32_t id
,
5624 Behavior behavior
) {
5625 Rooted
<CustomSerializableObject
*> obj(
5626 cx
, static_cast<CustomSerializableObject
*>(JS_NewObject(cx
, &class_
)));
5630 obj
->setReservedSlot(ID_SLOT
, Int32Value(id
));
5631 obj
->setReservedSlot(DETACHED_SLOT
, BooleanValue(false));
5632 obj
->setReservedSlot(BEHAVIOR_SLOT
,
5633 Int32Value(static_cast<int32_t>(behavior
)));
5635 if (!JS_DefineProperty(cx
, obj
, "log", getLog
, clearLog
, 0)) {
5643 static uint32_t tag() { return JS_SCTAG_USER_MIN
; }
5645 static bool log(int32_t id
, char action
) {
5646 return ActivityLog::log(id
, action
);
5648 bool log(char action
) {
5649 return log(getReservedSlot(ID_SLOT
).toInt32(), action
);
5652 void detach() { setReservedSlot(DETACHED_SLOT
, BooleanValue(true)); }
5653 bool isDetached() { return getReservedSlot(DETACHED_SLOT
).toBoolean(); }
5655 uint32_t id() const { return getReservedSlot(ID_SLOT
).toInt32(); }
5656 Behavior
behavior() {
5657 return static_cast<Behavior
>(getReservedSlot(BEHAVIOR_SLOT
).toInt32());
5660 static bool getLog(JSContext
* cx
, unsigned int argc
, JS::Value
* vp
) {
5661 CallArgs args
= CallArgsFromVp(argc
, vp
);
5662 return CallNonGenericMethod
<is
, getLog_impl
>(cx
, args
);
5665 static bool getLog_impl(JSContext
* cx
, const CallArgs
& args
) {
5666 Rooted
<CustomSerializableObject
*> obj(
5667 cx
, &args
.thisv().toObject().as
<CustomSerializableObject
>());
5669 size_t len
= ActivityLog::getThreadLog()->length
;
5670 uint32_t* logBuffer
= ActivityLog::getThreadLog()->buffer
;
5672 Rooted
<ArrayObject
*> result(cx
, NewDenseFullyAllocatedArray(cx
, len
));
5676 result
->ensureDenseInitializedLength(0, len
);
5678 for (size_t p
= 0; p
< len
; p
+= 2) {
5679 int32_t id
= int32_t(logBuffer
[p
]);
5680 char action
= char(logBuffer
[p
+ 1]);
5681 result
->setDenseElement(p
, Int32Value(id
));
5682 JSString
* str
= JS_NewStringCopyN(cx
, &action
, 1);
5686 result
->setDenseElement(p
+ 1, StringValue(str
));
5689 args
.rval().setObject(*result
);
5693 static bool clearLog(JSContext
* cx
, unsigned int argc
, JS::Value
* vp
) {
5694 CallArgs args
= CallArgsFromVp(argc
, vp
);
5695 if (!args
.get(0).isNullOrUndefined()) {
5696 JS_ReportErrorASCII(cx
, "log may only be assigned null/undefined");
5699 ActivityLog::getThreadLog()->length
= 0;
5700 args
.rval().setUndefined();
5704 static bool Write(JSContext
* cx
, JSStructuredCloneWriter
* w
,
5705 JS::HandleObject aObj
, bool* sameProcessScopeRequired
,
5707 Rooted
<CustomSerializableObject
*> obj(cx
);
5709 if ((obj
= aObj
->maybeUnwrapIf
<CustomSerializableObject
>())) {
5711 // Write a regular clone as a <tag, id> pair, followed by <0, behavior>.
5712 // Note that transferring will communicate the behavior via a different
5714 return JS_WriteUint32Pair(w
, obj
->tag(), obj
->id()) &&
5715 JS_WriteUint32Pair(w
, 0, static_cast<uint32_t>(obj
->behavior()));
5718 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
5719 JSMSG_SC_UNSUPPORTED_TYPE
);
5723 static JSObject
* Read(JSContext
* cx
, JSStructuredCloneReader
* r
,
5724 const JS::CloneDataPolicy
& cloneDataPolicy
,
5725 uint32_t tag
, uint32_t id
, void* closure
) {
5726 uint32_t dummy
, behaviorData
;
5727 if (!JS_ReadUint32Pair(r
, &dummy
, &behaviorData
)) {
5730 if (dummy
!= 0 || id
> INT32_MAX
) {
5731 JS_ReportErrorASCII(cx
, "out of range");
5735 auto b
= static_cast<Behavior
>(behaviorData
);
5736 Rooted
<CustomSerializableObject
*> obj(
5737 cx
, Create(cx
, static_cast<int32_t>(id
), b
));
5743 if (obj
->behavior() == Behavior::FailDuringRead
) {
5744 JS_ReportErrorASCII(cx
,
5745 "Failed as requested in read during deserialization");
5751 static bool CanTransfer(JSContext
* cx
, JS::Handle
<JSObject
*> wrapped
,
5752 bool* sameProcessScopeRequired
, void* closure
) {
5753 Rooted
<CustomSerializableObject
*> obj(cx
);
5755 if ((obj
= wrapped
->maybeUnwrapIf
<CustomSerializableObject
>())) {
5757 // For now, all CustomSerializable objects are considered to be
5765 static bool WriteTransfer(JSContext
* cx
, JS::Handle
<JSObject
*> aObj
,
5766 void* closure
, uint32_t* tag
,
5767 JS::TransferableOwnership
* ownership
,
5768 void** content
, uint64_t* extraData
) {
5769 Rooted
<CustomSerializableObject
*> obj(cx
);
5771 if ((obj
= aObj
->maybeUnwrapIf
<CustomSerializableObject
>())) {
5772 if (obj
->isDetached()) {
5773 JS_ReportErrorASCII(cx
, "Attempted to transfer detached object");
5777 *content
= reinterpret_cast<void*>(obj
->id());
5778 *extraData
= static_cast<uint64_t>(obj
->behavior());
5780 *ownership
= JS::SCTAG_TMO_CUSTOM
;
5785 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
5786 JSMSG_SC_NOT_TRANSFERABLE
);
5790 static bool ReadTransfer(JSContext
* cx
, JSStructuredCloneReader
* r
,
5791 const JS::CloneDataPolicy
& cloneDataPolicy
,
5792 uint32_t tag
, void* content
, uint64_t extraData
,
5794 JS::MutableHandleObject returnObject
) {
5795 if (tag
== CustomSerializableObject::tag()) {
5796 int32_t id
= int32_t(reinterpret_cast<uintptr_t>(content
));
5797 Rooted
<CustomSerializableObject
*> obj(
5798 cx
, CustomSerializableObject::Create(
5799 cx
, id
, static_cast<Behavior
>(extraData
)));
5804 if (obj
->behavior() == Behavior::FailDuringReadTransfer
) {
5807 returnObject
.set(obj
);
5814 static void FreeTransfer(uint32_t tag
, JS::TransferableOwnership ownership
,
5815 void* content
, uint64_t extraData
, void* closure
) {
5816 CustomSerializableObject::log(uint32_t(reinterpret_cast<intptr_t>(content
)),
5821 MOZ_THREAD_LOCAL(CustomSerializableObject::ActivityLog
*)
5822 CustomSerializableObject::ActivityLog::self
;
5824 static bool MakeSerializable(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5825 CallArgs args
= CallArgsFromVp(argc
, vp
);
5828 if (args
.get(0).isInt32()) {
5829 id
= args
[0].toInt32();
5831 JS_ReportErrorASCII(cx
, "id out of range");
5835 CustomSerializableObject::Behavior behavior
=
5836 CustomSerializableObject::Behavior::Nothing
;
5837 if (args
.get(1).isInt32()) {
5838 int32_t iv
= args
[1].toInt32();
5839 constexpr int32_t min
=
5840 static_cast<int32_t>(CustomSerializableObject::Behavior::Nothing
);
5841 constexpr int32_t max
= static_cast<int32_t>(
5842 CustomSerializableObject::Behavior::FailDuringRead
);
5843 if (iv
< min
|| iv
> max
) {
5844 JS_ReportErrorASCII(cx
, "behavior out of range");
5847 behavior
= static_cast<CustomSerializableObject::Behavior
>(iv
);
5850 JSObject
* obj
= CustomSerializableObject::Create(cx
, id
, behavior
);
5855 args
.rval().setObject(*obj
);
5859 static JSStructuredCloneCallbacks gCloneCallbacks
= {
5860 .read
= CustomSerializableObject::Read
,
5861 .write
= CustomSerializableObject::Write
,
5862 .reportError
= nullptr,
5863 .readTransfer
= CustomSerializableObject::ReadTransfer
,
5864 .writeTransfer
= CustomSerializableObject::WriteTransfer
,
5865 .freeTransfer
= CustomSerializableObject::FreeTransfer
,
5866 .canTransfer
= CustomSerializableObject::CanTransfer
,
5867 .sabCloned
= nullptr};
5869 bool js::testingFunc_serialize(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5870 CallArgs args
= CallArgsFromVp(argc
, vp
);
5872 if (js::SupportDifferentialTesting()) {
5873 RootedObject
callee(cx
, &args
.callee());
5874 ReportUsageErrorASCII(cx
, callee
,
5875 "Function unavailable in differential testing mode.");
5879 mozilla::Maybe
<JSAutoStructuredCloneBuffer
> clonebuf
;
5880 JS::CloneDataPolicy policy
;
5882 if (!args
.get(2).isUndefined()) {
5883 RootedObject
opts(cx
, ToObject(cx
, args
.get(2)));
5889 if (!JS_GetProperty(cx
, opts
, "SharedArrayBuffer", &v
)) {
5893 if (!v
.isUndefined()) {
5894 JSString
* str
= JS::ToString(cx
, v
);
5898 JSLinearString
* poli
= str
->ensureLinear(cx
);
5903 if (StringEqualsLiteral(poli
, "allow")) {
5904 policy
.allowSharedMemoryObjects();
5905 policy
.allowIntraClusterClonableSharedObjects();
5906 } else if (StringEqualsLiteral(poli
, "deny")) {
5909 JS_ReportErrorASCII(cx
, "Invalid policy value for 'SharedArrayBuffer'");
5914 if (!JS_GetProperty(cx
, opts
, "scope", &v
)) {
5918 if (!v
.isUndefined()) {
5919 RootedString
str(cx
, JS::ToString(cx
, v
));
5923 auto scope
= ParseCloneScope(cx
, str
);
5925 JS_ReportErrorASCII(cx
, "Invalid structured clone scope");
5928 clonebuf
.emplace(*scope
, &gCloneCallbacks
, nullptr);
5933 clonebuf
.emplace(JS::StructuredCloneScope::SameProcess
, &gCloneCallbacks
,
5937 if (!clonebuf
->write(cx
, args
.get(0), args
.get(1), policy
)) {
5941 RootedObject
obj(cx
, CloneBufferObject::Create(cx
, clonebuf
.ptr()));
5946 args
.rval().setObject(*obj
);
5950 static bool Deserialize(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5951 CallArgs args
= CallArgsFromVp(argc
, vp
);
5953 if (js::SupportDifferentialTesting()) {
5954 RootedObject
callee(cx
, &args
.callee());
5955 ReportUsageErrorASCII(cx
, callee
,
5956 "Function unavailable in differential testing mode.");
5960 if (!args
.get(0).isObject() || !args
[0].toObject().is
<CloneBufferObject
>()) {
5961 JS_ReportErrorASCII(cx
, "deserialize requires a clonebuffer argument");
5964 Rooted
<CloneBufferObject
*> obj(cx
,
5965 &args
[0].toObject().as
<CloneBufferObject
>());
5967 JS::CloneDataPolicy policy
;
5969 JS::StructuredCloneScope scope
=
5970 obj
->isSynthetic() ? JS::StructuredCloneScope::DifferentProcess
5971 : JS::StructuredCloneScope::SameProcess
;
5972 if (args
.get(1).isObject()) {
5973 RootedObject
opts(cx
, &args
[1].toObject());
5979 if (!JS_GetProperty(cx
, opts
, "SharedArrayBuffer", &v
)) {
5983 if (!v
.isUndefined()) {
5984 JSString
* str
= JS::ToString(cx
, v
);
5988 JSLinearString
* poli
= str
->ensureLinear(cx
);
5993 if (StringEqualsLiteral(poli
, "allow")) {
5994 policy
.allowSharedMemoryObjects();
5995 policy
.allowIntraClusterClonableSharedObjects();
5996 } else if (StringEqualsLiteral(poli
, "deny")) {
5999 JS_ReportErrorASCII(cx
, "Invalid policy value for 'SharedArrayBuffer'");
6004 if (!JS_GetProperty(cx
, opts
, "scope", &v
)) {
6008 if (!v
.isUndefined()) {
6009 RootedString
str(cx
, JS::ToString(cx
, v
));
6013 auto maybeScope
= ParseCloneScope(cx
, str
);
6015 JS_ReportErrorASCII(cx
, "Invalid structured clone scope");
6019 if (*maybeScope
< scope
) {
6020 JS_ReportErrorASCII(cx
,
6021 "Cannot use less restrictive scope "
6022 "than the deserialized clone buffer's scope");
6026 scope
= *maybeScope
;
6030 // Clone buffer was already consumed?
6032 JS_ReportErrorASCII(cx
,
6033 "deserialize given invalid clone buffer "
6034 "(transferables already consumed?)");
6038 bool hasTransferable
;
6039 if (!JS_StructuredCloneHasTransferables(*obj
->data(), &hasTransferable
)) {
6043 RootedValue
deserialized(cx
);
6044 if (!JS_ReadStructuredClone(cx
, *obj
->data(), JS_STRUCTURED_CLONE_VERSION
,
6045 scope
, &deserialized
, policy
, &gCloneCallbacks
,
6049 args
.rval().set(deserialized
);
6051 // Consume any clone buffer with transferables; throw an error if it is
6052 // deserialized again.
6053 if (hasTransferable
) {
6060 static bool DetachArrayBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6061 CallArgs args
= CallArgsFromVp(argc
, vp
);
6063 if (args
.length() != 1) {
6064 JS_ReportErrorASCII(cx
, "detachArrayBuffer() requires a single argument");
6068 if (!args
[0].isObject()) {
6069 JS_ReportErrorASCII(cx
, "detachArrayBuffer must be passed an object");
6073 RootedObject
obj(cx
, &args
[0].toObject());
6074 if (!JS::DetachArrayBuffer(cx
, obj
)) {
6078 args
.rval().setUndefined();
6082 static bool EnsureNonInline(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6083 CallArgs args
= CallArgsFromVp(argc
, vp
);
6084 Rooted
<JSObject
*> callee(cx
, &args
.callee());
6086 if (!args
.get(0).isObject()) {
6087 js::ReportUsageErrorASCII(cx
, callee
, "Single object argument required");
6091 RootedObject
obj(cx
, &args
[0].toObject());
6092 if (!JS::EnsureNonInlineArrayBufferOrView(cx
, obj
)) {
6096 args
.rval().setUndefined();
6100 static bool PinArrayBufferOrViewLength(JSContext
* cx
, unsigned argc
,
6102 CallArgs args
= CallArgsFromVp(argc
, vp
);
6103 Rooted
<JSObject
*> callee(cx
, &args
.callee());
6105 if (!args
.get(0).isObject()) {
6106 js::ReportUsageErrorASCII(
6107 cx
, callee
, "ArrayBuffer or ArrayBufferView argument required");
6110 RootedObject
obj(cx
, &args
[0].toObject());
6111 if (!obj
->canUnwrapAs
<ArrayBufferViewObject
>() &&
6112 !obj
->canUnwrapAs
<ArrayBufferObjectMaybeShared
>()) {
6113 js::ReportUsageErrorASCII(
6114 cx
, callee
, "ArrayBuffer or ArrayBufferView argument required");
6118 bool pin
= args
.get(1).isUndefined() ? true : ToBoolean(args
.get(1));
6120 args
.rval().setBoolean(JS::PinArrayBufferOrViewLength(obj
, pin
));
6124 static bool JSONStringify(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6125 CallArgs args
= CallArgsFromVp(argc
, vp
);
6127 RootedValue
value(cx
, args
.get(0));
6128 RootedValue
behaviorVal(cx
, args
.get(1));
6129 StringifyBehavior behavior
= StringifyBehavior::Normal
;
6130 if (behaviorVal
.isString()) {
6132 #define MATCH(name) \
6133 if (!JS_StringEqualsLiteral(cx, behaviorVal.toString(), #name, &matches)) { \
6137 behavior = StringifyBehavior::name; \
6146 JSStringBuilder
sb(cx
);
6147 if (!Stringify(cx
, &value
, nullptr, UndefinedValue(), sb
, behavior
)) {
6152 JSString
* str
= sb
.finishString();
6156 args
.rval().setString(str
);
6158 args
.rval().setUndefined();
6164 static bool HelperThreadCount(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6165 CallArgs args
= CallArgsFromVp(argc
, vp
);
6167 if (js::SupportDifferentialTesting()) {
6168 // Always return 0 to get consistent output with and without --no-threads.
6169 args
.rval().setInt32(0);
6173 if (CanUseExtraThreads()) {
6174 args
.rval().setInt32(GetHelperThreadCount());
6176 args
.rval().setInt32(0);
6181 static bool EnableShapeConsistencyChecks(JSContext
* cx
, unsigned argc
,
6183 CallArgs args
= CallArgsFromVp(argc
, vp
);
6185 NativeObject::enableShapeConsistencyChecks();
6187 args
.rval().setUndefined();
6191 // ShapeSnapshot holds information about an object's properties. This is used
6192 // for checking object and shape changes between two points in time.
6193 class ShapeSnapshot
{
6194 HeapPtr
<JSObject
*> object_
;
6195 HeapPtr
<Shape
*> shape_
;
6196 HeapPtr
<BaseShape
*> baseShape_
;
6197 ObjectFlags objectFlags_
;
6199 GCVector
<HeapPtr
<Value
>, 8> slots_
;
6201 struct PropertySnapshot
{
6202 HeapPtr
<PropMap
*> propMap
;
6203 uint32_t propMapIndex
;
6204 HeapPtr
<PropertyKey
> key
;
6207 explicit PropertySnapshot(PropMap
* map
, uint32_t index
)
6209 propMapIndex(index
),
6210 key(map
->getKey(index
)),
6211 prop(map
->getPropertyInfo(index
)) {}
6213 void trace(JSTracer
* trc
) {
6214 TraceEdge(trc
, &propMap
, "propMap");
6215 TraceEdge(trc
, &key
, "key");
6218 bool operator==(const PropertySnapshot
& other
) const {
6219 return propMap
== other
.propMap
&& propMapIndex
== other
.propMapIndex
&&
6220 key
== other
.key
&& prop
== other
.prop
;
6222 bool operator!=(const PropertySnapshot
& other
) const {
6223 return !operator==(other
);
6226 GCVector
<PropertySnapshot
, 8> properties_
;
6229 explicit ShapeSnapshot(JSContext
* cx
) : slots_(cx
), properties_(cx
) {}
6230 void checkSelf(JSContext
* cx
) const;
6231 void check(JSContext
* cx
, const ShapeSnapshot
& other
) const;
6232 bool init(JSObject
* obj
);
6233 void trace(JSTracer
* trc
);
6235 JSObject
* object() const { return object_
; }
6238 // A JSObject that holds a ShapeSnapshot.
6239 class ShapeSnapshotObject
: public NativeObject
{
6240 static constexpr size_t SnapshotSlot
= 0;
6241 static constexpr size_t ReservedSlots
= 1;
6244 static const JSClassOps classOps_
;
6245 static const JSClass class_
;
6247 bool hasSnapshot() const {
6248 // The snapshot may not be present yet if we GC during initialization.
6249 return !getReservedSlot(SnapshotSlot
).isUndefined();
6252 ShapeSnapshot
& snapshot() const {
6253 void* ptr
= getReservedSlot(SnapshotSlot
).toPrivate();
6255 return *static_cast<ShapeSnapshot
*>(ptr
);
6258 static ShapeSnapshotObject
* create(JSContext
* cx
, HandleObject obj
);
6260 static void finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
6261 if (obj
->as
<ShapeSnapshotObject
>().hasSnapshot()) {
6262 js_delete(&obj
->as
<ShapeSnapshotObject
>().snapshot());
6265 static void trace(JSTracer
* trc
, JSObject
* obj
) {
6266 if (obj
->as
<ShapeSnapshotObject
>().hasSnapshot()) {
6267 obj
->as
<ShapeSnapshotObject
>().snapshot().trace(trc
);
6272 /*static */ const JSClassOps
ShapeSnapshotObject::classOps_
= {
6273 nullptr, // addProperty
6274 nullptr, // delProperty
6275 nullptr, // enumerate
6276 nullptr, // newEnumerate
6278 nullptr, // mayResolve
6279 ShapeSnapshotObject::finalize
, // finalize
6281 nullptr, // construct
6282 ShapeSnapshotObject::trace
, // trace
6285 /*static */ const JSClass
ShapeSnapshotObject::class_
= {
6286 "ShapeSnapshotObject",
6287 JSCLASS_HAS_RESERVED_SLOTS(ShapeSnapshotObject::ReservedSlots
) |
6288 JSCLASS_BACKGROUND_FINALIZE
,
6289 &ShapeSnapshotObject::classOps_
,
6292 bool ShapeSnapshot::init(JSObject
* obj
) {
6294 shape_
= obj
->shape();
6295 baseShape_
= shape_
->base();
6296 objectFlags_
= shape_
->objectFlags();
6298 if (obj
->is
<NativeObject
>()) {
6299 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
6301 // Snapshot the slot values.
6302 size_t slotSpan
= nobj
->slotSpan();
6303 if (!slots_
.growBy(slotSpan
)) {
6306 for (size_t i
= 0; i
< slotSpan
; i
++) {
6307 slots_
[i
] = nobj
->getSlot(i
);
6310 // Snapshot property information.
6311 if (uint32_t len
= nobj
->shape()->propMapLength(); len
> 0) {
6312 PropMap
* map
= nobj
->shape()->propMap();
6314 for (uint32_t i
= 0; i
< len
; i
++) {
6315 if (!map
->hasKey(i
)) {
6318 if (!properties_
.append(PropertySnapshot(map
, i
))) {
6322 if (!map
->hasPrevious()) {
6325 map
= map
->asLinked()->previous();
6326 len
= PropMap::Capacity
;
6334 void ShapeSnapshot::trace(JSTracer
* trc
) {
6335 TraceEdge(trc
, &object_
, "object");
6336 TraceEdge(trc
, &shape_
, "shape");
6337 TraceEdge(trc
, &baseShape_
, "baseShape");
6339 properties_
.trace(trc
);
6342 void ShapeSnapshot::checkSelf(JSContext
* cx
) const {
6343 // Assertions based on a single snapshot.
6345 // Non-dictionary shapes must not be mutated.
6346 if (!shape_
->isDictionary()) {
6347 MOZ_RELEASE_ASSERT(shape_
->base() == baseShape_
);
6348 MOZ_RELEASE_ASSERT(shape_
->objectFlags() == objectFlags_
);
6351 for (const PropertySnapshot
& propSnapshot
: properties_
) {
6352 PropMap
* propMap
= propSnapshot
.propMap
;
6353 uint32_t propMapIndex
= propSnapshot
.propMapIndex
;
6354 PropertyInfo prop
= propSnapshot
.prop
;
6356 // Skip if the map no longer matches the snapshotted data. This can
6357 // only happen for dictionary maps because they can be mutated or compacted
6358 // after a shape change.
6359 if (!propMap
->hasKey(propMapIndex
) ||
6360 PropertySnapshot(propMap
, propMapIndex
) != propSnapshot
) {
6361 MOZ_RELEASE_ASSERT(propMap
->isDictionary());
6362 MOZ_RELEASE_ASSERT(object_
->shape() != shape_
);
6366 // Ensure ObjectFlags depending on property information are set if needed.
6367 ObjectFlags expectedFlags
= GetObjectFlagsForNewProperty(
6368 shape_
->getObjectClass(), shape_
->objectFlags(), propSnapshot
.key
,
6370 MOZ_RELEASE_ASSERT(expectedFlags
== objectFlags_
);
6372 // Accessors must have a PrivateGCThingValue(GetterSetter*) slot value.
6373 if (prop
.isAccessorProperty()) {
6374 Value slotVal
= slots_
[prop
.slot()];
6375 MOZ_RELEASE_ASSERT(slotVal
.isPrivateGCThing());
6376 MOZ_RELEASE_ASSERT(slotVal
.toGCThing()->is
<GetterSetter
>());
6379 // Data properties must not have a PrivateGCThingValue slot value.
6380 if (prop
.isDataProperty()) {
6381 Value slotVal
= slots_
[prop
.slot()];
6382 MOZ_RELEASE_ASSERT(!slotVal
.isPrivateGCThing());
6387 void ShapeSnapshot::check(JSContext
* cx
, const ShapeSnapshot
& later
) const {
6389 later
.checkSelf(cx
);
6391 if (object_
!= later
.object_
) {
6392 // Snapshots are for different objects. Assert dictionary shapes aren't
6394 if (object_
->is
<NativeObject
>()) {
6395 NativeObject
* nobj
= &object_
->as
<NativeObject
>();
6396 if (nobj
->inDictionaryMode()) {
6397 MOZ_RELEASE_ASSERT(nobj
->shape() != later
.shape_
);
6403 // We have two snapshots for the same object. Check the shape information
6404 // wasn't changed in invalid ways.
6406 // If the Shape is still the same, the object must have the same BaseShape,
6407 // ObjectFlags and property information.
6408 if (shape_
== later
.shape_
) {
6409 MOZ_RELEASE_ASSERT(objectFlags_
== later
.objectFlags_
);
6410 MOZ_RELEASE_ASSERT(baseShape_
== later
.baseShape_
);
6411 MOZ_RELEASE_ASSERT(slots_
.length() == later
.slots_
.length());
6412 MOZ_RELEASE_ASSERT(properties_
.length() == later
.properties_
.length());
6414 for (size_t i
= 0; i
< properties_
.length(); i
++) {
6415 MOZ_RELEASE_ASSERT(properties_
[i
] == later
.properties_
[i
]);
6416 // Non-configurable accessor properties and non-configurable, non-writable
6417 // data properties shouldn't have had their slot mutated.
6418 PropertyInfo prop
= properties_
[i
].prop
;
6419 if (!prop
.configurable()) {
6420 if (prop
.isAccessorProperty() ||
6421 (prop
.isDataProperty() && !prop
.writable())) {
6422 size_t slot
= prop
.slot();
6423 MOZ_RELEASE_ASSERT(slots_
[slot
] == later
.slots_
[slot
]);
6429 // Object flags should not be lost. The exception is the Indexed flag, it
6430 // can be cleared when densifying elements, so clear that flag first.
6432 ObjectFlags flags
= objectFlags_
;
6433 ObjectFlags flagsLater
= later
.objectFlags_
;
6434 flags
.clearFlag(ObjectFlag::Indexed
);
6435 flagsLater
.clearFlag(ObjectFlag::Indexed
);
6436 MOZ_RELEASE_ASSERT((flags
.toRaw() & flagsLater
.toRaw()) == flags
.toRaw());
6439 // If the HadGetterSetterChange flag wasn't set, all GetterSetter slots must
6441 if (!later
.objectFlags_
.hasFlag(ObjectFlag::HadGetterSetterChange
)) {
6442 for (size_t i
= 0; i
< slots_
.length(); i
++) {
6443 if (slots_
[i
].isPrivateGCThing() &&
6444 slots_
[i
].toGCThing()->is
<GetterSetter
>()) {
6445 MOZ_RELEASE_ASSERT(i
< later
.slots_
.length());
6446 MOZ_RELEASE_ASSERT(later
.slots_
[i
] == slots_
[i
]);
6453 ShapeSnapshotObject
* ShapeSnapshotObject::create(JSContext
* cx
,
6455 Rooted
<UniquePtr
<ShapeSnapshot
>> snapshot(cx
,
6456 cx
->make_unique
<ShapeSnapshot
>(cx
));
6457 if (!snapshot
|| !snapshot
->init(obj
)) {
6461 auto* snapshotObj
= NewObjectWithGivenProto
<ShapeSnapshotObject
>(cx
, nullptr);
6465 snapshotObj
->initReservedSlot(SnapshotSlot
, PrivateValue(snapshot
.release()));
6469 static bool CreateShapeSnapshot(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6470 CallArgs args
= CallArgsFromVp(argc
, vp
);
6472 if (!args
.get(0).isObject()) {
6473 JS_ReportErrorASCII(cx
, "createShapeSnapshot requires an object argument");
6477 RootedObject
obj(cx
, &args
[0].toObject());
6478 auto* res
= ShapeSnapshotObject::create(cx
, obj
);
6483 res
->snapshot().check(cx
, res
->snapshot());
6485 args
.rval().setObject(*res
);
6489 static bool CheckShapeSnapshot(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6490 CallArgs args
= CallArgsFromVp(argc
, vp
);
6492 if (!args
.get(0).isObject() ||
6493 !args
[0].toObject().is
<ShapeSnapshotObject
>()) {
6494 JS_ReportErrorASCII(cx
, "checkShapeSnapshot requires a snapshot argument");
6498 // Get the object to use from the snapshot if the second argument is not an
6500 RootedObject
obj(cx
);
6501 if (args
.get(1).isObject()) {
6502 obj
= &args
[1].toObject();
6504 auto& snapshot
= args
[0].toObject().as
<ShapeSnapshotObject
>().snapshot();
6505 obj
= snapshot
.object();
6508 RootedObject
otherSnapshot(cx
, ShapeSnapshotObject::create(cx
, obj
));
6509 if (!otherSnapshot
) {
6513 auto& snapshot1
= args
[0].toObject().as
<ShapeSnapshotObject
>().snapshot();
6514 auto& snapshot2
= otherSnapshot
->as
<ShapeSnapshotObject
>().snapshot();
6515 snapshot1
.check(cx
, snapshot2
);
6517 args
.rval().setUndefined();
6521 #if defined(DEBUG) || defined(JS_JITSPEW)
6522 static bool DumpObject(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6523 CallArgs args
= CallArgsFromVp(argc
, vp
);
6524 RootedObject
obj(cx
, ToObject(cx
, args
.get(0)));
6531 args
.rval().setUndefined();
6535 static bool DumpValue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6536 CallArgs args
= CallArgsFromVp(argc
, vp
);
6537 args
.get(0).get().dump();
6538 args
.rval().setUndefined();
6542 static bool DumpValueToString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6543 CallArgs args
= CallArgsFromVp(argc
, vp
);
6549 args
.get(0).get().dump(out
);
6551 JSString
* rep
= out
.release(cx
);
6556 args
.rval().setString(rep
);
6561 static bool SharedMemoryEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6562 CallArgs args
= CallArgsFromVp(argc
, vp
);
6563 args
.rval().setBoolean(
6564 cx
->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
6568 static bool SharedArrayRawBufferRefcount(JSContext
* cx
, unsigned argc
,
6570 CallArgs args
= CallArgsFromVp(argc
, vp
);
6571 if (args
.length() != 1 || !args
[0].isObject()) {
6572 JS_ReportErrorASCII(cx
, "Expected SharedArrayBuffer object");
6575 RootedObject
obj(cx
, &args
[0].toObject());
6576 if (!obj
->is
<SharedArrayBufferObject
>()) {
6577 JS_ReportErrorASCII(cx
, "Expected SharedArrayBuffer object");
6580 args
.rval().setInt32(
6581 obj
->as
<SharedArrayBufferObject
>().rawBufferObject()->refcount());
6585 #ifdef NIGHTLY_BUILD
6586 static bool ObjectAddress(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6587 CallArgs args
= CallArgsFromVp(argc
, vp
);
6589 if (js::SupportDifferentialTesting()) {
6590 RootedObject
callee(cx
, &args
.callee());
6591 ReportUsageErrorASCII(cx
, callee
,
6592 "Function unavailable in differential testing mode.");
6596 if (args
.length() != 1) {
6597 RootedObject
callee(cx
, &args
.callee());
6598 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
6601 if (!args
[0].isObject()) {
6602 RootedObject
callee(cx
, &args
.callee());
6603 ReportUsageErrorASCII(cx
, callee
, "Expected object");
6607 void* ptr
= js::UncheckedUnwrap(&args
[0].toObject(), true);
6609 SprintfLiteral(buffer
, "%p", ptr
);
6611 return ReturnStringCopy(cx
, args
, buffer
);
6614 static bool SharedAddress(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6615 CallArgs args
= CallArgsFromVp(argc
, vp
);
6617 if (js::SupportDifferentialTesting()) {
6618 RootedObject
callee(cx
, &args
.callee());
6619 ReportUsageErrorASCII(cx
, callee
,
6620 "Function unavailable in differential testing mode.");
6624 if (args
.length() != 1) {
6625 RootedObject
callee(cx
, &args
.callee());
6626 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
6629 if (!args
[0].isObject()) {
6630 RootedObject
callee(cx
, &args
.callee());
6631 ReportUsageErrorASCII(cx
, callee
, "Expected object");
6635 RootedObject
obj(cx
, CheckedUnwrapStatic(&args
[0].toObject()));
6637 ReportAccessDenied(cx
);
6640 if (!obj
->is
<SharedArrayBufferObject
>()) {
6641 JS_ReportErrorASCII(cx
, "Argument must be a SharedArrayBuffer");
6645 uint32_t nchar
= SprintfLiteral(
6647 obj
->as
<SharedArrayBufferObject
>().dataPointerShared().unwrap(
6650 JSString
* str
= JS_NewStringCopyN(cx
, buffer
, nchar
);
6655 args
.rval().setString(str
);
6661 static bool HasInvalidatedTeleporting(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6662 CallArgs args
= CallArgsFromVp(argc
, vp
);
6664 if (args
.length() != 1 || !args
[0].isObject()) {
6665 RootedObject
callee(cx
, &args
.callee());
6666 ReportUsageErrorASCII(cx
, callee
, "Expected single object argument");
6670 args
.rval().setBoolean(args
[0].toObject().hasInvalidatedTeleporting());
6674 static bool DumpBacktrace(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6675 CallArgs args
= CallArgsFromVp(argc
, vp
);
6677 args
.rval().setUndefined();
6681 static bool GetBacktrace(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6682 CallArgs args
= CallArgsFromVp(argc
, vp
);
6684 bool showArgs
= false;
6685 bool showLocals
= false;
6686 bool showThisProps
= false;
6688 if (args
.length() > 1) {
6689 RootedObject
callee(cx
, &args
.callee());
6690 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
6694 if (args
.length() == 1) {
6695 RootedObject
cfg(cx
, ToObject(cx
, args
[0]));
6701 if (!JS_GetProperty(cx
, cfg
, "args", &v
)) {
6704 showArgs
= ToBoolean(v
);
6706 if (!JS_GetProperty(cx
, cfg
, "locals", &v
)) {
6709 showLocals
= ToBoolean(v
);
6711 if (!JS_GetProperty(cx
, cfg
, "thisprops", &v
)) {
6714 showThisProps
= ToBoolean(v
);
6717 JS::UniqueChars buf
=
6718 JS::FormatStackDump(cx
, showArgs
, showLocals
, showThisProps
);
6724 UniqueTwoByteChars
ucbuf(JS::LossyUTF8CharsToNewTwoByteCharsZ(
6725 cx
, JS::UTF8Chars(buf
.get(), strlen(buf
.get())),
6726 &len
, js::MallocArena
)
6731 JSString
* str
= JS_NewUCStringCopyN(cx
, ucbuf
.get(), len
);
6736 args
.rval().setString(str
);
6740 static bool ReportOutOfMemory(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6741 CallArgs args
= CallArgsFromVp(argc
, vp
);
6742 JS_ReportOutOfMemory(cx
);
6743 cx
->clearPendingException();
6744 args
.rval().setUndefined();
6748 static bool ThrowOutOfMemory(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6749 JS_ReportOutOfMemory(cx
);
6753 static bool ReportLargeAllocationFailure(JSContext
* cx
, unsigned argc
,
6755 CallArgs args
= CallArgsFromVp(argc
, vp
);
6757 size_t bytes
= JSRuntime::LARGE_ALLOCATION
;
6758 if (args
.length() >= 1) {
6759 if (!args
[0].isInt32()) {
6760 RootedObject
callee(cx
, &args
.callee());
6761 ReportUsageErrorASCII(cx
, callee
,
6762 "First argument must be an integer if specified.");
6765 bytes
= args
[0].toInt32();
6768 void* buf
= cx
->runtime()->onOutOfMemoryCanGC(AllocFunction::Malloc
,
6769 js::MallocArena
, bytes
);
6772 args
.rval().setUndefined();
6776 namespace heaptools
{
6778 using EdgeName
= UniqueTwoByteChars
;
6780 // An edge to a node from its predecessor in a path through the graph.
6782 // The node from which this edge starts.
6783 JS::ubi::Node predecessor_
;
6785 // The name of this edge.
6789 BackEdge() : name_(nullptr) {}
6790 // Construct an initialized back edge, taking ownership of |name|.
6791 BackEdge(JS::ubi::Node predecessor
, EdgeName name
)
6792 : predecessor_(predecessor
), name_(std::move(name
)) {}
6793 BackEdge(BackEdge
&& rhs
)
6794 : predecessor_(rhs
.predecessor_
), name_(std::move(rhs
.name_
)) {}
6795 BackEdge
& operator=(BackEdge
&& rhs
) {
6796 MOZ_ASSERT(&rhs
!= this);
6798 new (this) BackEdge(std::move(rhs
));
6802 EdgeName
forgetName() { return std::move(name_
); }
6803 JS::ubi::Node
predecessor() const { return predecessor_
; }
6806 // No copy constructor or copying assignment.
6807 BackEdge(const BackEdge
&) = delete;
6808 BackEdge
& operator=(const BackEdge
&) = delete;
6811 // A path-finding handler class for use with JS::ubi::BreadthFirst.
6812 struct FindPathHandler
{
6813 using NodeData
= BackEdge
;
6814 using Traversal
= JS::ubi::BreadthFirst
<FindPathHandler
>;
6816 FindPathHandler(JSContext
* cx
, JS::ubi::Node start
, JS::ubi::Node target
,
6817 MutableHandle
<GCVector
<Value
>> nodes
, Vector
<EdgeName
>& edges
)
6825 bool operator()(Traversal
& traversal
, JS::ubi::Node origin
,
6826 const JS::ubi::Edge
& edge
, BackEdge
* backEdge
, bool first
) {
6827 // We take care of each node the first time we visit it, so there's
6828 // nothing to be done on subsequent visits.
6833 // Record how we reached this node. This is the last edge on a
6834 // shortest path to this node.
6836 DuplicateStringToArena(js::StringBufferArena
, cx
, edge
.name
.get());
6840 *backEdge
= BackEdge(origin
, std::move(edgeName
));
6842 // Have we reached our final target node?
6843 if (edge
.referent
== target
) {
6844 // Record the path that got us here, which must be a shortest path.
6845 if (!recordPath(traversal
, backEdge
)) {
6855 // We've found a path to our target. Walk the backlinks to produce the
6856 // (reversed) path, saving the path in |nodes| and |edges|. |nodes| is
6857 // rooted, so it can hold the path's nodes as we leave the scope of
6858 // the AutoCheckCannotGC. Note that nodes are added to |visited| after we
6859 // return from operator() so we have to pass the target BackEdge* to this
6861 bool recordPath(Traversal
& traversal
, BackEdge
* targetBackEdge
) {
6862 JS::ubi::Node here
= target
;
6865 BackEdge
* backEdge
= targetBackEdge
;
6866 if (here
!= target
) {
6867 Traversal::NodeMap::Ptr p
= traversal
.visited
.lookup(here
);
6869 backEdge
= &p
->value();
6871 JS::ubi::Node predecessor
= backEdge
->predecessor();
6872 if (!nodes
.append(predecessor
.exposeToJS()) ||
6873 !edges
.append(backEdge
->forgetName())) {
6877 } while (here
!= start
);
6884 // The node we're starting from.
6885 JS::ubi::Node start
;
6887 // The node we're looking for.
6888 JS::ubi::Node target
;
6890 // True if we found a path to target, false if we didn't.
6893 // The nodes and edges of the path --- should we find one. The path is
6894 // stored in reverse order, because that's how it's easiest for us to
6896 // - edges[i] is the name of the edge from nodes[i] to nodes[i-1].
6897 // - edges[0] is the name of the edge from nodes[0] to the target.
6898 // - The last node, nodes[n-1], is the start node.
6899 MutableHandle
<GCVector
<Value
>> nodes
;
6900 Vector
<EdgeName
>& edges
;
6903 } // namespace heaptools
6905 static bool FindPath(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6906 CallArgs args
= CallArgsFromVp(argc
, vp
);
6907 if (!args
.requireAtLeast(cx
, "findPath", 2)) {
6911 // We don't ToString non-objects given as 'start' or 'target', because this
6912 // test is all about object identity, and ToString doesn't preserve that.
6913 // Non-GCThing endpoints don't make much sense.
6914 if (!args
[0].isObject() && !args
[0].isString() && !args
[0].isSymbol()) {
6915 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[0],
6916 nullptr, "not an object, string, or symbol");
6920 if (!args
[1].isObject() && !args
[1].isString() && !args
[1].isSymbol()) {
6921 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[0],
6922 nullptr, "not an object, string, or symbol");
6926 Rooted
<GCVector
<Value
>> nodes(cx
, GCVector
<Value
>(cx
));
6927 Vector
<heaptools::EdgeName
> edges(cx
);
6930 // We can't tolerate the GC moving things around while we're searching
6931 // the heap. Check that nothing we do causes a GC.
6932 JS::AutoCheckCannotGC autoCannotGC
;
6934 JS::ubi::Node
start(args
[0]), target(args
[1]);
6936 heaptools::FindPathHandler
handler(cx
, start
, target
, &nodes
, edges
);
6937 heaptools::FindPathHandler::Traversal
traversal(cx
, handler
, autoCannotGC
);
6938 if (!traversal
.addStart(start
)) {
6939 ReportOutOfMemory(cx
);
6943 if (!traversal
.traverse()) {
6944 if (!cx
->isExceptionPending()) {
6945 ReportOutOfMemory(cx
);
6950 if (!handler
.foundPath
) {
6951 // We didn't find any paths from the start to the target.
6952 args
.rval().setUndefined();
6957 // |nodes| and |edges| contain the path from |start| to |target|, reversed.
6958 // Construct a JavaScript array describing the path from the start to the
6959 // target. Each element has the form:
6962 // node: <object or string or symbol>,
6963 // edge: <string describing outgoing edge from node>
6966 // or, if the node is some internal thing that isn't a proper JavaScript
6969 // { node: undefined, edge: <string> }
6970 size_t length
= nodes
.length();
6971 Rooted
<ArrayObject
*> result(cx
, NewDenseFullyAllocatedArray(cx
, length
));
6975 result
->ensureDenseInitializedLength(0, length
);
6977 // Walk |nodes| and |edges| in the stored order, and construct the result
6978 // array in start-to-target order.
6979 for (size_t i
= 0; i
< length
; i
++) {
6980 // Build an object describing the node and edge.
6981 RootedObject
obj(cx
, NewPlainObject(cx
));
6986 // Only define the "node" property if we're not fuzzing, to prevent the
6987 // fuzzers from messing with internal objects that we don't want to expose
6990 RootedValue
wrapped(cx
, nodes
[i
]);
6991 if (!cx
->compartment()->wrap(cx
, &wrapped
)) {
6995 if (!JS_DefineProperty(cx
, obj
, "node", wrapped
, JSPROP_ENUMERATE
)) {
7000 heaptools::EdgeName edgeName
= std::move(edges
[i
]);
7002 size_t edgeNameLength
= js_strlen(edgeName
.get());
7003 RootedString
edgeStr(
7004 cx
, NewString
<CanGC
>(cx
, std::move(edgeName
), edgeNameLength
));
7009 if (!JS_DefineProperty(cx
, obj
, "edge", edgeStr
, JSPROP_ENUMERATE
)) {
7013 result
->setDenseElement(length
- i
- 1, ObjectValue(*obj
));
7016 args
.rval().setObject(*result
);
7020 static bool ShortestPaths(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7021 CallArgs args
= CallArgsFromVp(argc
, vp
);
7022 if (!args
.requireAtLeast(cx
, "shortestPaths", 1)) {
7026 if (!args
[0].isObject() || !args
[0].toObject().is
<ArrayObject
>()) {
7027 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[0],
7028 nullptr, "not an array object");
7032 Rooted
<ArrayObject
*> objs(cx
, &args
[0].toObject().as
<ArrayObject
>());
7034 RootedValue
start(cx
, NullValue());
7035 int32_t maxNumPaths
= 3;
7037 if (!args
.get(1).isUndefined()) {
7038 if (!args
[1].isObject()) {
7039 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[1],
7040 nullptr, "not an options object");
7044 RootedObject
options(cx
, &args
[1].toObject());
7046 if (!JS_HasProperty(cx
, options
, "start", &exists
)) {
7050 if (!JS_GetProperty(cx
, options
, "start", &start
)) {
7054 // Non-GCThing endpoints don't make much sense.
7055 if (!start
.isGCThing()) {
7056 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, start
,
7057 nullptr, "not a GC thing");
7062 RootedValue
v(cx
, Int32Value(maxNumPaths
));
7063 if (!JS_HasProperty(cx
, options
, "maxNumPaths", &exists
)) {
7067 if (!JS_GetProperty(cx
, options
, "maxNumPaths", &v
)) {
7070 if (!JS::ToInt32(cx
, v
, &maxNumPaths
)) {
7074 if (maxNumPaths
<= 0) {
7075 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, v
,
7076 nullptr, "not greater than 0");
7081 // Ensure we have at least one target.
7082 size_t length
= objs
->getDenseInitializedLength();
7084 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[0],
7086 "not a dense array object with one or more elements");
7090 for (size_t i
= 0; i
< length
; i
++) {
7091 RootedValue
el(cx
, objs
->getDenseElement(i
));
7092 if (!el
.isGCThing()) {
7093 JS_ReportErrorASCII(cx
, "Each target must be a GC thing");
7098 // We accumulate the results into a GC-stable form, due to the fact that the
7099 // JS::ubi::ShortestPaths lifetime (when operating on the live heap graph)
7100 // is bounded within an AutoCheckCannotGC.
7101 Rooted
<GCVector
<GCVector
<GCVector
<Value
>>>> values(
7102 cx
, GCVector
<GCVector
<GCVector
<Value
>>>(cx
));
7103 Vector
<Vector
<Vector
<JS::ubi::EdgeName
>>> names(cx
);
7108 JS::ubi::RootList
rootList(cx
, true);
7109 if (start
.isNull()) {
7110 auto [ok
, nogc
] = rootList
.init();
7111 (void)nogc
; // Old compilers get anxious about nogc being unused.
7113 ReportOutOfMemory(cx
);
7116 root
= JS::ubi::Node(&rootList
);
7118 root
= JS::ubi::Node(start
);
7120 JS::AutoCheckCannotGC
noGC(cx
);
7122 JS::ubi::NodeSet targets
;
7124 for (size_t i
= 0; i
< length
; i
++) {
7125 RootedValue
val(cx
, objs
->getDenseElement(i
));
7126 JS::ubi::Node
node(val
);
7127 if (!targets
.put(node
)) {
7128 ReportOutOfMemory(cx
);
7133 auto maybeShortestPaths
= JS::ubi::ShortestPaths::Create(
7134 cx
, noGC
, maxNumPaths
, root
, std::move(targets
));
7135 if (maybeShortestPaths
.isNothing()) {
7136 ReportOutOfMemory(cx
);
7139 auto& shortestPaths
= *maybeShortestPaths
;
7141 for (size_t i
= 0; i
< length
; i
++) {
7142 if (!values
.append(GCVector
<GCVector
<Value
>>(cx
)) ||
7143 !names
.append(Vector
<Vector
<JS::ubi::EdgeName
>>(cx
))) {
7147 RootedValue
val(cx
, objs
->getDenseElement(i
));
7148 JS::ubi::Node
target(val
);
7150 bool ok
= shortestPaths
.forEachPath(target
, [&](JS::ubi::Path
& path
) {
7151 Rooted
<GCVector
<Value
>> pathVals(cx
, GCVector
<Value
>(cx
));
7152 Vector
<JS::ubi::EdgeName
> pathNames(cx
);
7154 for (auto& part
: path
) {
7155 if (!pathVals
.append(part
->predecessor().exposeToJS()) ||
7156 !pathNames
.append(std::move(part
->name()))) {
7161 return values
.back().append(std::move(pathVals
.get())) &&
7162 names
.back().append(std::move(pathNames
));
7171 MOZ_ASSERT(values
.length() == names
.length());
7172 MOZ_ASSERT(values
.length() == length
);
7174 Rooted
<ArrayObject
*> results(cx
, NewDenseFullyAllocatedArray(cx
, length
));
7178 results
->ensureDenseInitializedLength(0, length
);
7180 for (size_t i
= 0; i
< length
; i
++) {
7181 size_t numPaths
= values
[i
].length();
7182 MOZ_ASSERT(names
[i
].length() == numPaths
);
7184 Rooted
<ArrayObject
*> pathsArray(cx
,
7185 NewDenseFullyAllocatedArray(cx
, numPaths
));
7189 pathsArray
->ensureDenseInitializedLength(0, numPaths
);
7191 for (size_t j
= 0; j
< numPaths
; j
++) {
7192 size_t pathLength
= values
[i
][j
].length();
7193 MOZ_ASSERT(names
[i
][j
].length() == pathLength
);
7195 Rooted
<ArrayObject
*> path(cx
,
7196 NewDenseFullyAllocatedArray(cx
, pathLength
));
7200 path
->ensureDenseInitializedLength(0, pathLength
);
7202 for (size_t k
= 0; k
< pathLength
; k
++) {
7203 Rooted
<PlainObject
*> part(cx
, NewPlainObject(cx
));
7208 RootedValue
predecessor(cx
, values
[i
][j
][k
]);
7209 if (!cx
->compartment()->wrap(cx
, &predecessor
) ||
7210 !JS_DefineProperty(cx
, part
, "predecessor", predecessor
,
7211 JSPROP_ENUMERATE
)) {
7215 if (names
[i
][j
][k
]) {
7216 RootedString
edge(cx
,
7217 NewStringCopyZ
<CanGC
>(cx
, names
[i
][j
][k
].get()));
7219 !JS_DefineProperty(cx
, part
, "edge", edge
, JSPROP_ENUMERATE
)) {
7224 path
->setDenseElement(k
, ObjectValue(*part
));
7227 pathsArray
->setDenseElement(j
, ObjectValue(*path
));
7230 results
->setDenseElement(i
, ObjectValue(*pathsArray
));
7233 args
.rval().setObject(*results
);
7237 static bool EvalReturningScope(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7238 CallArgs args
= CallArgsFromVp(argc
, vp
);
7239 if (!args
.requireAtLeast(cx
, "evalReturningScope", 1)) {
7243 RootedString
str(cx
, ToString(cx
, args
[0]));
7248 JS::AutoFilename filename
;
7251 JS::DescribeScriptedCaller(cx
, &filename
, &lineno
);
7253 // CompileOption should be created in the target global's realm.
7254 RootedObject
global(cx
);
7255 Maybe
<JS::CompileOptions
> maybeOptions
;
7256 if (args
.hasDefined(1)) {
7257 global
= ToObject(cx
, args
[1]);
7262 global
= CheckedUnwrapDynamic(global
, cx
, /* stopAtWindowProxy = */ false);
7264 JS_ReportErrorASCII(cx
, "Permission denied to access global");
7267 if (!global
->is
<GlobalObject
>()) {
7268 JS_ReportErrorASCII(cx
, "Argument must be a global object");
7272 JSAutoRealm
ar(cx
, global
);
7273 maybeOptions
.emplace(cx
);
7275 global
= JS::CurrentGlobalOrNull(cx
);
7276 maybeOptions
.emplace(cx
);
7279 CompileOptions
& options
= maybeOptions
.ref();
7280 options
.setFileAndLine(filename
.get(), lineno
);
7281 options
.setNoScriptRval(true);
7282 options
.setNonSyntacticScope(true);
7284 AutoStableStringChars
linearChars(cx
);
7285 if (!linearChars
.initTwoByte(cx
, str
)) {
7288 JS::SourceText
<char16_t
> srcBuf
;
7289 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
7293 RootedObject
varObj(cx
);
7296 // ExecuteInFrameScriptEnvironment requires the script be in the same
7297 // realm as the global. The script compilation should be done after
7298 // switching globals.
7299 AutoRealm
ar(cx
, global
);
7301 RootedScript
script(cx
, JS::Compile(cx
, options
, srcBuf
));
7306 JS::RootedObject
obj(cx
, JS_NewPlainObject(cx
));
7311 RootedObject
lexicalScope(cx
);
7312 if (!js::ExecuteInFrameScriptEnvironment(cx
, obj
, script
, &lexicalScope
)) {
7316 varObj
= lexicalScope
->enclosingEnvironment()->enclosingEnvironment();
7317 MOZ_ASSERT(varObj
->is
<NonSyntacticVariablesObject
>());
7320 RootedValue
varObjVal(cx
, ObjectValue(*varObj
));
7321 if (!cx
->compartment()->wrap(cx
, &varObjVal
)) {
7325 args
.rval().set(varObjVal
);
7329 static bool ByteSize(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7330 CallArgs args
= CallArgsFromVp(argc
, vp
);
7331 mozilla::MallocSizeOf mallocSizeOf
= cx
->runtime()->debuggerMallocSizeOf
;
7334 // We can't tolerate the GC moving things around while we're using a
7335 // ubi::Node. Check that nothing we do causes a GC.
7336 JS::AutoCheckCannotGC autoCannotGC
;
7338 JS::ubi::Node node
= args
.get(0);
7340 args
.rval().setNumber(uint32_t(node
.size(mallocSizeOf
)));
7342 args
.rval().setUndefined();
7348 static bool ByteSizeOfScript(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7349 CallArgs args
= CallArgsFromVp(argc
, vp
);
7350 if (!args
.requireAtLeast(cx
, "byteSizeOfScript", 1)) {
7353 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
7354 JS_ReportErrorASCII(cx
, "Argument must be a Function object");
7358 RootedFunction
fun(cx
, &args
[0].toObject().as
<JSFunction
>());
7359 if (fun
->isNativeFun()) {
7360 JS_ReportErrorASCII(cx
, "Argument must be a scripted function");
7364 RootedScript
script(cx
, JSFunction::getOrCreateScript(cx
, fun
));
7369 mozilla::MallocSizeOf mallocSizeOf
= cx
->runtime()->debuggerMallocSizeOf
;
7372 // We can't tolerate the GC moving things around while we're using a
7373 // ubi::Node. Check that nothing we do causes a GC.
7374 JS::AutoCheckCannotGC autoCannotGC
;
7376 JS::ubi::Node node
= script
;
7378 args
.rval().setNumber(uint32_t(node
.size(mallocSizeOf
)));
7380 args
.rval().setUndefined();
7386 static bool SetImmutablePrototype(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7387 CallArgs args
= CallArgsFromVp(argc
, vp
);
7388 if (!args
.get(0).isObject()) {
7389 JS_ReportErrorASCII(cx
, "setImmutablePrototype: object expected");
7393 RootedObject
obj(cx
, &args
[0].toObject());
7396 if (!js::SetImmutablePrototype(cx
, obj
, &succeeded
)) {
7400 args
.rval().setBoolean(succeeded
);
7404 #if defined(DEBUG) || defined(JS_JITSPEW)
7405 static bool DumpStringRepresentation(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7406 CallArgs args
= CallArgsFromVp(argc
, vp
);
7408 RootedString
str(cx
, ToString(cx
, args
.get(0)));
7413 Fprinter
out(stderr
);
7414 str
->dumpRepresentation(out
);
7416 args
.rval().setUndefined();
7420 static bool GetStringRepresentation(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7421 CallArgs args
= CallArgsFromVp(argc
, vp
);
7423 RootedString
str(cx
, ToString(cx
, args
.get(0)));
7432 str
->dumpRepresentation(out
);
7434 JSString
* rep
= out
.release(cx
);
7439 args
.rval().setString(rep
);
7444 static bool ParseCompileOptionsForModule(JSContext
* cx
,
7445 JS::CompileOptions
& options
,
7446 JS::Handle
<JSObject
*> opts
,
7448 JS::Rooted
<JS::Value
> v(cx
);
7450 if (!JS_GetProperty(cx
, opts
, "module", &v
)) {
7453 if (!v
.isUndefined() && JS::ToBoolean(v
)) {
7454 options
.setModule();
7457 if (!ValidateModuleCompileOptions(cx
, options
)) {
7467 static bool ParseCompileOptionsForInstantiate(JSContext
* cx
,
7468 JS::CompileOptions
& options
,
7469 JS::Handle
<JSObject
*> opts
,
7470 bool& prepareForInstantiate
) {
7471 JS::Rooted
<JS::Value
> v(cx
);
7473 if (!JS_GetProperty(cx
, opts
, "prepareForInstantiate", &v
)) {
7476 if (!v
.isUndefined()) {
7477 prepareForInstantiate
= JS::ToBoolean(v
);
7479 prepareForInstantiate
= false;
7485 static bool CompileToStencil(JSContext
* cx
, uint32_t argc
, Value
* vp
) {
7486 CallArgs args
= CallArgsFromVp(argc
, vp
);
7488 if (!args
.requireAtLeast(cx
, "compileToStencil", 1)) {
7491 if (!args
[0].isString()) {
7492 const char* typeName
= InformalValueTypeName(args
[0]);
7493 JS_ReportErrorASCII(cx
, "expected string to parse, got %s", typeName
);
7497 RootedString
src(cx
, ToString
<CanGC
>(cx
, args
[0]));
7502 /* Linearize the string to obtain a char16_t* range. */
7503 AutoStableStringChars
linearChars(cx
);
7504 if (!linearChars
.initTwoByte(cx
, src
)) {
7507 JS::SourceText
<char16_t
> srcBuf
;
7508 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
7512 CompileOptions
options(cx
);
7513 options
.setFile("<compileToStencil>");
7515 RootedString
displayURL(cx
);
7516 RootedString
sourceMapURL(cx
);
7517 UniqueChars fileNameBytes
;
7518 bool isModule
= false;
7519 bool prepareForInstantiate
= false;
7520 if (args
.length() == 2) {
7521 if (!args
[1].isObject()) {
7522 JS_ReportErrorASCII(
7523 cx
, "compileToStencil: The 2nd argument must be an object");
7527 RootedObject
opts(cx
, &args
[1].toObject());
7529 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
7532 if (!ParseCompileOptionsForModule(cx
, options
, opts
, isModule
)) {
7535 if (!ParseCompileOptionsForInstantiate(cx
, options
, opts
,
7536 prepareForInstantiate
)) {
7539 if (!js::ParseSourceOptions(cx
, opts
, &displayURL
, &sourceMapURL
)) {
7544 AutoReportFrontendContext
fc(cx
);
7545 RefPtr
<JS::Stencil
> stencil
;
7547 stencil
= JS::CompileModuleScriptToStencil(&fc
, options
, srcBuf
);
7549 stencil
= JS::CompileGlobalScriptToStencil(&fc
, options
, srcBuf
);
7555 if (!SetSourceOptions(cx
, &fc
, stencil
->source
, displayURL
, sourceMapURL
)) {
7559 JS::InstantiationStorage storage
;
7560 if (prepareForInstantiate
) {
7561 if (!JS::PrepareForInstantiate(&fc
, *stencil
, storage
)) {
7566 Rooted
<js::StencilObject
*> stencilObj(
7567 cx
, js::StencilObject::create(cx
, std::move(stencil
)));
7572 args
.rval().setObject(*stencilObj
);
7576 static bool EvalStencil(JSContext
* cx
, uint32_t argc
, Value
* vp
) {
7577 CallArgs args
= CallArgsFromVp(argc
, vp
);
7579 if (!args
.requireAtLeast(cx
, "evalStencil", 1)) {
7583 /* Prepare the input byte array. */
7584 if (!args
[0].isObject()) {
7585 JS_ReportErrorASCII(cx
, "evalStencil: Stencil object expected");
7588 Rooted
<js::StencilObject
*> stencilObj(
7589 cx
, args
[0].toObject().maybeUnwrapIf
<js::StencilObject
>());
7591 JS_ReportErrorASCII(cx
, "evalStencil: Stencil object expected");
7595 if (stencilObj
->stencil()->isModule()) {
7596 JS_ReportErrorASCII(cx
,
7597 "evalStencil: Module stencil cannot be evaluated. Use "
7598 "instantiateModuleStencil instead");
7602 CompileOptions
options(cx
);
7603 UniqueChars fileNameBytes
;
7604 Rooted
<JS::Value
> privateValue(cx
);
7605 Rooted
<JSString
*> elementAttributeName(cx
);
7606 if (args
.length() == 2) {
7607 if (!args
[1].isObject()) {
7608 JS_ReportErrorASCII(cx
,
7609 "evalStencil: The 2nd argument must be an object");
7613 RootedObject
opts(cx
, &args
[1].toObject());
7615 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
7618 if (!js::ParseDebugMetadata(cx
, opts
, &privateValue
,
7619 &elementAttributeName
)) {
7624 bool useDebugMetadata
= !privateValue
.isUndefined() || elementAttributeName
;
7626 JS::InstantiateOptions
instantiateOptions(options
);
7627 if (useDebugMetadata
) {
7628 instantiateOptions
.hideScriptFromDebugger
= true;
7631 if (!js::ValidateLazinessOfStencilAndGlobal(cx
, *stencilObj
->stencil())) {
7635 RootedScript
script(cx
, JS::InstantiateGlobalStencil(cx
, instantiateOptions
,
7636 stencilObj
->stencil()));
7641 if (useDebugMetadata
) {
7642 instantiateOptions
.hideScriptFromDebugger
= false;
7643 if (!JS::UpdateDebugMetadata(cx
, script
, instantiateOptions
, privateValue
,
7644 elementAttributeName
, nullptr, nullptr)) {
7649 RootedValue
retVal(cx
);
7650 if (!JS_ExecuteScript(cx
, script
, &retVal
)) {
7654 args
.rval().set(retVal
);
7658 static bool CompileToStencilXDR(JSContext
* cx
, uint32_t argc
, Value
* vp
) {
7659 CallArgs args
= CallArgsFromVp(argc
, vp
);
7661 if (!args
.requireAtLeast(cx
, "compileToStencilXDR", 1)) {
7665 RootedString
src(cx
, ToString
<CanGC
>(cx
, args
[0]));
7670 /* Linearize the string to obtain a char16_t* range. */
7671 AutoStableStringChars
linearChars(cx
);
7672 if (!linearChars
.initTwoByte(cx
, src
)) {
7675 JS::SourceText
<char16_t
> srcBuf
;
7676 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
7680 CompileOptions
options(cx
);
7681 options
.setFile("<compileToStencilXDR>");
7683 RootedString
displayURL(cx
);
7684 RootedString
sourceMapURL(cx
);
7685 UniqueChars fileNameBytes
;
7686 bool isModule
= false;
7687 if (args
.length() == 2) {
7688 if (!args
[1].isObject()) {
7689 JS_ReportErrorASCII(
7690 cx
, "compileToStencilXDR: The 2nd argument must be an object");
7694 RootedObject
opts(cx
, &args
[1].toObject());
7696 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
7699 if (!ParseCompileOptionsForModule(cx
, options
, opts
, isModule
)) {
7702 if (!js::ParseSourceOptions(cx
, opts
, &displayURL
, &sourceMapURL
)) {
7707 /* Compile the script text to stencil. */
7708 AutoReportFrontendContext
fc(cx
);
7709 frontend::NoScopeBindingCache scopeCache
;
7710 Rooted
<frontend::CompilationInput
> input(cx
,
7711 frontend::CompilationInput(options
));
7712 UniquePtr
<frontend::ExtensibleCompilationStencil
> stencil
;
7714 stencil
= frontend::ParseModuleToExtensibleStencil(
7715 cx
, &fc
, cx
->tempLifoAlloc(), input
.get(), &scopeCache
, srcBuf
);
7717 stencil
= frontend::CompileGlobalScriptToExtensibleStencil(
7718 cx
, &fc
, input
.get(), &scopeCache
, srcBuf
, ScopeKind::Global
);
7724 if (!SetSourceOptions(cx
, &fc
, stencil
->source
, displayURL
, sourceMapURL
)) {
7728 /* Serialize the stencil to XDR. */
7729 JS::TranscodeBuffer xdrBytes
;
7731 frontend::BorrowingCompilationStencil
borrowingStencil(*stencil
);
7732 bool succeeded
= false;
7733 if (!borrowingStencil
.serializeStencils(cx
, input
.get(), xdrBytes
,
7738 fc
.clearAutoReport();
7739 JS_ReportErrorASCII(cx
, "Encoding failure");
7744 Rooted
<StencilXDRBufferObject
*> xdrObj(
7746 StencilXDRBufferObject::create(cx
, xdrBytes
.begin(), xdrBytes
.length()));
7751 args
.rval().setObject(*xdrObj
);
7755 static bool EvalStencilXDR(JSContext
* cx
, uint32_t argc
, Value
* vp
) {
7756 CallArgs args
= CallArgsFromVp(argc
, vp
);
7758 if (!args
.requireAtLeast(cx
, "evalStencilXDR", 1)) {
7762 /* Prepare the input byte array. */
7763 if (!args
[0].isObject()) {
7764 JS_ReportErrorASCII(cx
, "evalStencilXDR: Stencil XDR object expected");
7767 Rooted
<StencilXDRBufferObject
*> xdrObj(
7768 cx
, args
[0].toObject().maybeUnwrapIf
<StencilXDRBufferObject
>());
7770 JS_ReportErrorASCII(cx
, "evalStencilXDR: Stencil XDR object expected");
7773 MOZ_ASSERT(xdrObj
->hasBuffer());
7775 CompileOptions
options(cx
);
7776 UniqueChars fileNameBytes
;
7777 Rooted
<JS::Value
> privateValue(cx
);
7778 Rooted
<JSString
*> elementAttributeName(cx
);
7779 if (args
.length() == 2) {
7780 if (!args
[1].isObject()) {
7781 JS_ReportErrorASCII(cx
,
7782 "evalStencilXDR: The 2nd argument must be an object");
7786 RootedObject
opts(cx
, &args
[1].toObject());
7788 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
7791 if (!js::ParseDebugMetadata(cx
, opts
, &privateValue
,
7792 &elementAttributeName
)) {
7797 /* Prepare the CompilationStencil for decoding. */
7798 AutoReportFrontendContext
fc(cx
);
7799 frontend::CompilationStencil
stencil(nullptr);
7801 /* Deserialize the stencil from XDR. */
7802 JS::TranscodeRange
xdrRange(xdrObj
->buffer(), xdrObj
->bufferLength());
7803 bool succeeded
= false;
7804 if (!stencil
.deserializeStencils(&fc
, options
, xdrRange
, &succeeded
)) {
7808 fc
.clearAutoReport();
7809 JS_ReportErrorASCII(cx
, "Decoding failure");
7813 if (stencil
.isModule()) {
7814 fc
.clearAutoReport();
7815 JS_ReportErrorASCII(cx
,
7816 "evalStencilXDR: Module stencil cannot be evaluated. "
7817 "Use instantiateModuleStencilXDR instead");
7821 if (!js::ValidateLazinessOfStencilAndGlobal(cx
, stencil
)) {
7825 bool useDebugMetadata
= !privateValue
.isUndefined() || elementAttributeName
;
7827 JS::InstantiateOptions
instantiateOptions(options
);
7828 if (useDebugMetadata
) {
7829 instantiateOptions
.hideScriptFromDebugger
= true;
7831 RootedScript
script(
7832 cx
, JS::InstantiateGlobalStencil(cx
, instantiateOptions
, &stencil
));
7837 if (useDebugMetadata
) {
7838 instantiateOptions
.hideScriptFromDebugger
= false;
7839 if (!JS::UpdateDebugMetadata(cx
, script
, instantiateOptions
, privateValue
,
7840 elementAttributeName
, nullptr, nullptr)) {
7845 RootedValue
retVal(cx
);
7846 if (!JS_ExecuteScript(cx
, script
, &retVal
)) {
7850 args
.rval().set(retVal
);
7854 static bool GetExceptionInfo(JSContext
* cx
, uint32_t argc
, Value
* vp
) {
7855 CallArgs args
= CallArgsFromVp(argc
, vp
);
7857 if (!args
.requireAtLeast(cx
, "getExceptionInfo", 1)) {
7861 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
7862 JS_ReportErrorASCII(cx
, "getExceptionInfo: expected function argument");
7866 RootedValue
rval(cx
);
7867 if (JS_CallFunctionValue(cx
, nullptr, args
[0], JS::HandleValueArray::empty(),
7869 // Function didn't throw.
7870 args
.rval().setNull();
7874 // We currently don't support interrupts or forced returns.
7875 if (!cx
->isExceptionPending()) {
7876 JS_ReportErrorASCII(cx
, "getExceptionInfo: unsupported exception status");
7880 RootedValue
excVal(cx
);
7881 Rooted
<SavedFrame
*> stack(cx
);
7882 if (!GetAndClearExceptionAndStack(cx
, &excVal
, &stack
)) {
7886 RootedValue
stackVal(cx
);
7888 RootedString
stackString(cx
);
7889 if (!BuildStackString(cx
, cx
->realm()->principals(), stack
, &stackString
)) {
7892 stackVal
.setString(stackString
);
7897 RootedObject
obj(cx
, NewPlainObject(cx
));
7902 if (!JS_DefineProperty(cx
, obj
, "exception", excVal
, JSPROP_ENUMERATE
)) {
7906 if (!JS_DefineProperty(cx
, obj
, "stack", stackVal
, JSPROP_ENUMERATE
)) {
7910 args
.rval().setObject(*obj
);
7914 class AllocationMarkerObject
: public NativeObject
{
7916 static const JSClass class_
;
7919 const JSClass
AllocationMarkerObject::class_
= {
7923 static bool AllocationMarker(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7924 CallArgs args
= CallArgsFromVp(argc
, vp
);
7926 bool allocateInsideNursery
= true;
7927 if (args
.length() > 0 && args
[0].isObject()) {
7928 RootedObject
options(cx
, &args
[0].toObject());
7930 RootedValue
nurseryVal(cx
);
7931 if (!JS_GetProperty(cx
, options
, "nursery", &nurseryVal
)) {
7934 allocateInsideNursery
= ToBoolean(nurseryVal
);
7938 allocateInsideNursery
7939 ? NewObjectWithGivenProto
<AllocationMarkerObject
>(cx
, nullptr)
7940 : NewTenuredObjectWithGivenProto
<AllocationMarkerObject
>(cx
, nullptr);
7945 args
.rval().setObject(*obj
);
7949 namespace gcCallback
{
7956 static void majorGC(JSContext
* cx
, JSGCStatus status
, JS::GCReason reason
,
7958 auto info
= static_cast<MajorGC
*>(data
);
7959 if (!(info
->phases
& (1 << status
))) {
7963 if (info
->depth
> 0) {
7965 JS::PrepareForFullGC(cx
);
7966 JS::NonIncrementalGC(cx
, JS::GCOptions::Normal
, JS::GCReason::API
);
7976 static void minorGC(JSContext
* cx
, JSGCStatus status
, JS::GCReason reason
,
7978 auto info
= static_cast<MinorGC
*>(data
);
7979 if (!(info
->phases
& (1 << status
))) {
7984 info
->active
= false;
7985 if (cx
->zone() && !cx
->zone()->isAtomsZone()) {
7986 cx
->runtime()->gc
.evictNursery(JS::GCReason::DEBUG_GC
);
7988 info
->active
= true;
7992 // Process global, should really be runtime-local.
7993 static MajorGC majorGCInfo
;
7994 static MinorGC minorGCInfo
;
7996 static void enterNullRealm(JSContext
* cx
, JSGCStatus status
,
7997 JS::GCReason reason
, void* data
) {
7998 JSAutoNullableRealm
enterRealm(cx
, nullptr);
8001 } /* namespace gcCallback */
8003 static bool SetGCCallback(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8004 CallArgs args
= CallArgsFromVp(argc
, vp
);
8006 if (args
.length() != 1) {
8007 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
8011 RootedObject
opts(cx
, ToObject(cx
, args
[0]));
8017 if (!JS_GetProperty(cx
, opts
, "action", &v
)) {
8021 JSString
* str
= JS::ToString(cx
, v
);
8025 Rooted
<JSLinearString
*> action(cx
, str
->ensureLinear(cx
));
8031 if (StringEqualsLiteral(action
, "minorGC") ||
8032 StringEqualsLiteral(action
, "majorGC")) {
8033 if (!JS_GetProperty(cx
, opts
, "phases", &v
)) {
8036 if (v
.isUndefined()) {
8037 phases
= (1 << JSGC_END
);
8039 JSString
* str
= JS::ToString(cx
, v
);
8043 JSLinearString
* phasesStr
= str
->ensureLinear(cx
);
8048 if (StringEqualsLiteral(phasesStr
, "begin")) {
8049 phases
= (1 << JSGC_BEGIN
);
8050 } else if (StringEqualsLiteral(phasesStr
, "end")) {
8051 phases
= (1 << JSGC_END
);
8052 } else if (StringEqualsLiteral(phasesStr
, "both")) {
8053 phases
= (1 << JSGC_BEGIN
) | (1 << JSGC_END
);
8055 JS_ReportErrorASCII(cx
, "Invalid callback phase");
8061 if (StringEqualsLiteral(action
, "minorGC")) {
8062 gcCallback::minorGCInfo
.phases
= phases
;
8063 gcCallback::minorGCInfo
.active
= true;
8064 JS_SetGCCallback(cx
, gcCallback::minorGC
, &gcCallback::minorGCInfo
);
8065 } else if (StringEqualsLiteral(action
, "majorGC")) {
8066 if (!JS_GetProperty(cx
, opts
, "depth", &v
)) {
8070 if (!v
.isUndefined()) {
8071 if (!ToInt32(cx
, v
, &depth
)) {
8076 JS_ReportErrorASCII(cx
, "Nesting depth cannot be negative");
8079 if (depth
+ gcstats::MAX_PHASE_NESTING
>
8080 gcstats::Statistics::MAX_SUSPENDED_PHASES
) {
8081 JS_ReportErrorASCII(cx
, "Nesting depth too large, would overflow");
8085 gcCallback::majorGCInfo
.phases
= phases
;
8086 gcCallback::majorGCInfo
.depth
= depth
;
8087 JS_SetGCCallback(cx
, gcCallback::majorGC
, &gcCallback::majorGCInfo
);
8088 } else if (StringEqualsLiteral(action
, "enterNullRealm")) {
8089 JS_SetGCCallback(cx
, gcCallback::enterNullRealm
, nullptr);
8091 JS_ReportErrorASCII(cx
, "Unknown GC callback action");
8095 args
.rval().setUndefined();
8100 static bool EnqueueMark(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8101 CallArgs args
= CallArgsFromVp(argc
, vp
);
8102 gc::GCRuntime
* gc
= &cx
->runtime()->gc
;
8104 if (gc
->isIncrementalGCInProgress() &&
8105 gc
->hasZealMode(gc::ZealMode::IncrementalMarkingValidator
)) {
8106 JS_ReportErrorASCII(
8108 "Can't add to test mark queue during incremental GC if marking "
8109 "validation is enabled as this can cause failures");
8113 if (args
.get(0).isString()) {
8114 RootedString
val(cx
, args
[0].toString());
8115 if (!val
->ensureLinear(cx
)) {
8118 if (!gc
->appendTestMarkQueue(StringValue(val
))) {
8119 JS_ReportOutOfMemory(cx
);
8122 } else if (args
.get(0).isObject()) {
8123 if (!gc
->appendTestMarkQueue(args
[0])) {
8124 JS_ReportOutOfMemory(cx
);
8128 JS_ReportErrorASCII(cx
, "Argument must be a string or object");
8132 args
.rval().setUndefined();
8136 static bool GetMarkQueue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8137 CallArgs args
= CallArgsFromVp(argc
, vp
);
8139 const auto& queue
= cx
->runtime()->gc
.getTestMarkQueue();
8141 RootedObject
result(cx
, JS::NewArrayObject(cx
, queue
.length()));
8145 for (size_t i
= 0; i
< queue
.length(); i
++) {
8146 RootedValue
val(cx
, queue
[i
]);
8147 if (!JS_WrapValue(cx
, &val
)) {
8150 if (!JS_SetElement(cx
, result
, i
, val
)) {
8155 args
.rval().setObject(*result
);
8159 static bool ClearMarkQueue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8160 CallArgs args
= CallArgsFromVp(argc
, vp
);
8162 cx
->runtime()->gc
.clearTestMarkQueue();
8163 args
.rval().setUndefined();
8168 static bool NurseryStringsEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8169 CallArgs args
= CallArgsFromVp(argc
, vp
);
8170 args
.rval().setBoolean(cx
->zone()->allocNurseryStrings());
8174 static bool IsNurseryAllocated(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8175 CallArgs args
= CallArgsFromVp(argc
, vp
);
8176 if (!args
.get(0).isGCThing()) {
8177 JS_ReportErrorASCII(
8178 cx
, "The function takes one argument, which must be a GC thing");
8182 args
.rval().setBoolean(IsInsideNursery(args
[0].toGCThing()));
8186 static bool NumAllocSitesPretenured(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8187 CallArgs args
= CallArgsFromVp(argc
, vp
);
8188 args
.rval().setInt32(cx
->realm()->numAllocSitesPretenured
);
8192 static bool GetLcovInfo(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8193 CallArgs args
= CallArgsFromVp(argc
, vp
);
8195 if (args
.length() > 1) {
8196 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
8200 if (!coverage::IsLCovEnabled()) {
8201 JS_ReportErrorASCII(cx
, "Coverage not enabled for process.");
8205 RootedObject
global(cx
);
8206 if (args
.hasDefined(0)) {
8207 global
= ToObject(cx
, args
[0]);
8209 JS_ReportErrorASCII(cx
, "Permission denied to access global");
8212 global
= CheckedUnwrapDynamic(global
, cx
, /* stopAtWindowProxy = */ false);
8214 ReportAccessDenied(cx
);
8217 if (!global
->is
<GlobalObject
>()) {
8218 JS_ReportErrorASCII(cx
, "Argument must be a global object");
8222 global
= JS::CurrentGlobalOrNull(cx
);
8226 UniqueChars content
;
8228 AutoRealm
ar(cx
, global
);
8229 content
= js::GetCodeCoverageSummary(cx
, &length
);
8237 JS_NewStringCopyUTF8N(cx
, JS::UTF8Chars(content
.get(), length
));
8242 args
.rval().setString(str
);
8247 static bool SetRNGState(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8248 CallArgs args
= CallArgsFromVp(argc
, vp
);
8249 if (!args
.requireAtLeast(cx
, "SetRNGState", 2)) {
8254 if (!ToNumber(cx
, args
[0], &d0
)) {
8259 if (!ToNumber(cx
, args
[1], &d1
)) {
8263 uint64_t seed0
= static_cast<uint64_t>(d0
);
8264 uint64_t seed1
= static_cast<uint64_t>(d1
);
8266 if (seed0
== 0 && seed1
== 0) {
8267 JS_ReportErrorASCII(cx
, "RNG requires non-zero seed");
8271 cx
->realm()->getOrCreateRandomNumberGenerator().setState(seed0
, seed1
);
8273 args
.rval().setUndefined();
8278 static bool GetTimeZone(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8279 CallArgs args
= CallArgsFromVp(argc
, vp
);
8280 RootedObject
callee(cx
, &args
.callee());
8282 if (args
.length() != 0) {
8283 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8288 auto getTimeZone
= [](std::time_t* now
) -> const char* {
8290 # if defined(_WIN32)
8292 if (localtime_s(&local
, now
) == 0) {
8293 return _tzname
[local
.tm_isdst
> 0];
8297 # if defined(HAVE_LOCALTIME_R)
8298 if (localtime_r(now
, &local
)) {
8300 std::tm
* localtm
= std::localtime(now
);
8303 # endif /* HAVE_LOCALTIME_R */
8305 # if defined(HAVE_TM_ZONE_TM_GMTOFF)
8306 return local
.tm_zone
;
8308 return tzname
[local
.tm_isdst
> 0];
8309 # endif /* HAVE_TM_ZONE_TM_GMTOFF */
8311 # endif /* _WIN32 */
8315 std::time_t now
= std::time(nullptr);
8316 if (now
!= static_cast<std::time_t>(-1)) {
8317 if (const char* tz
= getTimeZone(&now
)) {
8318 return ReturnStringCopy(cx
, args
, tz
);
8321 #endif /* __wasi__ */
8322 args
.rval().setUndefined();
8327 * Validate time zone input. Accepts the following formats:
8328 * - "America/Chicago" (raw time zone)
8329 * - ":America/Chicago"
8330 * - "/this-part-is-ignored/zoneinfo/America/Chicago"
8331 * - ":/this-part-is-ignored/zoneinfo/America/Chicago"
8332 * - "/etc/localtime"
8333 * - ":/etc/localtime"
8334 * Once the raw time zone is parsed out of the string, it is checked
8335 * against the time zones from GetAvailableTimeZones(). Throws an
8336 * Error if the time zone is invalid.
8338 #if defined(JS_HAS_INTL_API) && !defined(__wasi__)
8339 static bool ValidateTimeZone(JSContext
* cx
, const char* timeZone
) {
8340 static constexpr char zoneInfo
[] = "/zoneinfo/";
8341 static constexpr size_t zoneInfoLength
= sizeof(zoneInfo
) - 1;
8344 if (timeZone
[i
] == ':') {
8347 const char* zoneInfoPtr
= strstr(timeZone
, zoneInfo
);
8348 const char* timeZonePart
= timeZone
[i
] == '/' && zoneInfoPtr
8349 ? zoneInfoPtr
+ zoneInfoLength
8352 if (!*timeZonePart
) {
8353 JS_ReportErrorASCII(cx
, "Invalid time zone format");
8357 if (!strcmp(timeZonePart
, "/etc/localtime")) {
8361 auto timeZones
= mozilla::intl::TimeZone::GetAvailableTimeZones();
8362 if (timeZones
.isErr()) {
8363 intl::ReportInternalError(cx
, timeZones
.unwrapErr());
8366 for (auto timeZoneName
: timeZones
.unwrap()) {
8367 if (timeZoneName
.isErr()) {
8368 intl::ReportInternalError(cx
);
8372 if (!strcmp(timeZonePart
, timeZoneName
.unwrap().data())) {
8377 JS_ReportErrorASCII(cx
, "Unsupported time zone name: %s", timeZonePart
);
8382 static bool SetTimeZone(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8383 CallArgs args
= CallArgsFromVp(argc
, vp
);
8384 RootedObject
callee(cx
, &args
.callee());
8386 if (args
.length() != 1) {
8387 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8391 if (!args
[0].isString() && !args
[0].isUndefined()) {
8392 ReportUsageErrorASCII(cx
, callee
,
8393 "First argument should be a string or undefined");
8398 auto setTimeZone
= [](const char* value
) {
8399 # if defined(_WIN32)
8400 return _putenv_s("TZ", value
) == 0;
8402 return setenv("TZ", value
, true) == 0;
8403 # endif /* _WIN32 */
8406 auto unsetTimeZone
= []() {
8407 # if defined(_WIN32)
8408 return _putenv_s("TZ", "") == 0;
8410 return unsetenv("TZ") == 0;
8411 # endif /* _WIN32 */
8414 if (args
[0].isString() && !args
[0].toString()->empty()) {
8415 Rooted
<JSLinearString
*> str(cx
, args
[0].toString()->ensureLinear(cx
));
8420 if (!StringIsAscii(str
)) {
8421 ReportUsageErrorASCII(cx
, callee
,
8422 "First argument contains non-ASCII characters");
8426 UniqueChars timeZone
= JS_EncodeStringToASCII(cx
, str
);
8431 const char* timeZoneStr
= timeZone
.get();
8432 # ifdef JS_HAS_INTL_API
8433 if (!ValidateTimeZone(cx
, timeZoneStr
)) {
8438 if (!setTimeZone(timeZoneStr
)) {
8439 JS_ReportErrorASCII(cx
, "Failed to set 'TZ' environment variable");
8443 if (!unsetTimeZone()) {
8444 JS_ReportErrorASCII(cx
, "Failed to unset 'TZ' environment variable");
8449 # if defined(_WIN32)
8453 # endif /* _WIN32 */
8455 JS::ResetTimeZone();
8457 #endif /* __wasi__ */
8458 args
.rval().setUndefined();
8462 static bool GetCoreCount(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8463 CallArgs args
= CallArgsFromVp(argc
, vp
);
8464 RootedObject
callee(cx
, &args
.callee());
8466 if (args
.length() != 0) {
8467 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8471 args
.rval().setInt32(GetCPUCount());
8475 static bool GetDefaultLocale(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8476 CallArgs args
= CallArgsFromVp(argc
, vp
);
8477 RootedObject
callee(cx
, &args
.callee());
8479 if (args
.length() != 0) {
8480 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8484 UniqueChars locale
= JS_GetDefaultLocale(cx
);
8489 return ReturnStringCopy(cx
, args
, locale
.get());
8492 static bool SetDefaultLocale(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8493 CallArgs args
= CallArgsFromVp(argc
, vp
);
8494 RootedObject
callee(cx
, &args
.callee());
8496 if (args
.length() != 1) {
8497 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8501 if (!args
[0].isString() && !args
[0].isUndefined()) {
8502 ReportUsageErrorASCII(cx
, callee
,
8503 "First argument should be a string or undefined");
8507 if (args
[0].isString() && !args
[0].toString()->empty()) {
8508 RootedString
str(cx
, args
[0].toString());
8509 UniqueChars locale
= StringToLocale(cx
, callee
, str
);
8514 if (!JS_SetDefaultLocale(cx
->runtime(), locale
.get())) {
8515 ReportOutOfMemory(cx
);
8519 JS_ResetDefaultLocale(cx
->runtime());
8522 args
.rval().setUndefined();
8527 static bool AflLoop(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8528 CallArgs args
= CallArgsFromVp(argc
, vp
);
8531 if (!ToUint32(cx
, args
.get(0), &max_cnt
)) {
8535 args
.rval().setBoolean(!!__AFL_LOOP(max_cnt
));
8540 static bool MonotonicNow(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8541 CallArgs args
= CallArgsFromVp(argc
, vp
);
8544 // The std::chrono symbols are too new to be present in STL on all platforms we
8545 // care about, so use raw POSIX clock APIs when it might be necessary.
8546 #if defined(XP_UNIX) && !defined(XP_DARWIN)
8547 auto ComputeNow
= [](const timespec
& ts
) {
8548 return ts
.tv_sec
* 1000 + ts
.tv_nsec
/ 1000000;
8552 if (clock_gettime(CLOCK_MONOTONIC
, &ts
) == 0) {
8553 // Use a monotonic clock if available.
8554 now
= ComputeNow(ts
);
8556 // Use a realtime clock as fallback.
8557 if (clock_gettime(CLOCK_REALTIME
, &ts
) != 0) {
8558 // Fail if no clock is available.
8559 JS_ReportErrorASCII(cx
, "can't retrieve system clock");
8563 now
= ComputeNow(ts
);
8565 // Manually enforce atomicity on a non-monotonic clock.
8567 static mozilla::Atomic
<bool, mozilla::ReleaseAcquire
> spinLock
;
8568 while (!spinLock
.compareExchange(false, true)) {
8572 static double lastNow
= -FLT_MAX
;
8573 now
= lastNow
= std::max(now
, lastNow
);
8579 using std::chrono::duration_cast
;
8580 using std::chrono::milliseconds
;
8581 using std::chrono::steady_clock
;
8582 now
= duration_cast
<milliseconds
>(steady_clock::now().time_since_epoch())
8584 #endif // XP_UNIX && !XP_DARWIN
8586 args
.rval().setNumber(now
);
8590 static bool TimeSinceCreation(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8591 CallArgs args
= CallArgsFromVp(argc
, vp
);
8593 (mozilla::TimeStamp::Now() - mozilla::TimeStamp::ProcessCreation())
8595 args
.rval().setNumber(when
);
8599 static bool GetInnerMostEnvironmentObject(JSContext
* cx
, unsigned argc
,
8601 CallArgs args
= CallArgsFromVp(argc
, vp
);
8605 args
.rval().setNull();
8609 args
.rval().setObjectOrNull(iter
.environmentChain(cx
));
8613 static bool GetEnclosingEnvironmentObject(JSContext
* cx
, unsigned argc
,
8615 CallArgs args
= CallArgsFromVp(argc
, vp
);
8616 if (!args
.requireAtLeast(cx
, "getEnclosingEnvironmentObject", 1)) {
8620 if (!args
[0].isObject()) {
8621 args
.rval().setUndefined();
8625 JSObject
* envObj
= &args
[0].toObject();
8627 if (envObj
->is
<EnvironmentObject
>()) {
8628 EnvironmentObject
* env
= &envObj
->as
<EnvironmentObject
>();
8629 args
.rval().setObject(env
->enclosingEnvironment());
8633 if (envObj
->is
<DebugEnvironmentProxy
>()) {
8634 DebugEnvironmentProxy
* envProxy
= &envObj
->as
<DebugEnvironmentProxy
>();
8635 args
.rval().setObject(envProxy
->enclosingEnvironment());
8639 args
.rval().setNull();
8643 static bool GetEnvironmentObjectType(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8644 CallArgs args
= CallArgsFromVp(argc
, vp
);
8645 if (!args
.requireAtLeast(cx
, "getEnvironmentObjectType", 1)) {
8649 if (!args
[0].isObject()) {
8650 args
.rval().setUndefined();
8654 JSObject
* envObj
= &args
[0].toObject();
8656 if (envObj
->is
<EnvironmentObject
>()) {
8657 EnvironmentObject
* env
= &envObj
->as
<EnvironmentObject
>();
8658 JSString
* str
= JS_NewStringCopyZ(cx
, env
->typeString());
8659 args
.rval().setString(str
);
8662 if (envObj
->is
<DebugEnvironmentProxy
>()) {
8663 DebugEnvironmentProxy
* envProxy
= &envObj
->as
<DebugEnvironmentProxy
>();
8664 EnvironmentObject
* env
= &envProxy
->environment();
8665 char buf
[256] = {'\0'};
8666 SprintfLiteral(buf
, "[DebugProxy] %s", env
->typeString());
8667 JSString
* str
= JS_NewStringCopyZ(cx
, buf
);
8668 args
.rval().setString(str
);
8672 args
.rval().setUndefined();
8676 static bool AssertRealmFuseInvariants(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8677 CallArgs args
= CallArgsFromVp(argc
, vp
);
8678 // Note: This will crash if any invariant isn't held, so it's sufficient to
8679 // simply return true always.
8680 cx
->realm()->realmFuses
.assertInvariants(cx
);
8681 args
.rval().setUndefined();
8685 static bool GetFuseState(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8686 CallArgs args
= CallArgsFromVp(argc
, vp
);
8688 cx
->realm()->realmFuses
.assertInvariants(cx
);
8690 RootedObject
returnObj(cx
, JS_NewPlainObject(cx
));
8695 RootedObject
fuseObj(cx
);
8696 RootedString
intactStr(cx
, NewStringCopyZ
<CanGC
>(cx
, "intact"));
8701 RootedValue
intactValue(cx
);
8703 #define FUSE(Name, LowerName) \
8704 fuseObj = JS_NewPlainObject(cx); \
8708 intactValue.setBoolean(cx->realm()->realmFuses.LowerName.intact()); \
8709 if (!JS_DefineProperty(cx, fuseObj, "intact", intactValue, \
8710 JSPROP_ENUMERATE)) { \
8713 if (!JS_DefineProperty(cx, returnObj, #Name, fuseObj, JSPROP_ENUMERATE)) { \
8717 FOR_EACH_REALM_FUSE(FUSE
)
8720 // Register hasSeenUndefinedFuse
8721 fuseObj
= JS_NewPlainObject(cx
);
8725 intactValue
.setBoolean(
8726 cx
->runtime()->hasSeenObjectEmulateUndefinedFuse
.ref().intact());
8727 if (!JS_DefineProperty(cx
, fuseObj
, "intact", intactValue
,
8728 JSPROP_ENUMERATE
)) {
8732 if (!JS_DefineProperty(cx
, returnObj
, "hasSeenObjectEmulateUndefinedFuse",
8733 fuseObj
, JSPROP_ENUMERATE
)) {
8737 args
.rval().setObject(*returnObj
);
8741 static bool PopAllFusesInRealm(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8742 CallArgs args
= CallArgsFromVp(argc
, vp
);
8744 MOZ_ASSERT(cx
->realm());
8746 RealmFuses
& realmFuses
= cx
->realm()->realmFuses
;
8748 #define FUSE(Name, LowerName) realmFuses.LowerName.popFuse(cx, realmFuses);
8749 FOR_EACH_REALM_FUSE(FUSE
)
8752 args
.rval().setUndefined();
8756 static bool GetAllPrefNames(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8757 CallArgs args
= CallArgsFromVp(argc
, vp
);
8759 RootedValueVector
values(cx
);
8761 auto addPref
= [cx
, &values
](const char* name
) {
8762 JSString
* s
= JS_NewStringCopyZ(cx
, name
);
8766 return values
.append(StringValue(s
));
8769 #define ADD_NAME(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \
8770 if (!addPref(NAME)) { \
8773 FOR_EACH_JS_PREF(ADD_NAME
)
8776 ArrayObject
* arr
= NewDenseCopiedArray(cx
, values
.length(), values
.begin());
8781 args
.rval().setObject(*arr
);
8785 static bool GetPrefValue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8786 CallArgs args
= CallArgsFromVp(argc
, vp
);
8787 if (!args
.requireAtLeast(cx
, "getPrefValue", 1)) {
8791 if (!args
[0].isString()) {
8792 JS_ReportErrorASCII(cx
, "expected string argument");
8796 Rooted
<JSLinearString
*> name(cx
, args
[0].toString()->ensureLinear(cx
));
8801 auto setReturnValue
= [&args
](auto value
) {
8802 using T
= decltype(value
);
8803 if constexpr (std::is_same_v
<T
, bool>) {
8804 args
.rval().setBoolean(value
);
8806 static_assert(std::is_same_v
<T
, int32_t> || std::is_same_v
<T
, uint32_t>);
8807 args
.rval().setNumber(value
);
8811 // Search for a matching pref and return its value.
8812 #define CHECK_PREF(NAME, CPP_NAME, TYPE, SETTER, IS_STARTUP_PREF) \
8813 if (StringEqualsAscii(name, NAME)) { \
8814 setReturnValue(JS::Prefs::CPP_NAME()); \
8817 FOR_EACH_JS_PREF(CHECK_PREF
)
8820 JS_ReportErrorASCII(cx
, "invalid pref name");
8824 static bool GetErrorNotes(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8825 CallArgs args
= CallArgsFromVp(argc
, vp
);
8826 if (!args
.requireAtLeast(cx
, "getErrorNotes", 1)) {
8830 if (!args
[0].isObject() || !args
[0].toObject().is
<ErrorObject
>()) {
8831 args
.rval().setNull();
8835 JSErrorReport
* report
= args
[0].toObject().as
<ErrorObject
>().getErrorReport();
8837 args
.rval().setNull();
8841 RootedObject
notesArray(cx
, CreateErrorNotesArray(cx
, report
));
8846 args
.rval().setObject(*notesArray
);
8850 static bool IsConstructor(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8851 CallArgs args
= CallArgsFromVp(argc
, vp
);
8852 if (args
.length() < 1) {
8853 args
.rval().setBoolean(false);
8855 args
.rval().setBoolean(IsConstructor(args
[0]));
8860 static bool SetTimeResolution(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8861 CallArgs args
= CallArgsFromVp(argc
, vp
);
8862 RootedObject
callee(cx
, &args
.callee());
8864 if (!args
.requireAtLeast(cx
, "setTimeResolution", 2)) {
8868 if (!args
[0].isInt32()) {
8869 ReportUsageErrorASCII(cx
, callee
, "First argument must be an Int32.");
8872 int32_t resolution
= args
[0].toInt32();
8874 if (!args
[1].isBoolean()) {
8875 ReportUsageErrorASCII(cx
, callee
, "Second argument must be a Boolean");
8878 bool jitter
= args
[1].toBoolean();
8880 JS::SetTimeResolutionUsec(resolution
, jitter
);
8882 args
.rval().setUndefined();
8886 static bool ScriptedCallerGlobal(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8887 CallArgs args
= CallArgsFromVp(argc
, vp
);
8889 RootedObject
obj(cx
, JS::GetScriptedCallerGlobal(cx
));
8891 args
.rval().setNull();
8895 obj
= ToWindowProxyIfWindow(obj
);
8897 if (!cx
->compartment()->wrap(cx
, &obj
)) {
8901 args
.rval().setObject(*obj
);
8905 static bool ObjectGlobal(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8906 CallArgs args
= CallArgsFromVp(argc
, vp
);
8907 RootedObject
callee(cx
, &args
.callee());
8909 if (!args
.get(0).isObject()) {
8910 ReportUsageErrorASCII(cx
, callee
, "Argument must be an object");
8914 RootedObject
obj(cx
, &args
[0].toObject());
8915 if (IsCrossCompartmentWrapper(obj
)) {
8916 args
.rval().setNull();
8920 obj
= ToWindowProxyIfWindow(&obj
->nonCCWGlobal());
8922 args
.rval().setObject(*obj
);
8926 static bool IsSameCompartment(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8927 CallArgs args
= CallArgsFromVp(argc
, vp
);
8928 RootedObject
callee(cx
, &args
.callee());
8930 if (!args
.get(0).isObject() || !args
.get(1).isObject()) {
8931 ReportUsageErrorASCII(cx
, callee
, "Both arguments must be objects");
8935 RootedObject
obj1(cx
, UncheckedUnwrap(&args
[0].toObject()));
8936 RootedObject
obj2(cx
, UncheckedUnwrap(&args
[1].toObject()));
8938 args
.rval().setBoolean(obj1
->compartment() == obj2
->compartment());
8942 static bool FirstGlobalInCompartment(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8943 CallArgs args
= CallArgsFromVp(argc
, vp
);
8944 RootedObject
callee(cx
, &args
.callee());
8946 if (!args
.get(0).isObject()) {
8947 ReportUsageErrorASCII(cx
, callee
, "Argument must be an object");
8951 RootedObject
obj(cx
, UncheckedUnwrap(&args
[0].toObject()));
8952 obj
= ToWindowProxyIfWindow(GetFirstGlobalInCompartment(obj
->compartment()));
8954 if (!cx
->compartment()->wrap(cx
, &obj
)) {
8958 args
.rval().setObject(*obj
);
8962 static bool AssertCorrectRealm(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8963 CallArgs args
= CallArgsFromVp(argc
, vp
);
8964 MOZ_RELEASE_ASSERT(cx
->realm() == args
.callee().as
<JSFunction
>().realm());
8965 args
.rval().setUndefined();
8969 static bool GlobalLexicals(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8970 CallArgs args
= CallArgsFromVp(argc
, vp
);
8972 Rooted
<GlobalLexicalEnvironmentObject
*> globalLexical(
8973 cx
, &cx
->global()->lexicalEnvironment());
8975 RootedIdVector
props(cx
);
8976 if (!GetPropertyKeys(cx
, globalLexical
, JSITER_HIDDEN
, &props
)) {
8980 RootedObject
res(cx
, JS_NewPlainObject(cx
));
8985 RootedValue
val(cx
);
8986 for (size_t i
= 0; i
< props
.length(); i
++) {
8987 HandleId id
= props
[i
];
8988 if (!JS_GetPropertyById(cx
, globalLexical
, id
, &val
)) {
8991 if (val
.isMagic(JS_UNINITIALIZED_LEXICAL
)) {
8994 if (!JS_DefinePropertyById(cx
, res
, id
, val
, JSPROP_ENUMERATE
)) {
8999 args
.rval().setObject(*res
);
9003 static bool EncodeAsUtf8InBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9004 CallArgs args
= CallArgsFromVp(argc
, vp
);
9005 if (!args
.requireAtLeast(cx
, "encodeAsUtf8InBuffer", 2)) {
9009 RootedObject
callee(cx
, &args
.callee());
9011 if (!args
[0].isString()) {
9012 ReportUsageErrorASCII(cx
, callee
, "First argument must be a String");
9016 // Create the amounts array early so that the raw pointer into Uint8Array
9017 // data has as short a lifetime as possible
9018 Rooted
<ArrayObject
*> array(cx
, NewDenseFullyAllocatedArray(cx
, 2));
9022 array
->ensureDenseInitializedLength(0, 2);
9024 JSObject
* obj
= args
[1].isObject() ? &args
[1].toObject() : nullptr;
9025 Rooted
<JS::Uint8Array
> view(cx
, JS::Uint8Array::unwrap(obj
));
9027 ReportUsageErrorASCII(cx
, callee
, "Second argument must be a Uint8Array");
9031 mozilla::Span
<uint8_t> span
;
9032 bool isSharedMemory
= false;
9034 // The hazard analysis does not track the data pointer, so it can neither
9035 // tell that `data` is dead if ReportUsageErrorASCII is called, nor that
9036 // its live range ends at the call to AsWritableChars(). Construct a
9037 // temporary scope to hide from the analysis. This should really be replaced
9038 // with a safer mechanism.
9039 JS::AutoCheckCannotGC
nogc(cx
);
9040 if (!view
.isDetached()) {
9041 span
= view
.get().getData(&isSharedMemory
, nogc
);
9045 if (isSharedMemory
|| // exclude views of SharedArrayBuffers
9046 !span
.data()) { // exclude views of detached ArrayBuffers
9047 ReportUsageErrorASCII(
9049 "Second argument must be an unshared, non-detached Uint8Array");
9053 Maybe
<std::tuple
<size_t, size_t>> amounts
=
9054 JS_EncodeStringToUTF8BufferPartial(cx
, args
[0].toString(),
9055 AsWritableChars(span
));
9057 ReportOutOfMemory(cx
);
9061 auto [unitsRead
, bytesWritten
] = *amounts
;
9063 array
->initDenseElement(0, Int32Value(AssertedCast
<int32_t>(unitsRead
)));
9064 array
->initDenseElement(1, Int32Value(AssertedCast
<int32_t>(bytesWritten
)));
9066 args
.rval().setObject(*array
);
9070 JSScript
* js::TestingFunctionArgumentToScript(
9071 JSContext
* cx
, HandleValue v
, JSFunction
** funp
/* = nullptr */) {
9073 // To convert a string to a script, compile it. Parse it as an ES6 Program.
9074 Rooted
<JSString
*> str(cx
, v
.toString());
9075 AutoStableStringChars
linearChars(cx
);
9076 if (!linearChars
.initTwoByte(cx
, str
)) {
9079 SourceText
<char16_t
> source
;
9080 if (!source
.initMaybeBorrowed(cx
, linearChars
)) {
9084 CompileOptions
options(cx
);
9085 return JS::Compile(cx
, options
, source
);
9088 RootedFunction
fun(cx
, JS_ValueToFunction(cx
, v
));
9093 if (!fun
->isInterpreted()) {
9094 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
9095 JSMSG_TESTING_SCRIPTS_ONLY
);
9099 JSScript
* script
= JSFunction::getOrCreateScript(cx
, fun
);
9111 static bool BaselineCompile(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9112 CallArgs args
= CallArgsFromVp(argc
, vp
);
9113 RootedObject
callee(cx
, &args
.callee());
9115 RootedScript
script(cx
);
9116 if (args
.length() == 0) {
9117 NonBuiltinScriptFrameIter
iter(cx
);
9119 ReportUsageErrorASCII(cx
, callee
,
9120 "no script argument and no script caller");
9123 script
= iter
.script();
9125 script
= TestingFunctionArgumentToScript(cx
, args
[0]);
9131 bool forceDebug
= false;
9132 if (args
.length() > 1) {
9133 if (args
.length() > 2) {
9134 ReportUsageErrorASCII(cx
, callee
, "too many arguments");
9137 if (!args
[1].isBoolean() && !args
[1].isUndefined()) {
9138 ReportUsageErrorASCII(
9139 cx
, callee
, "forceDebugInstrumentation argument should be boolean");
9142 forceDebug
= ToBoolean(args
[1]);
9145 const char* returnedStr
= nullptr;
9147 // In order to check for differential behaviour, baselineCompile should have
9148 // the same output whether --no-baseline is used or not.
9149 if (js::SupportDifferentialTesting()) {
9150 returnedStr
= "skipped (differential testing)";
9154 AutoRealm
ar(cx
, script
);
9155 if (script
->hasBaselineScript()) {
9156 if (forceDebug
&& !script
->baselineScript()->hasDebugInstrumentation()) {
9157 // There isn't an easy way to do this for a script that might be on
9158 // stack right now. See
9159 // js::jit::RecompileOnStackBaselineScriptsForDebugMode.
9160 ReportUsageErrorASCII(
9161 cx
, callee
, "unsupported case: recompiling script for debug mode");
9165 args
.rval().setUndefined();
9169 if (!jit::IsBaselineJitEnabled(cx
)) {
9170 returnedStr
= "baseline disabled";
9173 if (!script
->canBaselineCompile()) {
9174 returnedStr
= "can't compile";
9177 if (!cx
->zone()->ensureJitZoneExists(cx
)) {
9181 jit::MethodStatus status
= jit::BaselineCompile(cx
, script
, forceDebug
);
9183 case jit::Method_Error
:
9185 case jit::Method_CantCompile
:
9186 returnedStr
= "can't compile";
9188 case jit::Method_Skipped
:
9189 returnedStr
= "skipped";
9191 case jit::Method_Compiled
:
9192 args
.rval().setUndefined();
9197 return ReturnStringCopy(cx
, args
, returnedStr
);
9203 static bool ClearKeptObjects(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9204 CallArgs args
= CallArgsFromVp(argc
, vp
);
9205 JS::ClearKeptObjects(cx
);
9206 args
.rval().setUndefined();
9210 static bool NumberToDouble(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9211 CallArgs args
= CallArgsFromVp(argc
, vp
);
9212 if (!args
.requireAtLeast(cx
, "numberToDouble", 1)) {
9216 if (!args
[0].isNumber()) {
9217 RootedObject
callee(cx
, &args
.callee());
9218 ReportUsageErrorASCII(cx
, callee
, "argument must be a number");
9222 args
.rval().setDouble(args
[0].toNumber());
9226 static bool GetICUOptions(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9227 CallArgs args
= CallArgsFromVp(argc
, vp
);
9229 RootedObject
info(cx
, JS_NewPlainObject(cx
));
9234 #ifdef JS_HAS_INTL_API
9235 RootedString
str(cx
);
9237 str
= NewStringCopy
<CanGC
>(cx
, mozilla::intl::ICU4CLibrary::GetVersion());
9238 if (!str
|| !JS_DefineProperty(cx
, info
, "version", str
, JSPROP_ENUMERATE
)) {
9242 str
= NewStringCopy
<CanGC
>(cx
, mozilla::intl::String::GetUnicodeVersion());
9243 if (!str
|| !JS_DefineProperty(cx
, info
, "unicode", str
, JSPROP_ENUMERATE
)) {
9247 str
= NewStringCopyZ
<CanGC
>(cx
, mozilla::intl::Locale::GetDefaultLocale());
9248 if (!str
|| !JS_DefineProperty(cx
, info
, "locale", str
, JSPROP_ENUMERATE
)) {
9252 auto tzdataVersion
= mozilla::intl::TimeZone::GetTZDataVersion();
9253 if (tzdataVersion
.isErr()) {
9254 intl::ReportInternalError(cx
, tzdataVersion
.unwrapErr());
9258 str
= NewStringCopy
<CanGC
>(cx
, tzdataVersion
.unwrap());
9259 if (!str
|| !JS_DefineProperty(cx
, info
, "tzdata", str
, JSPROP_ENUMERATE
)) {
9263 intl::FormatBuffer
<char16_t
, intl::INITIAL_CHAR_BUFFER_SIZE
> buf(cx
);
9265 if (auto ok
= DateTimeInfo::timeZoneId(DateTimeInfo::ForceUTC::No
, buf
);
9267 intl::ReportInternalError(cx
, ok
.unwrapErr());
9271 str
= buf
.toString(cx
);
9272 if (!str
|| !JS_DefineProperty(cx
, info
, "timezone", str
, JSPROP_ENUMERATE
)) {
9276 if (auto ok
= mozilla::intl::TimeZone::GetHostTimeZone(buf
); ok
.isErr()) {
9277 intl::ReportInternalError(cx
, ok
.unwrapErr());
9281 str
= buf
.toString(cx
);
9283 !JS_DefineProperty(cx
, info
, "host-timezone", str
, JSPROP_ENUMERATE
)) {
9288 args
.rval().setObject(*info
);
9292 static bool GetAvailableLocalesOf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9293 CallArgs args
= CallArgsFromVp(argc
, vp
);
9294 RootedObject
callee(cx
, &args
.callee());
9296 if (!args
.requireAtLeast(cx
, "getAvailableLocalesOf", 1)) {
9300 HandleValue arg
= args
[0];
9301 if (!arg
.isString()) {
9302 ReportUsageErrorASCII(cx
, callee
, "First argument must be a string");
9306 ArrayObject
* result
;
9307 #ifdef JS_HAS_INTL_API
9308 using SupportedLocaleKind
= js::intl::SharedIntlData::SupportedLocaleKind
;
9310 SupportedLocaleKind kind
;
9312 JSLinearString
* typeStr
= arg
.toString()->ensureLinear(cx
);
9317 if (StringEqualsLiteral(typeStr
, "Collator")) {
9318 kind
= SupportedLocaleKind::Collator
;
9319 } else if (StringEqualsLiteral(typeStr
, "DateTimeFormat")) {
9320 kind
= SupportedLocaleKind::DateTimeFormat
;
9321 } else if (StringEqualsLiteral(typeStr
, "DisplayNames")) {
9322 kind
= SupportedLocaleKind::DisplayNames
;
9323 } else if (StringEqualsLiteral(typeStr
, "ListFormat")) {
9324 kind
= SupportedLocaleKind::ListFormat
;
9325 } else if (StringEqualsLiteral(typeStr
, "NumberFormat")) {
9326 kind
= SupportedLocaleKind::NumberFormat
;
9327 } else if (StringEqualsLiteral(typeStr
, "PluralRules")) {
9328 kind
= SupportedLocaleKind::PluralRules
;
9329 } else if (StringEqualsLiteral(typeStr
, "RelativeTimeFormat")) {
9330 kind
= SupportedLocaleKind::RelativeTimeFormat
;
9331 } else if (StringEqualsLiteral(typeStr
, "Segmenter")) {
9332 kind
= SupportedLocaleKind::Segmenter
;
9334 ReportUsageErrorASCII(cx
, callee
, "Unsupported Intl constructor name");
9339 intl::SharedIntlData
& sharedIntlData
= cx
->runtime()->sharedIntlData
.ref();
9340 result
= sharedIntlData
.availableLocalesOf(cx
, kind
);
9342 result
= NewDenseEmptyArray(cx
);
9348 args
.rval().setObject(*result
);
9352 static bool IsSmallFunction(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9353 CallArgs args
= CallArgsFromVp(argc
, vp
);
9354 RootedObject
callee(cx
, &args
.callee());
9356 if (!args
.requireAtLeast(cx
, "IsSmallFunction", 1)) {
9360 HandleValue arg
= args
[0];
9361 if (!arg
.isObject() || !arg
.toObject().is
<JSFunction
>()) {
9362 ReportUsageErrorASCII(cx
, callee
, "First argument must be a function");
9366 RootedFunction
fun(cx
, &args
[0].toObject().as
<JSFunction
>());
9367 if (!fun
->isInterpreted()) {
9368 ReportUsageErrorASCII(cx
, callee
,
9369 "First argument must be an interpreted function");
9373 JSScript
* script
= JSFunction::getOrCreateScript(cx
, fun
);
9378 args
.rval().setBoolean(jit::JitOptions
.isSmallFunction(script
));
9382 static bool PCCountProfiling_Start(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9383 CallArgs args
= CallArgsFromVp(argc
, vp
);
9385 JS::StartPCCountProfiling(cx
);
9387 args
.rval().setUndefined();
9391 static bool PCCountProfiling_Stop(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9392 CallArgs args
= CallArgsFromVp(argc
, vp
);
9394 JS::StopPCCountProfiling(cx
);
9396 args
.rval().setUndefined();
9400 static bool PCCountProfiling_Purge(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9401 CallArgs args
= CallArgsFromVp(argc
, vp
);
9403 JS::PurgePCCounts(cx
);
9405 args
.rval().setUndefined();
9409 static bool PCCountProfiling_ScriptCount(JSContext
* cx
, unsigned argc
,
9411 CallArgs args
= CallArgsFromVp(argc
, vp
);
9413 size_t length
= JS::GetPCCountScriptCount(cx
);
9415 args
.rval().setNumber(double(length
));
9419 static bool PCCountProfiling_ScriptSummary(JSContext
* cx
, unsigned argc
,
9421 CallArgs args
= CallArgsFromVp(argc
, vp
);
9422 if (!args
.requireAtLeast(cx
, "summary", 1)) {
9427 if (!JS::ToUint32(cx
, args
[0], &index
)) {
9431 JSString
* str
= JS::GetPCCountScriptSummary(cx
, index
);
9436 args
.rval().setString(str
);
9440 static bool PCCountProfiling_ScriptContents(JSContext
* cx
, unsigned argc
,
9442 CallArgs args
= CallArgsFromVp(argc
, vp
);
9443 if (!args
.requireAtLeast(cx
, "contents", 1)) {
9448 if (!JS::ToUint32(cx
, args
[0], &index
)) {
9452 JSString
* str
= JS::GetPCCountScriptContents(cx
, index
);
9457 args
.rval().setString(str
);
9461 static bool NukeCCW(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9462 CallArgs args
= CallArgsFromVp(argc
, vp
);
9464 if (args
.length() != 1 || !args
[0].isObject() ||
9465 !IsCrossCompartmentWrapper(&args
[0].toObject())) {
9466 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_INVALID_ARGS
,
9471 NukeCrossCompartmentWrapper(cx
, &args
[0].toObject());
9472 args
.rval().setUndefined();
9476 static bool IsCCW(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9477 CallArgs args
= CallArgsFromVp(argc
, vp
);
9479 if (args
.length() != 1 || !args
[0].isObject()) {
9480 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_INVALID_ARGS
,
9485 args
.rval().setBoolean(IsCrossCompartmentWrapper(&args
[0].toObject()));
9489 static bool FdLibM_Pow(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9490 CallArgs args
= CallArgsFromVp(argc
, vp
);
9493 if (!JS::ToNumber(cx
, args
.get(0), &x
)) {
9498 if (!JS::ToNumber(cx
, args
.get(1), &y
)) {
9502 // Because C99 and ECMA specify different behavior for pow(), we need to wrap
9503 // the fdlibm call to make it ECMA compliant.
9504 if (!std::isfinite(y
) && (x
== 1.0 || x
== -1.0)) {
9505 args
.rval().setNaN();
9507 args
.rval().setDouble(fdlibm_pow(x
, y
));
9512 static bool HadOutOfMemory(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9513 CallArgs args
= CallArgsFromVp(argc
, vp
);
9514 args
.rval().setBoolean(cx
->runtime()->hadOutOfMemory
);
9519 static const JSFunctionSpecWithHelp TestingFunctions
[] = {
9520 JS_FN_HELP("gc", ::GC
, 0, 0,
9521 "gc([obj] | 'zone' [, ('shrinking' | 'last-ditch') ])",
9522 " Run the garbage collector.\n"
9523 " The first parameter describes which zones to collect: if an object is\n"
9524 " given, GC only its zone. If 'zone' is given, GC any zones that were\n"
9525 " scheduled via schedulegc.\n"
9526 " The second parameter is optional and may be 'shrinking' to perform a\n"
9527 " shrinking GC or 'last-ditch' for a shrinking, last-ditch GC."),
9529 JS_FN_HELP("minorgc", ::MinorGC
, 0, 0,
9530 "minorgc([aboutToOverflow])",
9531 " Run a minor collector on the Nursery. When aboutToOverflow is true, marks\n"
9532 " the store buffer as about-to-overflow before collecting."),
9534 JS_FN_HELP("maybegc", ::MaybeGC
, 0, 0,
9536 " Hint to the engine that now is an ok time to run the garbage collector.\n"),
9538 JS_FN_HELP("gcparam", GCParameter
, 2, 0,
9539 "gcparam(name [, value])",
9540 " Wrapper for JS_[GS]etGCParameter. The name is one of:" GC_PARAMETER_ARGS_LIST
),
9542 JS_FN_HELP("finishBackgroundFree", FinishBackgroundFree
, 0, 0,
9543 "finishBackgroundFree()",
9544 " Wait for the GC's background free task to finish.\n"),
9546 JS_FN_HELP("hasDisassembler", HasDisassembler
, 0, 0,
9547 "hasDisassembler()",
9548 " Return true if a disassembler is present (for disnative and wasmDis)."),
9550 JS_FN_HELP("disnative", DisassembleNative
, 2, 0,
9551 "disnative(fun,[path])",
9552 " Disassemble a function into its native code. Optionally write the native code bytes to a file on disk.\n"),
9554 JS_FN_HELP("relazifyFunctions", RelazifyFunctions
, 0, 0,
9555 "relazifyFunctions(...)",
9556 " Perform a GC and allow relazification of functions. Accepts the same\n"
9557 " arguments as gc()."),
9559 JS_FN_HELP("getBuildConfiguration", GetBuildConfiguration
, 1, 0,
9560 "getBuildConfiguration([option])",
9561 " Query the options SpiderMonkey was built with, or return an object\n"
9562 " with the options if no argument is given."),
9564 JS_FN_HELP("getRealmConfiguration", GetRealmConfiguration
, 1, 0,
9565 "getRealmConfiguration([option])",
9566 " Query the runtime options SpiderMonkey is running with, or return an\n."
9567 " object with the options if no argument is given."),
9569 JS_FN_HELP("isLcovEnabled", ::IsLCovEnabled
, 0, 0,
9571 " Return true if JS LCov support is enabled."),
9573 JS_FN_HELP("trialInline", TrialInline
, 0, 0,
9575 " Perform trial-inlining for the caller's frame if it's a BaselineFrame."),
9577 JS_FN_HELP("hasChild", HasChild
, 0, 0,
9578 "hasChild(parent, child)",
9579 " Return true if |child| is a child of |parent|, as determined by a call to\n"
9582 JS_FN_HELP("setSavedStacksRNGState", SetSavedStacksRNGState
, 1, 0,
9583 "setSavedStacksRNGState(seed)",
9584 " Set this compartment's SavedStacks' RNG state.\n"),
9586 JS_FN_HELP("getSavedFrameCount", GetSavedFrameCount
, 0, 0,
9587 "getSavedFrameCount()",
9588 " Return the number of SavedFrame instances stored in this compartment's\n"
9589 " SavedStacks cache."),
9591 JS_FN_HELP("clearSavedFrames", ClearSavedFrames
, 0, 0,
9592 "clearSavedFrames()",
9593 " Empty the current compartment's cache of SavedFrame objects, so that\n"
9594 " subsequent stack captures allocate fresh objects to represent frames.\n"
9595 " Clear the current stack's LiveSavedFrameCaches."),
9597 JS_FN_HELP("saveStack", SaveStack
, 0, 0,
9598 "saveStack([maxDepth [, compartment]])",
9599 " Capture a stack. If 'maxDepth' is given, capture at most 'maxDepth' number\n"
9600 " of frames. If 'compartment' is given, allocate the js::SavedFrame instances\n"
9601 " with the given object's compartment."),
9603 JS_FN_HELP("captureFirstSubsumedFrame", CaptureFirstSubsumedFrame
, 1, 0,
9604 "saveStack(object [, shouldIgnoreSelfHosted = true]])",
9605 " Capture a stack back to the first frame whose principals are subsumed by the\n"
9606 " object's compartment's principals. If 'shouldIgnoreSelfHosted' is given,\n"
9607 " control whether self-hosted frames are considered when checking principals."),
9609 JS_FN_HELP("callFunctionFromNativeFrame", CallFunctionFromNativeFrame
, 1, 0,
9610 "callFunctionFromNativeFrame(function)",
9611 " Call 'function' with a (C++-)native frame on stack.\n"
9612 " Required for testing that SaveStack properly handles native frames."),
9614 JS_FN_HELP("callFunctionWithAsyncStack", CallFunctionWithAsyncStack
, 0, 0,
9615 "callFunctionWithAsyncStack(function, stack, asyncCause)",
9616 " Call 'function', using the provided stack as the async stack responsible\n"
9617 " for the call, and propagate its return value or the exception it throws.\n"
9618 " The function is called with no arguments, and 'this' is 'undefined'. The\n"
9619 " specified |asyncCause| is attached to the provided stack frame."),
9621 JS_FN_HELP("enableTrackAllocations", EnableTrackAllocations
, 0, 0,
9622 "enableTrackAllocations()",
9623 " Start capturing the JS stack at every allocation. Note that this sets an\n"
9624 " object metadata callback that will override any other object metadata\n"
9625 " callback that may be set."),
9627 JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations
, 0, 0,
9628 "disableTrackAllocations()",
9629 " Stop capturing the JS stack at every allocation."),
9631 JS_FN_HELP("setTestFilenameValidationCallback", SetTestFilenameValidationCallback
, 0, 0,
9632 "setTestFilenameValidationCallback()",
9633 " Set the filename validation callback to a callback that accepts only\n"
9634 " filenames starting with 'safe' or (only in system realms) 'system'."),
9636 JS_FN_HELP("newObjectWithAddPropertyHook", NewObjectWithAddPropertyHook
, 0, 0,
9637 "newObjectWithAddPropertyHook()",
9638 " Returns a new object with an addProperty JSClass hook. This hook\n"
9639 " increments the value of the _propertiesAdded data property on the object\n"
9640 " when a new property is added."),
9642 JS_FN_HELP("newObjectWithCallHook", NewObjectWithCallHook
, 0, 0,
9643 "newObjectWithCallHook()",
9644 " Returns a new object with call/construct JSClass hooks. These hooks return\n"
9645 " a new object that contains the Values supplied by the caller."),
9647 JS_FN_HELP("newObjectWithManyReservedSlots", NewObjectWithManyReservedSlots
, 0, 0,
9648 "newObjectWithManyReservedSlots()",
9649 " Returns a new object with many reserved slots. The slots are initialized to int32\n"
9650 " values. checkObjectWithManyReservedSlots can be used to check the slots still\n"
9651 " hold these values."),
9653 JS_FN_HELP("checkObjectWithManyReservedSlots", CheckObjectWithManyReservedSlots
, 1, 0,
9654 "checkObjectWithManyReservedSlots(obj)",
9655 " Checks the reserved slots set by newObjectWithManyReservedSlots still hold the expected\n"
9658 JS_FN_HELP("getWatchtowerLog", GetWatchtowerLog
, 0, 0,
9659 "getWatchtowerLog()",
9660 " Returns the Watchtower log recording object changes for objects for which\n"
9661 " addWatchtowerTarget was called. The internal log is cleared. The return\n"
9662 " value is an array of plain objects with the following properties:\n"
9663 " - kind: a string describing the kind of mutation, for example \"add-prop\"\n"
9664 " - object: the object being mutated\n"
9665 " - extra: an extra value, for example the name of the property being added"),
9667 JS_FN_HELP("addWatchtowerTarget", AddWatchtowerTarget
, 1, 0,
9668 "addWatchtowerTarget(object)",
9669 " Invoke the watchtower callback for changes to this object."),
9671 JS_FN_HELP("newString", NewString
, 2, 0,
9672 "newString(str[, options])",
9673 " Copies str's chars and returns a new string. Valid options:\n"
9675 " - tenured: allocate directly into the tenured heap.\n"
9677 " - twoByte: create a \"two byte\" string, not a latin1 string, regardless of the\n"
9678 " input string's characters. Latin1 will be used by default if possible\n"
9679 " (again regardless of the input string.)\n"
9681 " - newStringBuffer: create a new string that uses a refcounted StringBuffer for\n"
9682 " the characters.\n"
9684 " - shareStringBuffer: create a new string that shares str's StringBuffer.\n"
9686 " - external: create an external string. External strings are always twoByte and\n"
9689 " - maybeExternal: create an external string, unless the data fits within an\n"
9690 " inline string. Inline strings may be nursery-allocated."),
9692 JS_FN_HELP("newDependentString", NewDependentString
, 2, 0,
9693 "newDependentString(str, indexStart[, indexEnd] [, options])",
9694 " Essentially the same as str.substring() but insist on\n"
9695 " creating a dependent string and failing if not. Also has options to\n"
9696 " control the heap the string object is allocated into:\n"
9698 " - tenured: if true, allocate in the tenured heap or throw. If false,\n"
9699 " allocate in the nursery or throw."),
9701 JS_FN_HELP("ensureLinearString", EnsureLinearString
, 1, 0,
9702 "ensureLinearString(str)",
9703 " Ensures str is a linear (non-rope) string and returns it."),
9705 JS_FN_HELP("representativeStringArray", RepresentativeStringArray
, 0, 0,
9706 "representativeStringArray()",
9707 " Returns an array of strings that represent the various internal string\n"
9708 " types and character encodings."),
9710 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
9712 JS_FN_HELP("oomThreadTypes", OOMThreadTypes
, 0, 0,
9714 " Get the number of thread types that can be used as an argument for\n"
9715 " oomAfterAllocations() and oomAtAllocation()."),
9717 JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations
, 2, 0,
9718 "oomAfterAllocations(count [,threadType])",
9719 " After 'count' js_malloc memory allocations, fail every following allocation\n"
9720 " (return nullptr). The optional thread type limits the effect to the\n"
9721 " specified type of helper thread."),
9723 JS_FN_HELP("oomAtAllocation", OOMAtAllocation
, 2, 0,
9724 "oomAtAllocation(count [,threadType])",
9725 " After 'count' js_malloc memory allocations, fail the next allocation\n"
9726 " (return nullptr). The optional thread type limits the effect to the\n"
9727 " specified type of helper thread."),
9729 JS_FN_HELP("resetOOMFailure", ResetOOMFailure
, 0, 0,
9730 "resetOOMFailure()",
9731 " Remove the allocation failure scheduled by either oomAfterAllocations() or\n"
9732 " oomAtAllocation() and return whether any allocation had been caused to fail."),
9734 JS_FN_HELP("oomTest", OOMTest
, 0, 0,
9735 "oomTest(function, [expectExceptionOnFailure = true | options])",
9736 " Test that the passed function behaves correctly under OOM conditions by\n"
9737 " repeatedly executing it and simulating allocation failure at successive\n"
9738 " allocations until the function completes without seeing a failure.\n"
9739 " By default this tests that an exception is raised if execution fails, but\n"
9740 " this can be disabled by passing false as the optional second parameter.\n"
9741 " This is also disabled when --fuzzing-safe is specified.\n"
9742 " Alternatively an object can be passed to set the following options:\n"
9743 " expectExceptionOnFailure: bool - as described above.\n"
9744 " keepFailing: bool - continue to fail after first simulated failure.\n"
9746 " WARNING: By design, oomTest assumes the test-function follows the same\n"
9747 " code path each time it is called, right up to the point where OOM occurs.\n"
9748 " If on iteration 70 it finishes and caches a unit of work that saves 65\n"
9749 " allocations the next time we run, then the subsequent 65 allocation\n"
9750 " points will go untested.\n"
9752 " Things in this category include lazy parsing and baseline compilation,\n"
9753 " so it is very easy to accidentally write an oomTest that only tests one\n"
9754 " or the other of those, and not the functionality you meant to test!\n"
9755 " To avoid lazy parsing, call the test function once first before passing\n"
9756 " it to oomTest. The jits can be disabled via the test harness.\n"),
9758 JS_FN_HELP("stackTest", StackTest
, 0, 0,
9759 "stackTest(function, [expectExceptionOnFailure = true])",
9760 " This function behaves exactly like oomTest with the difference that\n"
9761 " instead of simulating regular OOM conditions, it simulates the engine\n"
9762 " running out of stack space (failing recursion check).\n"
9764 " See the WARNING in help('oomTest').\n"),
9766 JS_FN_HELP("interruptTest", InterruptTest
, 0, 0,
9767 "interruptTest(function)",
9768 " This function simulates interrupts similar to how oomTest simulates OOM conditions."
9770 " See the WARNING in help('oomTest').\n"),
9772 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
9774 JS_FN_HELP("newRope", NewRope
, 3, 0,
9775 "newRope(left, right[, options])",
9776 " Creates a rope with the given left/right strings.\n"
9777 " Available options:\n"
9778 " nursery: bool - force the string to be created in/out of the nursery, if possible.\n"),
9780 JS_FN_HELP("isRope", IsRope
, 1, 0,
9782 " Returns true if the parameter is a rope"),
9784 JS_FN_HELP("settlePromiseNow", SettlePromiseNow
, 1, 0,
9785 "settlePromiseNow(promise)",
9786 " 'Settle' a 'promise' immediately. This just marks the promise as resolved\n"
9787 " with a value of `undefined` and causes the firing of any onPromiseSettled\n"
9788 " hooks set on Debugger instances that are observing the given promise's\n"
9789 " global as a debuggee."),
9790 JS_FN_HELP("getWaitForAllPromise", GetWaitForAllPromise
, 1, 0,
9791 "getWaitForAllPromise(densePromisesArray)",
9792 " Calls the 'GetWaitForAllPromise' JSAPI function and returns the result\n"
9794 JS_FN_HELP("resolvePromise", ResolvePromise
, 2, 0,
9795 "resolvePromise(promise, resolution)",
9796 " Resolve a Promise by calling the JSAPI function JS::ResolvePromise."),
9797 JS_FN_HELP("rejectPromise", RejectPromise
, 2, 0,
9798 "rejectPromise(promise, reason)",
9799 " Reject a Promise by calling the JSAPI function JS::RejectPromise."),
9801 JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver
, 0, 0,
9802 "makeFinalizeObserver()",
9803 " Get a special object whose finalization increases the counter returned\n"
9804 " by the finalizeCount function."),
9806 JS_FN_HELP("finalizeCount", FinalizeCount
, 0, 0,
9808 " Return the current value of the finalization counter that is incremented\n"
9809 " each time an object returned by the makeFinalizeObserver is finalized."),
9811 JS_FN_HELP("resetFinalizeCount", ResetFinalizeCount
, 0, 0,
9812 "resetFinalizeCount()",
9813 " Reset the value returned by finalizeCount()."),
9815 JS_FN_HELP("gcPreserveCode", GCPreserveCode
, 0, 0,
9817 " Preserve JIT code during garbage collections."),
9820 JS_FN_HELP("gczeal", GCZeal
, 2, 0,
9821 "gczeal([mode, [frequency]])",
9822 gc::ZealModeHelpText
),
9824 JS_FN_HELP("unsetgczeal", UnsetGCZeal
, 2, 0,
9825 "unsetgczeal(mode)",
9826 " Turn off a single zeal mode set with gczeal() and don't finish any ongoing\n"
9827 " collection that may be happening."),
9829 JS_FN_HELP("schedulegc", ScheduleGC
, 1, 0,
9830 "schedulegc([num])",
9831 " If num is given, schedule a GC after num allocations.\n"
9832 " Returns the number of allocations before the next trigger."),
9834 JS_FN_HELP("selectforgc", SelectForGC
, 0, 0,
9835 "selectforgc(obj1, obj2, ...)",
9836 " Schedule the given objects to be marked in the next GC slice."),
9838 JS_FN_HELP("verifyprebarriers", VerifyPreBarriers
, 0, 0,
9839 "verifyprebarriers()",
9840 " Start or end a run of the pre-write barrier verifier."),
9842 JS_FN_HELP("verifypostbarriers", VerifyPostBarriers
, 0, 0,
9843 "verifypostbarriers()",
9844 " Does nothing (the post-write barrier verifier has been remove)."),
9846 JS_FN_HELP("currentgc", CurrentGC
, 0, 0,
9848 " Report various information about the currently running incremental GC,\n"
9849 " if one is running."),
9851 JS_FN_HELP("deterministicgc", DeterministicGC
, 1, 0,
9852 "deterministicgc(true|false)",
9853 " If true, only allow determinstic GCs to run."),
9855 JS_FN_HELP("dumpGCArenaInfo", DumpGCArenaInfo
, 0, 0,
9856 "dumpGCArenaInfo()",
9857 " Prints information about the different GC things and how they are arranged\n"
9860 JS_FN_HELP("setMarkStackLimit", SetMarkStackLimit
, 1, 0,
9861 "markStackLimit(limit)",
9862 " Sets a limit on the number of words used for the mark stack. Used to test OOM"
9863 " handling during marking.\n"),
9867 JS_FN_HELP("gcstate", GCState
, 0, 0,
9869 " Report the global GC state, or the GC state for the zone containing |obj|."),
9871 JS_FN_HELP("schedulezone", ScheduleZoneForGC
, 1, 0,
9872 "schedulezone([obj | string])",
9873 " If obj is given, schedule a GC of obj's zone.\n"
9874 " If string is given, schedule a GC of the string's zone if possible."),
9876 JS_FN_HELP("startgc", StartGC
, 1, 0,
9877 "startgc([n [, 'shrinking']])",
9878 " Start an incremental GC and run a slice that processes about n objects.\n"
9879 " If 'shrinking' is passesd as the optional second argument, perform a\n"
9880 " shrinking GC rather than a normal GC. If no zones have been selected with\n"
9881 " schedulezone(), a full GC will be performed."),
9883 JS_FN_HELP("finishgc", FinishGC
, 0, 0,
9885 " Finish an in-progress incremental GC, if none is running then do nothing."),
9887 JS_FN_HELP("gcslice", GCSlice
, 1, 0,
9888 "gcslice([n [, options]])",
9889 " Start or continue an an incremental GC, running a slice that processes\n"
9890 " about n objects. Takes an optional options object, which may contain the\n"
9891 " following properties:\n"
9892 " dontStart: do not start a new incremental GC if one is not already\n"
9895 JS_FN_HELP("abortgc", AbortGC
, 1, 0,
9897 " Abort the current incremental GC."),
9899 JS_FN_HELP("setMallocMaxDirtyPageModifier", SetMallocMaxDirtyPageModifier
, 1, 0,
9900 "setMallocMaxDirtyPageModifier(value)",
9901 " Change the maximum size of jemalloc's page cache. The value should be between\n"
9902 " -5 and 16 (inclusive). See moz_set_max_dirty_page_modifier.\n"),
9904 JS_FN_HELP("fullcompartmentchecks", FullCompartmentChecks
, 1, 0,
9905 "fullcompartmentchecks(true|false)",
9906 " If true, check for compartment mismatches before every GC."),
9908 JS_FN_HELP("nondeterministicGetWeakMapKeys", NondeterministicGetWeakMapKeys
, 1, 0,
9909 "nondeterministicGetWeakMapKeys(weakmap)",
9910 " Return an array of the keys in the given WeakMap."),
9912 JS_FN_HELP("internalConst", InternalConst
, 1, 0,
9913 "internalConst(name)",
9914 " Query an internal constant for the engine. See InternalConst source for\n"
9915 " the list of constant names."),
9917 JS_FN_HELP("isProxy", IsProxy
, 1, 0,
9919 " If true, obj is a proxy of some sort"),
9921 JS_FN_HELP("dumpHeap", DumpHeap
, 1, 0,
9922 "dumpHeap([filename])",
9923 " Dump reachable and unreachable objects to the named file, or to stdout. Objects\n"
9924 " in the nursery are ignored, so if you wish to include them, consider calling\n"
9925 " minorgc() first."),
9927 JS_FN_HELP("terminate", Terminate
, 0, 0,
9929 " Terminate JavaScript execution, as if we had run out of\n"
9930 " memory or been terminated by the slow script dialog."),
9932 JS_FN_HELP("readGeckoProfilingStack", ReadGeckoProfilingStack
, 0, 0,
9933 "readGeckoProfilingStack()",
9934 " Reads the JIT/Wasm stack using ProfilingFrameIterator. Skips non-JIT/Wasm frames."),
9936 JS_FN_HELP("readGeckoInterpProfilingStack", ReadGeckoInterpProfilingStack
, 0, 0,
9937 "readGeckoInterpProfilingStack()",
9938 " Reads the C++ interpreter profiling stack. Skips JIT/Wasm frames."),
9940 JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks
, 0, 0,
9941 "enableOsiPointRegisterChecks()",
9942 " Emit extra code to verify live regs at the start of a VM call are not\n"
9943 " modified before its OsiPoint."),
9945 JS_FN_HELP("displayName", DisplayName
, 1, 0,
9947 " Gets the display name for a function, which can possibly be a guessed or\n"
9948 " inferred name based on where the function was defined. This can be\n"
9949 " different from the 'name' property on the function."),
9951 JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable
, 0, 0,
9952 "isAsmJSCompilationAvailable",
9953 " Returns whether asm.js compilation is currently available or whether it is disabled\n"
9954 " (e.g., by the debugger)."),
9956 JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions
, 0, 0,
9957 "getJitCompilerOptions()",
9958 " Return an object describing some of the JIT compiler options.\n"),
9960 JS_FN_HELP("isAsmJSModule", IsAsmJSModule
, 1, 0,
9961 "isAsmJSModule(fn)",
9962 " Returns whether the given value is a function containing \"use asm\" that has been\n"
9963 " validated according to the asm.js spec."),
9965 JS_FN_HELP("isAsmJSFunction", IsAsmJSFunction
, 1, 0,
9966 "isAsmJSFunction(fn)",
9967 " Returns whether the given value is a nested function in an asm.js module that has been\n"
9968 " both compile- and link-time validated."),
9970 JS_FN_HELP("isAvxPresent", IsAvxPresent
, 0, 0,
9971 "isAvxPresent([minVersion])",
9972 " Returns whether AVX is present and enabled. If minVersion specified,\n"
9973 " use 1 - to check if AVX is enabled (default), 2 - if AVX2 is enabled."),
9975 JS_FN_HELP("wasmIsSupported", WasmIsSupported
, 0, 0,
9976 "wasmIsSupported()",
9977 " Returns a boolean indicating whether WebAssembly is supported on the current device."),
9979 JS_FN_HELP("wasmIsSupportedByHardware", WasmIsSupportedByHardware
, 0, 0,
9980 "wasmIsSupportedByHardware()",
9981 " Returns a boolean indicating whether WebAssembly is supported on the current hardware (regardless of whether we've enabled support)."),
9983 JS_FN_HELP("wasmDebuggingEnabled", WasmDebuggingEnabled
, 0, 0,
9984 "wasmDebuggingEnabled()",
9985 " Returns a boolean indicating whether WebAssembly debugging is supported on the current device;\n"
9986 " returns false also if WebAssembly is not supported"),
9988 JS_FN_HELP("wasmStreamingEnabled", WasmStreamingEnabled
, 0, 0,
9989 "wasmStreamingEnabled()",
9990 " Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
9992 JS_FN_HELP("wasmCachingEnabled", WasmCachingEnabled
, 0, 0,
9993 "wasmCachingEnabled()",
9994 " Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
9996 JS_FN_HELP("wasmHugeMemorySupported", WasmHugeMemorySupported
, 0, 0,
9997 "wasmHugeMemorySupported()",
9998 " Returns a boolean indicating whether WebAssembly supports using a large"
9999 " virtual memory reservation in order to elide bounds checks on this platform."),
10001 JS_FN_HELP("wasmMaxMemoryPages", WasmMaxMemoryPages
, 1, 0,
10002 "wasmMaxMemoryPages(indexType)",
10003 " Returns an int with the maximum number of pages that can be allocated to a memory."
10004 " This is an implementation artifact that does depend on the index type, the hardware,"
10005 " the operating system, the build configuration, and flags. The result is constant for"
10006 " a given combination of those; there is no guarantee that that size allocation will"
10007 " always succeed, only that it can succeed in principle. The indexType is a string,"
10008 " 'i32' or 'i64'."),
10010 #define WASM_FEATURE(NAME, ...) \
10011 JS_FN_HELP("wasm" #NAME "Enabled", Wasm##NAME##Enabled, 0, 0, \
10012 "wasm" #NAME "Enabled()", \
10013 " Returns a boolean indicating whether the WebAssembly " #NAME " proposal is enabled."),
10014 JS_FOR_WASM_FEATURES(WASM_FEATURE
)
10015 #undef WASM_FEATURE
10017 JS_FN_HELP("wasmThreadsEnabled", WasmThreadsEnabled
, 0, 0,
10018 "wasmThreadsEnabled()",
10019 " Returns a boolean indicating whether the WebAssembly threads proposal is\n"
10020 " supported on the current device."),
10022 JS_FN_HELP("wasmSimdEnabled", WasmSimdEnabled
, 0, 0,
10023 "wasmSimdEnabled()",
10024 " Returns a boolean indicating whether WebAssembly SIMD proposal is\n"
10025 " supported by the current device."),
10027 #if defined(ENABLE_WASM_SIMD) && defined(DEBUG)
10028 JS_FN_HELP("wasmSimdAnalysis", WasmSimdAnalysis
, 1, 0,
10029 "wasmSimdAnalysis(...)",
10030 " Unstable API for white-box testing.\n"),
10033 JS_FN_HELP("wasmGlobalFromArrayBuffer", WasmGlobalFromArrayBuffer
, 2, 0,
10034 "wasmGlobalFromArrayBuffer(type, arrayBuffer)",
10035 " Create a WebAssembly.Global object from a provided ArrayBuffer. The type\n"
10036 " must be POD (i32, i64, f32, f64, v128). The buffer must be the same\n"
10037 " size as the type in bytes.\n"),
10038 JS_FN_HELP("wasmGlobalExtractLane", WasmGlobalExtractLane
, 3, 0,
10039 "wasmGlobalExtractLane(global, laneInterp, laneIndex)",
10040 " Extract a lane from a WebAssembly.Global object that contains a v128 value\n"
10041 " and return it as a new WebAssembly.Global object of the appropriate type.\n"
10042 " The supported laneInterp values are i32x4, i64x2, f32x4, and\n"
10044 JS_FN_HELP("wasmGlobalsEqual", WasmGlobalsEqual
, 2, 0,
10045 "wasmGlobalsEqual(globalA, globalB)",
10046 " Compares two WebAssembly.Global objects for if their types and values are\n"
10047 " equal. Mutability is not compared. Floating point values are compared for\n"
10048 " bitwise equality, not IEEE 754 equality.\n"),
10049 JS_FN_HELP("wasmGlobalIsNaN", WasmGlobalIsNaN
, 2, 0,
10050 "wasmGlobalIsNaN(global, flavor)",
10051 " Compares a floating point WebAssembly.Global object for if its value is a\n"
10052 " specific NaN flavor. Valid flavors are `arithmetic_nan` and `canonical_nan`.\n"),
10053 JS_FN_HELP("wasmGlobalToString", WasmGlobalToString
, 1, 0,
10054 "wasmGlobalToString(global)",
10055 " Returns a debug representation of the contents of a WebAssembly.Global\n"
10057 JS_FN_HELP("wasmLosslessInvoke", WasmLosslessInvoke
, 1, 0,
10058 "wasmLosslessInvoke(wasmFunc, args...)",
10059 " Invokes the provided WebAssembly function using a modified conversion\n"
10060 " function that allows providing a param as a WebAssembly.Global and\n"
10061 " returning a result as a WebAssembly.Global.\n"),
10063 JS_FN_HELP("wasmCompilersPresent", WasmCompilersPresent
, 0, 0,
10064 "wasmCompilersPresent()",
10065 " Returns a string indicating the present wasm compilers: a comma-separated list\n"
10066 " of 'baseline', 'ion'. A compiler is present in the executable if it is compiled\n"
10067 " in and can generate code for the current architecture."),
10069 JS_FN_HELP("wasmCompileMode", WasmCompileMode
, 0, 0,
10070 "wasmCompileMode()",
10071 " Returns a string indicating the available wasm compilers: 'baseline', 'ion',\n"
10072 " 'baseline+ion', or 'none'. A compiler is available if it is present in the\n"
10073 " executable and not disabled by switches or runtime conditions. At most one\n"
10074 " baseline and one optimizing compiler can be available."),
10076 JS_FN_HELP("wasmBaselineDisabledByFeatures", WasmBaselineDisabledByFeatures
, 0, 0,
10077 "wasmBaselineDisabledByFeatures()",
10078 " If some feature is enabled at compile-time or run-time that prevents baseline\n"
10079 " from being used then this returns a truthy string describing the features that\n."
10080 " are disabling it. Otherwise it returns false."),
10082 JS_FN_HELP("wasmIonDisabledByFeatures", WasmIonDisabledByFeatures
, 0, 0,
10083 "wasmIonDisabledByFeatures()",
10084 " If some feature is enabled at compile-time or run-time that prevents Ion\n"
10085 " from being used then this returns a truthy string describing the features that\n."
10086 " are disabling it. Otherwise it returns false."),
10088 JS_FN_HELP("wasmFunctionTier", WasmFunctionTier
, 1, 0,
10089 "wasmFunctionTier(wasmFunc)\n",
10090 " Returns the best compiled tier for a function. Either 'baseline' or 'optimized'."),
10092 JS_FN_HELP("wasmHasTier2CompilationCompleted", WasmHasTier2CompilationCompleted
, 1, 0,
10093 "wasmHasTier2CompilationCompleted(module)",
10094 " Returns a boolean indicating whether a given module has finished compiled code for tier2. \n"
10095 "This will return true early if compilation isn't two-tiered. "),
10097 JS_FN_HELP("wasmLoadedFromCache", WasmLoadedFromCache
, 1, 0,
10098 "wasmLoadedFromCache(module)",
10099 " Returns a boolean indicating whether a given module was deserialized directly from a\n"
10100 " cache (as opposed to compiled from bytecode)."),
10102 JS_FN_HELP("wasmBuiltinI8VecMul", WasmBuiltinI8VecMul
, 0, 0,
10103 "wasmBuiltinI8VecMul()",
10104 " Returns a module that implements an i8 vector pairwise multiplication intrinsic."),
10106 #ifdef ENABLE_WASM_GC
10107 JS_FN_HELP("wasmGcReadField", WasmGcReadField
, 2, 0,
10108 "wasmGcReadField(obj, index)",
10109 " Gets a field of a WebAssembly GC struct or array."),
10111 JS_FN_HELP("wasmGcArrayLength", WasmGcArrayLength
, 1, 0,
10112 "wasmGcArrayLength(arr)",
10113 " Gets the length of a WebAssembly GC array."),
10114 #endif // ENABLE_WASM_GC
10116 #ifdef ENABLE_WASM_BRANCH_HINTING
10117 JS_FN_HELP("wasmParsedBranchHints", WasmParsedBranchHints
, 1, 0,
10118 "wasmParsedBranchHints(module)",
10119 " Returns a boolean indicating whether a given module has successfully parsed a\n"
10120 " custom branch hinting section."),
10122 #endif // ENABLE_WASM_BRANCH_HINTING
10124 JS_FN_HELP("largeArrayBufferSupported", LargeArrayBufferSupported
, 0, 0,
10125 "largeArrayBufferSupported()",
10126 " Returns true if array buffers larger than 2GB can be allocated."),
10128 JS_FN_HELP("isLazyFunction", IsLazyFunction
, 1, 0,
10129 "isLazyFunction(fun)",
10130 " True if fun is a lazy JSFunction."),
10132 JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction
, 1, 0,
10133 "isRelazifiableFunction(fun)",
10134 " True if fun is a JSFunction with a relazifiable JSScript."),
10136 JS_FN_HELP("hasSameBytecodeData", HasSameBytecodeData
, 2, 0,
10137 "hasSameBytecodeData(fun1, fun2)",
10138 " True if fun1 and fun2 share the same copy of bytecode data. This will\n"
10139 " delazify the function if necessary."),
10141 JS_FN_HELP("enableShellAllocationMetadataBuilder", EnableShellAllocationMetadataBuilder
, 0, 0,
10142 "enableShellAllocationMetadataBuilder()",
10143 " Use ShellAllocationMetadataBuilder to supply metadata for all newly created objects."),
10145 JS_FN_HELP("getAllocationMetadata", GetAllocationMetadata
, 1, 0,
10146 "getAllocationMetadata(obj)",
10147 " Get the metadata for an object."),
10149 JS_INLINABLE_FN_HELP("bailout", testingFunc_bailout
, 0, 0, TestBailout
,
10151 " Force a bailout out of ionmonkey (if running in ionmonkey)."),
10153 JS_FN_HELP("bailAfter", testingFunc_bailAfter
, 1, 0,
10154 "bailAfter(number)",
10155 " Start a counter to bail once after passing the given amount of possible bailout positions in\n"
10158 JS_FN_HELP("invalidate", testingFunc_invalidate
, 0, 0,
10160 " Force an immediate invalidation (if running in Warp)."),
10162 JS_FN_HELP("inJit", testingFunc_inJit
, 0, 0,
10164 " Returns true when called within (jit-)compiled code. When jit compilation is disabled this\n"
10165 " function returns an error string. This function returns false in all other cases.\n"
10166 " Depending on truthiness, you should continue to wait for compilation to happen or stop execution.\n"),
10168 JS_FN_HELP("inIon", testingFunc_inIon
, 0, 0,
10170 " Returns true when called within ion. When ion is disabled or when compilation is abnormally\n"
10171 " slow to start, this function returns an error string. Otherwise, this function returns false.\n"
10172 " This behaviour ensures that a falsy value means that we are not in ion, but expect a\n"
10173 " compilation to occur in the future. Conversely, a truthy value means that we are either in\n"
10174 " ion or that there is litle or no chance of ion ever compiling the current script."),
10176 JS_FN_HELP("assertJitStackInvariants", TestingFunc_assertJitStackInvariants
, 0, 0,
10177 "assertJitStackInvariants()",
10178 " Iterates the Jit stack and check that stack invariants hold."),
10180 JS_FN_HELP("setIonCheckGraphCoherency", SetIonCheckGraphCoherency
, 1, 0,
10181 "setIonCheckGraphCoherency(bool)",
10182 " Set whether Ion should perform graph consistency (DEBUG-only) assertions. These assertions\n"
10183 " are valuable and should be generally enabled, however they can be very expensive for large\n"
10184 " (wasm) programs."),
10186 JS_FN_HELP("serialize", testingFunc_serialize
, 1, 0,
10187 "serialize(data, [transferables, [policy]])",
10188 " Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
10189 " clone buffer object. 'policy' may be an options hash. Valid keys:\n"
10190 " 'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
10191 " to specify whether SharedArrayBuffers may be serialized.\n"
10192 " 'scope' - SameProcess, DifferentProcess, or\n"
10193 " DifferentProcessForIndexedDB. Determines how some values will be\n"
10194 " serialized. Clone buffers may only be deserialized with a compatible\n"
10195 " scope. NOTE - For DifferentProcess/DifferentProcessForIndexedDB,\n"
10196 " must also set SharedArrayBuffer:'deny' if data contains any shared memory\n"
10199 JS_FN_HELP("deserialize", Deserialize
, 1, 0,
10200 "deserialize(clonebuffer[, opts])",
10201 " Deserialize data generated by serialize. 'opts' may be an options hash.\n"
10203 " 'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
10204 " to specify whether SharedArrayBuffers may be serialized.\n"
10205 " 'scope', which limits the clone buffers that are considered\n"
10206 " valid. Allowed values: ''SameProcess', 'DifferentProcess',\n"
10207 " and 'DifferentProcessForIndexedDB'. So for example, a\n"
10208 " DifferentProcessForIndexedDB clone buffer may be deserialized in any scope, but\n"
10209 " a SameProcess clone buffer cannot be deserialized in a\n"
10210 " DifferentProcess scope."),
10212 JS_FN_HELP("detachArrayBuffer", DetachArrayBuffer
, 1, 0,
10213 "detachArrayBuffer(buffer)",
10214 " Detach the given ArrayBuffer object from its memory, i.e. as if it\n"
10215 " had been transferred to a WebWorker."),
10217 JS_FN_HELP("makeSerializable", MakeSerializable
, 1, 0,
10218 "makeSerializable(numeric id, [behavior])",
10219 " Make a custom serializable, transferable object. It will have a single accessor\n"
10220 " obj.log that will give a history of all operations on all such objects in the\n"
10221 " current thread as an array [id, action, id, action, ...] where the id\n"
10222 " is the number passed into this function, and the action is one of:\n"
10223 " ? - the canTransfer() hook was called.\n"
10224 " w - the write() hook was called.\n"
10225 " W - the writeTransfer() hook was called.\n"
10226 " R - the readTransfer() hook was called.\n"
10227 " r - the read() hook was called.\n"
10228 " F - the freeTransfer() hook was called.\n"
10229 " The `behavior` parameter can be used to force a failure during processing:\n"
10230 " 1 - fail during readTransfer() hook\n"
10231 " 2 - fail during read() hook\n"
10232 " Set the log to null to clear it."),
10234 JS_FN_HELP("ensureNonInline", EnsureNonInline
, 1, 0,
10235 "ensureNonInline(view or buffer)",
10236 " Ensure that the memory for the given ArrayBuffer or ArrayBufferView\n"
10237 " is not inline."),
10239 JS_FN_HELP("pinArrayBufferOrViewLength", PinArrayBufferOrViewLength
, 1, 0,
10240 "pinArrayBufferOrViewLength(view or buffer[, pin])",
10241 " Prevent or allow (if `pin` is false) changes to the length of the given\n"
10242 " ArrayBuffer or ArrayBufferView. `pin` defaults to true."),
10244 JS_FN_HELP("JSONStringify", JSONStringify
, 4, 0,
10245 "JSONStringify(value, behavior)",
10246 " Same as JSON.stringify(value), but allows setting behavior:\n"
10247 " Normal: the default\n"
10248 " FastOnly: throw if the fast path bails\n"
10249 " SlowOnly: skip the fast path entirely\n"
10250 " Compare: run both the fast and slow paths and compare the result. Crash if\n"
10251 " they do not match. If the fast path bails, no comparison is done."),
10253 JS_FN_HELP("helperThreadCount", HelperThreadCount
, 0, 0,
10254 "helperThreadCount()",
10255 " Returns the number of helper threads available for off-thread tasks."),
10257 JS_FN_HELP("createShapeSnapshot", CreateShapeSnapshot
, 1, 0,
10258 "createShapeSnapshot(obj)",
10259 " Returns an object containing a shape snapshot for use with\n"
10260 " checkShapeSnapshot.\n"),
10262 JS_FN_HELP("checkShapeSnapshot", CheckShapeSnapshot
, 2, 0,
10263 "checkShapeSnapshot(snapshot, [obj])",
10264 " Check shape invariants based on the given snapshot and optional object.\n"
10265 " If there's no object argument, the snapshot's object is used.\n"),
10267 JS_FN_HELP("enableShapeConsistencyChecks", EnableShapeConsistencyChecks
, 0, 0,
10268 "enableShapeConsistencyChecks()",
10269 " Enable some slow Shape assertions.\n"),
10271 JS_FN_HELP("reportOutOfMemory", ReportOutOfMemory
, 0, 0,
10272 "reportOutOfMemory()",
10273 " Report OOM, then clear the exception and return undefined. For crash testing."),
10275 JS_FN_HELP("throwOutOfMemory", ThrowOutOfMemory
, 0, 0,
10276 "throwOutOfMemory()",
10277 " Throw out of memory exception, for OOM handling testing."),
10279 JS_FN_HELP("reportLargeAllocationFailure", ReportLargeAllocationFailure
, 0, 0,
10280 "reportLargeAllocationFailure([bytes])",
10281 " Call the large allocation failure callback, as though a large malloc call failed,\n"
10282 " then return undefined. In Gecko, this sends a memory pressure notification, which\n"
10283 " can free up some memory."),
10285 JS_FN_HELP("findPath", FindPath
, 2, 0,
10286 "findPath(start, target)",
10287 " Return an array describing one of the shortest paths of GC heap edges from\n"
10288 " |start| to |target|, or |undefined| if |target| is unreachable from |start|.\n"
10289 " Each element of the array is either of the form:\n"
10290 " { node: <object or string>, edge: <string describing edge from node> }\n"
10291 " if the node is a JavaScript object or value; or of the form:\n"
10292 " { type: <string describing node>, edge: <string describing edge> }\n"
10293 " if the node is some internal thing that is not a proper JavaScript value\n"
10294 " (like a shape or a scope chain element). The destination of the i'th array\n"
10295 " element's edge is the node of the i+1'th array element; the destination of\n"
10296 " the last array element is implicitly |target|.\n"),
10298 JS_FN_HELP("wasmMetadataAnalysis", wasmMetadataAnalysis
, 1, 0,
10299 "wasmMetadataAnalysis(wasmObject)",
10300 " Prints an analysis of the size of metadata on this wasm object.\n"),
10302 JS_FN_HELP("sharedMemoryEnabled", SharedMemoryEnabled
, 0, 0,
10303 "sharedMemoryEnabled()",
10304 " Return true if SharedArrayBuffer and Atomics are enabled"),
10306 JS_FN_HELP("sharedArrayRawBufferRefcount", SharedArrayRawBufferRefcount
, 0, 0,
10307 "sharedArrayRawBufferRefcount(sab)",
10308 " Return the reference count of the SharedArrayRawBuffer object held by sab"),
10310 #ifdef NIGHTLY_BUILD
10311 JS_FN_HELP("objectAddress", ObjectAddress
, 1, 0,
10312 "objectAddress(obj)",
10313 " Return the current address of the object. For debugging only--this\n"
10314 " address may change during a moving GC."),
10316 JS_FN_HELP("sharedAddress", SharedAddress
, 1, 0,
10317 "sharedAddress(obj)",
10318 " Return the address of the shared storage of a SharedArrayBuffer."),
10321 JS_FN_HELP("hasInvalidatedTeleporting", HasInvalidatedTeleporting
, 1, 0,
10322 "hasInvalidatedTeleporting(obj)",
10323 " Return true if the shape teleporting optimization has been disabled for |obj|."),
10325 JS_FN_HELP("evalReturningScope", EvalReturningScope
, 1, 0,
10326 "evalReturningScope(scriptStr, [global])",
10327 " Evaluate the script in a new scope and return the scope.\n"
10328 " If |global| is present, clone the script to |global| before executing."),
10330 JS_FN_HELP("backtrace", DumpBacktrace
, 1, 0,
10332 " Dump out a brief backtrace."),
10334 JS_FN_HELP("getBacktrace", GetBacktrace
, 1, 0,
10335 "getBacktrace([options])",
10336 " Return the current stack as a string. Takes an optional options object,\n"
10337 " which may contain any or all of the boolean properties:\n"
10338 " options.args - show arguments to each function\n"
10339 " options.locals - show local variables in each frame\n"
10340 " options.thisprops - show the properties of the 'this' object of each frame\n"),
10342 JS_FN_HELP("byteSize", ByteSize
, 1, 0,
10344 " Return the size in bytes occupied by |value|, or |undefined| if value\n"
10345 " is not allocated in memory.\n"),
10347 JS_FN_HELP("byteSizeOfScript", ByteSizeOfScript
, 1, 0,
10348 "byteSizeOfScript(f)",
10349 " Return the size in bytes occupied by the function |f|'s JSScript.\n"),
10351 JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype
, 1, 0,
10352 "setImmutablePrototype(obj)",
10353 " Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n"
10354 " change it will fail. Return true if obj's [[Prototype]] was successfully made\n"
10355 " immutable (or if it already was immutable), false otherwise. Throws in case\n"
10356 " of internal error, or if the operation doesn't even make sense (for example,\n"
10357 " because the object is a revoked proxy)."),
10359 JS_FN_HELP("allocationMarker", AllocationMarker
, 0, 0,
10360 "allocationMarker([options])",
10361 " Return a freshly allocated object whose [[Class]] name is\n"
10362 " \"AllocationMarker\". Such objects are allocated only by calls\n"
10363 " to this function, never implicitly by the system, making them\n"
10364 " suitable for use in allocation tooling tests. Takes an optional\n"
10365 " options object which may contain the following properties:\n"
10366 " * nursery: bool, whether to allocate the object in the nursery\n"),
10368 JS_FN_HELP("setGCCallback", SetGCCallback
, 1, 0,
10369 "setGCCallback({action:\"...\", options...})",
10370 " Set the GC callback. action may be:\n"
10371 " 'minorGC' - run a nursery collection\n"
10372 " 'majorGC' - run a major collection, nesting up to a given 'depth'\n"),
10375 JS_FN_HELP("enqueueMark", EnqueueMark
, 1, 0,
10376 "enqueueMark(obj|string)",
10377 " Add an object to the queue of objects to mark at the beginning every GC. (Note\n"
10378 " that the objects will actually be marked at the beginning of every slice, but\n"
10379 " after the first slice they will already be marked so nothing will happen.)\n"
10381 " Instead of an object, a few magic strings may be used:\n"
10382 " 'yield' - cause the current marking slice to end, as if the mark budget were\n"
10384 " 'enter-weak-marking-mode' - divide the list into two segments. The items after\n"
10385 " this string will not be marked until we enter weak marking mode. Note that weak\n"
10386 " marking mode may be entered zero or multiple times for one GC.\n"
10387 " 'abort-weak-marking-mode' - same as above, but then abort weak marking to fall back\n"
10388 " on the old iterative marking code path.\n"
10389 " 'drain' - fully drain the mark stack before continuing.\n"
10390 " 'set-color-black' - force everything following in the mark queue to be marked black.\n"
10391 " 'set-color-gray' - continue with the regular GC until gray marking is possible, then force\n"
10392 " everything following in the mark queue to be marked gray.\n"
10393 " 'unset-color' - stop forcing the mark color."),
10395 JS_FN_HELP("clearMarkQueue", ClearMarkQueue
, 0, 0,
10396 "clearMarkQueue()",
10397 " Cancel the special marking of all objects enqueue with enqueueMark()."),
10399 JS_FN_HELP("getMarkQueue", GetMarkQueue
, 0, 0,
10401 " Return the current mark queue set up via enqueueMark calls. Note that all\n"
10402 " returned values will be wrapped into the current compartment, so this loses\n"
10403 " some fidelity."),
10406 JS_FN_HELP("nurseryStringsEnabled", NurseryStringsEnabled
, 0, 0,
10407 "nurseryStringsEnabled()",
10408 " Return whether strings are currently allocated in the nursery for the current\n"
10411 JS_FN_HELP("isNurseryAllocated", IsNurseryAllocated
, 1, 0,
10412 "isNurseryAllocated(thing)",
10413 " Return whether a GC thing is nursery allocated.\n"),
10415 JS_FN_HELP("numAllocSitesPretenured", NumAllocSitesPretenured
, 0, 0,
10416 "numAllocSitesPretenured()",
10417 " Return the number of allocation sites that were pretenured for the current\n"
10420 JS_FN_HELP("getLcovInfo", GetLcovInfo
, 1, 0,
10421 "getLcovInfo(global)",
10422 " Generate LCOV tracefile for the given compartment. If no global are provided then\n"
10423 " the current global is used as the default one.\n"),
10426 JS_FN_HELP("setRNGState", SetRNGState
, 2, 0,
10427 "setRNGState(seed0, seed1)",
10428 " Set this compartment's RNG state.\n"),
10432 JS_FN_HELP("aflloop", AflLoop
, 1, 0,
10433 "aflloop(max_cnt)",
10434 " Call the __AFL_LOOP() runtime function (see AFL docs)\n"),
10437 JS_FN_HELP("monotonicNow", MonotonicNow
, 0, 0,
10439 " Return a timestamp reflecting the current elapsed system time.\n"
10440 " This is monotonically increasing.\n"),
10442 JS_FN_HELP("timeSinceCreation", TimeSinceCreation
, 0, 0,
10443 "TimeSinceCreation()",
10444 " Returns the time in milliseconds since process creation.\n"
10445 " This uses a clock compatible with the profiler.\n"),
10447 JS_FN_HELP("isConstructor", IsConstructor
, 1, 0,
10448 "isConstructor(value)",
10449 " Returns whether the value is considered IsConstructor.\n"),
10451 JS_FN_HELP("getTimeZone", GetTimeZone
, 0, 0,
10453 " Get the current time zone.\n"),
10455 JS_FN_HELP("getDefaultLocale", GetDefaultLocale
, 0, 0,
10456 "getDefaultLocale()",
10457 " Get the current default locale.\n"),
10459 JS_FN_HELP("getCoreCount", GetCoreCount
, 0, 0,
10461 " Get the number of CPU cores from the platform layer. Typically this\n"
10462 " means the number of hyperthreads on systems where that makes sense.\n"),
10464 JS_FN_HELP("setTimeResolution", SetTimeResolution
, 2, 0,
10465 "setTimeResolution(resolution, jitter)",
10466 " Enables time clamping and jittering. Specify a time resolution in\n"
10467 " microseconds and whether or not to jitter\n"),
10469 JS_FN_HELP("scriptedCallerGlobal", ScriptedCallerGlobal
, 0, 0,
10470 "scriptedCallerGlobal()",
10471 " Get the caller's global (or null). See JS::GetScriptedCallerGlobal.\n"),
10473 JS_FN_HELP("objectGlobal", ObjectGlobal
, 1, 0,
10474 "objectGlobal(obj)",
10475 " Returns the object's global object or null if the object is a wrapper.\n"),
10477 JS_FN_HELP("isSameCompartment", IsSameCompartment
, 2, 0,
10478 "isSameCompartment(obj1, obj2)",
10479 " Unwraps obj1 and obj2 and returns whether the unwrapped objects are\n"
10480 " same-compartment.\n"),
10482 JS_FN_HELP("firstGlobalInCompartment", FirstGlobalInCompartment
, 1, 0,
10483 "firstGlobalInCompartment(obj)",
10484 " Returns the first global in obj's compartment.\n"),
10486 JS_FN_HELP("assertCorrectRealm", AssertCorrectRealm
, 0, 0,
10487 "assertCorrectRealm()",
10488 " Asserts cx->realm matches callee->realm.\n"),
10490 JS_FN_HELP("globalLexicals", GlobalLexicals
, 0, 0,
10491 "globalLexicals()",
10492 " Returns an object containing a copy of all global lexical bindings.\n"
10493 " Example use: let x = 1; assertEq(globalLexicals().x, 1);\n"),
10495 JS_FN_HELP("baselineCompile", BaselineCompile
, 2, 0,
10496 "baselineCompile([fun/code], forceDebugInstrumentation=false)",
10497 " Baseline-compiles the given JS function or script.\n"
10498 " Without arguments, baseline-compiles the caller's script; but note\n"
10499 " that extra boilerplate is needed afterwards to cause the VM to start\n"
10500 " running the jitcode rather than staying in the interpreter:\n"
10501 " baselineCompile(); for (var i=0; i<1; i++) {} ...\n"
10502 " The interpreter will enter the new jitcode at the loop header unless\n"
10503 " baselineCompile returned a string or threw an error.\n"),
10505 JS_FN_HELP("encodeAsUtf8InBuffer", EncodeAsUtf8InBuffer
, 2, 0,
10506 "encodeAsUtf8InBuffer(str, uint8Array)",
10507 " Encode as many whole code points from the string str into the provided\n"
10508 " Uint8Array as will completely fit in it, converting lone surrogates to\n"
10509 " REPLACEMENT CHARACTER. Return an array [r, w] where |r| is the\n"
10510 " number of 16-bit units read and |w| is the number of bytes of UTF-8\n"
10513 JS_FN_HELP("clearKeptObjects", ClearKeptObjects
, 0, 0,
10514 "clearKeptObjects()",
10515 "Perform the ECMAScript ClearKeptObjects operation, clearing the list of\n"
10516 "observed WeakRef targets that are kept alive until the next synchronous\n"
10517 "sequence of ECMAScript execution completes. This is used for testing\n"
10520 JS_FN_HELP("numberToDouble", NumberToDouble
, 1, 0,
10521 "numberToDouble(number)",
10522 " Return the input number as double-typed number."),
10524 JS_FN_HELP("getICUOptions", GetICUOptions
, 0, 0,
10526 " Return an object describing the following ICU options.\n\n"
10527 " version: a string containing the ICU version number, e.g. '67.1'\n"
10528 " unicode: a string containing the Unicode version number, e.g. '13.0'\n"
10529 " locale: the ICU default locale, e.g. 'en_US'\n"
10530 " tzdata: a string containing the tzdata version number, e.g. '2020a'\n"
10531 " timezone: the ICU default time zone, e.g. 'America/Los_Angeles'\n"
10532 " host-timezone: the host time zone, e.g. 'America/Los_Angeles'"),
10534 JS_FN_HELP("getAvailableLocalesOf", GetAvailableLocalesOf
, 0, 0,
10535 "getAvailableLocalesOf(name)",
10536 " Return an array of all available locales for the given Intl constuctor."),
10538 JS_FN_HELP("isSmallFunction", IsSmallFunction
, 1, 0,
10539 "isSmallFunction(fun)",
10540 " Returns true if a scripted function is small enough to be inlinable."),
10542 JS_FN_HELP("compileToStencil", CompileToStencil
, 1, 0,
10543 "compileToStencil(string, [options])",
10544 " Parses the given string argument as js script, returns the stencil"
10547 JS_FN_HELP("evalStencil", EvalStencil
, 1, 0,
10548 "evalStencil(stencil, [options])",
10549 " Instantiates the given stencil, and evaluates the top-level script it"
10552 JS_FN_HELP("compileToStencilXDR", CompileToStencilXDR
, 1, 0,
10553 "compileToStencilXDR(string, [options])",
10554 " Parses the given string argument as js script, produces the stencil"
10555 " for it, XDR-encodes the stencil, and returns an object that contains the"
10558 JS_FN_HELP("evalStencilXDR", EvalStencilXDR
, 1, 0,
10559 "evalStencilXDR(stencilXDR, [options])",
10560 " Reads the given stencil XDR object, and evaluates the top-level script it"
10563 JS_FN_HELP("getExceptionInfo", GetExceptionInfo
, 1, 0,
10564 "getExceptionInfo(fun)",
10565 " Calls the given function and returns information about the exception it"
10566 " throws. Returns null if the function didn't throw an exception."),
10568 JS_FN_HELP("nukeCCW", NukeCCW
, 1, 0,
10569 "nukeCCW(wrapper)",
10570 " Nuke a CrossCompartmentWrapper, which turns it into a DeadProxyObject."),
10572 JS_FN_HELP("assertRealmFuseInvariants", AssertRealmFuseInvariants
, 0, 0,
10573 "assertRealmFuseInvariants()",
10574 " Runs the realm's fuse invariant checks -- these will crash on failure. "
10575 " Only available in fuzzing or debug builds, so usage should be guarded. "),
10577 JS_FN_HELP("isCCW", IsCCW
, 1, 0,
10579 " Return true if an object is a CCW."),
10581 JS_FN_HELP("popAllFusesInRealm", PopAllFusesInRealm
, 0, 0,
10582 "popAllFusesInRealm()",
10583 " Pops all the fuses in the current realm"),
10585 JS_FN_HELP("getAllPrefNames", GetAllPrefNames
, 0, 0,
10586 "getAllPrefNames()",
10587 " Returns an array containing the names of all JS prefs."),
10589 JS_FN_HELP("getPrefValue", GetPrefValue
, 1, 0,
10590 "getPrefValue(name)",
10591 " Return the value of the JS pref with the given name."),
10593 JS_FN_HELP("hadOutOfMemory", HadOutOfMemory
, 0, 0,
10594 "hadOutOfMemory()",
10595 " Return the runtime's internal hadOutOfMemory flag that is set when\n"
10596 " out of memory is hit with an exception being propagated. "),
10602 // clang-format off
10603 static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions
[] = {
10604 JS_FN_HELP("getErrorNotes", GetErrorNotes
, 1, 0,
10605 "getErrorNotes(error)",
10606 " Returns an array of error notes."),
10608 JS_FN_HELP("setTimeZone", SetTimeZone
, 1, 0,
10609 "setTimeZone(tzname)",
10610 " Set the 'TZ' environment variable to the given time zone and applies the new time zone.\n"
10611 " The time zone given is validated according to the current environment.\n"
10612 " An empty string or undefined resets the time zone to its default value."),
10614 JS_FN_HELP("setDefaultLocale", SetDefaultLocale
, 1, 0,
10615 "setDefaultLocale(locale)",
10616 " Set the runtime default locale to the given value.\n"
10617 " An empty string or undefined resets the runtime locale to its default value.\n"
10618 " NOTE: The input string is not fully validated, it must be a valid BCP-47 language tag."),
10620 JS_FN_HELP("isInStencilCache", IsInStencilCache
, 1, 0,
10621 "isInStencilCache(fun)",
10622 " True if fun is available in the stencil cache."),
10624 JS_FN_HELP("waitForStencilCache", WaitForStencilCache
, 1, 0,
10625 "waitForStencilCache(fun)",
10626 " Block main thread execution until the function is made available in the cache."),
10628 JS_FN_HELP("getInnerMostEnvironmentObject", GetInnerMostEnvironmentObject
, 0, 0,
10629 "getInnerMostEnvironmentObject()",
10630 " Return the inner-most environment object for current execution."),
10632 JS_FN_HELP("getEnclosingEnvironmentObject", GetEnclosingEnvironmentObject
, 1, 0,
10633 "getEnclosingEnvironmentObject(env)",
10634 " Return the enclosing environment object for given environment object."),
10636 JS_FN_HELP("getEnvironmentObjectType", GetEnvironmentObjectType
, 1, 0,
10637 "getEnvironmentObjectType(env)",
10638 " Return a string represents the type of given environment object."),
10640 JS_FN_HELP("shortestPaths", ShortestPaths
, 3, 0,
10641 "shortestPaths(targets, options)",
10642 " Return an array of arrays of shortest retaining paths. There is an array of\n"
10643 " shortest retaining paths for each object in |targets|. Each element in a path\n"
10644 " is of the form |{ predecessor, edge }|. |options| may contain:\n"
10646 " maxNumPaths: The maximum number of paths returned in each of those arrays\n"
10648 " start: The object to start all paths from. If not given, then\n"
10649 " the starting point will be the set of GC roots."),
10651 JS_FN_HELP("getFuseState", GetFuseState
, 0, 0,
10653 " Return an object describing the calling realm's fuse state, "
10654 "as well as the state of any runtime fuses."),
10656 #if defined(DEBUG) || defined(JS_JITSPEW)
10657 JS_FN_HELP("dumpObject", DumpObject
, 1, 0,
10659 " Dump an internal representation of an object."),
10661 JS_FN_HELP("dumpValue", DumpValue
, 1, 0,
10663 " Dump an internal representation of a value."),
10665 JS_FN_HELP("dumpValueToString", DumpValueToString
, 1, 0,
10667 " Return a dump of an internal representation of a value."),
10669 JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation
, 1, 0,
10670 "dumpStringRepresentation(str)",
10671 " Print a human-readable description of how the string |str| is represented.\n"),
10673 JS_FN_HELP("stringRepresentation", GetStringRepresentation
, 1, 0,
10674 "stringRepresentation(str)",
10675 " Return a human-readable description of how the string |str| is represented.\n"),
10678 JS_FN_HELP("wasmExtractCode", WasmExtractCode
, 1, 0,
10679 "wasmExtractCode(module[, tier])",
10680 " Extracts generated machine code from WebAssembly.Module. The tier is a string,\n"
10681 " 'stable', 'best', 'baseline', or 'ion'; the default is 'stable'. If the request\n"
10682 " cannot be satisfied then null is returned. If the request is 'ion' then block\n"
10683 " until background compilation is complete."),
10685 JS_FN_HELP("wasmDis", WasmDisassemble
, 1, 0,
10686 "wasmDis(wasmObject[, options])\n",
10687 " Disassembles generated machine code from an exported WebAssembly function,\n"
10688 " or from all the functions defined in the module or instance, exported and not.\n"
10689 " The `options` is an object with the following optional keys:\n"
10690 " asString: boolean - if true, return a string rather than printing on stderr,\n"
10691 " the default is false.\n"
10692 " tier: string - one of 'stable', 'best', 'baseline', or 'ion'; the default is\n"
10694 " kinds: string - if set, and the wasmObject is a module or instance, a\n"
10695 " comma-separated list of the following keys, the default is `Function`:\n"
10696 " Function - functions defined in the module\n"
10697 " InterpEntry - C++-to-wasm stubs\n"
10698 " JitEntry - jitted-js-to-wasm stubs\n"
10699 " ImportInterpExit - wasm-to-C++ stubs\n"
10700 " ImportJitExit - wasm-to-jitted-JS stubs\n"
10701 " all - all kinds, including obscure ones\n"),
10703 JS_FN_HELP("wasmDumpIon", WasmDumpIon
, 2, 0,
10704 "wasmDumpIon(bytecode, funcIndex, [, contents])\n",
10705 "wasmDumpIon(bytecode, funcIndex, [, contents])"
10706 " Returns a dump of compiling a function in the specified module with Ion."
10707 " The `contents` flag controls what is dumped. one of:"
10708 " `mir` | `unopt-mir`: Unoptimized MIR (the default)"
10709 " `opt-mir`: Optimized MIR"
10716 // clang-format off
10717 static const JSFunctionSpecWithHelp PCCountProfilingTestingFunctions
[] = {
10718 JS_FN_HELP("start", PCCountProfiling_Start
, 0, 0,
10720 " Start PC count profiling."),
10722 JS_FN_HELP("stop", PCCountProfiling_Stop
, 0, 0,
10724 " Stop PC count profiling."),
10726 JS_FN_HELP("purge", PCCountProfiling_Purge
, 0, 0,
10728 " Purge the collected PC count profiling data."),
10730 JS_FN_HELP("count", PCCountProfiling_ScriptCount
, 0, 0,
10732 " Return the number of profiled scripts."),
10734 JS_FN_HELP("summary", PCCountProfiling_ScriptSummary
, 1, 0,
10736 " Return the PC count profiling summary for the given script index.\n"
10737 " The script index must be in the range [0, pc.count())."),
10739 JS_FN_HELP("contents", PCCountProfiling_ScriptContents
, 1, 0,
10741 " Return the complete profiling contents for the given script index.\n"
10742 " The script index must be in the range [0, pc.count())."),
10748 // clang-format off
10749 static const JSFunctionSpecWithHelp FdLibMTestingFunctions
[] = {
10750 JS_FN_HELP("pow", FdLibM_Pow
, 2, 0,
10752 " Return x ** y."),
10758 bool js::InitTestingFunctions() { return disasmBuf
.init(); }
10760 bool js::DefineTestingFunctions(JSContext
* cx
, HandleObject obj
,
10761 bool fuzzingSafe_
, bool disableOOMFunctions_
) {
10762 fuzzingSafe
= fuzzingSafe_
;
10763 if (EnvVarIsDefined("MOZ_FUZZING_SAFE")) {
10764 fuzzingSafe
= true;
10767 disableOOMFunctions
= disableOOMFunctions_
;
10769 if (!fuzzingSafe
) {
10770 if (!JS_DefineFunctionsWithHelp(cx
, obj
, FuzzingUnsafeTestingFunctions
)) {
10774 RootedObject
pccount(cx
, JS_NewPlainObject(cx
));
10779 if (!JS_DefineProperty(cx
, obj
, "pccount", pccount
, 0)) {
10783 if (!JS_DefineFunctionsWithHelp(cx
, pccount
,
10784 PCCountProfilingTestingFunctions
)) {
10789 RootedObject
fdlibm(cx
, JS_NewPlainObject(cx
));
10794 if (!JS_DefineProperty(cx
, obj
, "fdlibm", fdlibm
, 0)) {
10798 if (!JS_DefineFunctionsWithHelp(cx
, fdlibm
, FdLibMTestingFunctions
)) {
10802 return JS_DefineFunctionsWithHelp(cx
, obj
, TestingFunctions
);
10805 #ifdef FUZZING_JS_FUZZILLI
10806 uint32_t js::FuzzilliHashDouble(double value
) {
10807 // We shouldn't GC here as this is called directly from IC code.
10808 AutoUnsafeCallWithABI unsafe
;
10809 uint64_t v
= mozilla::BitwiseCast
<uint64_t>(value
);
10810 return static_cast<uint32_t>(v
) + static_cast<uint32_t>(v
>> 32);
10813 uint32_t js::FuzzilliHashBigInt(BigInt
* bigInt
) {
10814 // We shouldn't GC here as this is called directly from IC code.
10815 AutoUnsafeCallWithABI unsafe
;
10816 return bigInt
->hash();
10819 void js::FuzzilliHashObject(JSContext
* cx
, JSObject
* obj
) {
10820 // called from IC and baseline/interpreter
10822 FuzzilliHashObjectInl(cx
, obj
, &hash
);
10824 cx
->executionHashInputs
+= 1;
10825 cx
->executionHash
= mozilla::RotateLeft(cx
->executionHash
+ hash
, 1);
10828 void js::FuzzilliHashObjectInl(JSContext
* cx
, JSObject
* obj
, uint32_t* out
) {
10830 if (!js::SupportDifferentialTesting()) {
10837 JSAutoStructuredCloneBuffer
JSCloner(
10838 JS::StructuredCloneScope::DifferentProcess
, nullptr, nullptr);
10839 if (JSCloner
.write(cx
, v
)) {
10840 JSStructuredCloneData
& data
= JSCloner
.data();
10841 data
.ForEachDataChunk([&](const char* aData
, size_t aSize
) {
10842 uint32_t h
= mozilla::HashBytes(aData
, aSize
);
10848 } else if (JS_IsExceptionPending(cx
)) {
10849 JS_ClearPendingException(cx
);