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/ScopeExit.h"
20 #include "mozilla/Span.h"
21 #include "mozilla/Sprintf.h"
22 #include "mozilla/TextUtils.h"
23 #include "mozilla/ThreadLocal.h"
32 #include <initializer_list>
36 #if defined(XP_UNIX) && !defined(XP_DARWIN)
44 #include "jsfriendapi.h"
46 #ifdef JS_HAS_INTL_API
47 # include "builtin/intl/CommonFunctions.h"
48 # include "builtin/intl/FormatBuffer.h"
49 # include "builtin/intl/SharedIntlData.h"
51 #include "builtin/BigInt.h"
52 #include "builtin/JSON.h"
53 #include "builtin/MapObject.h"
54 #include "builtin/Promise.h"
55 #include "builtin/TestingUtility.h" // js::ParseCompileOptions, js::ParseDebugMetadata
56 #include "ds/IdValuePair.h" // js::IdValuePair
57 #include "frontend/BytecodeCompiler.h" // frontend::{CompileGlobalScriptToExtensibleStencil,ParseModuleToExtensibleStencil}
58 #include "frontend/CompilationStencil.h" // frontend::CompilationStencil
59 #include "frontend/FrontendContext.h" // AutoReportFrontendContext
61 #include "gc/GCEnum.h"
62 #include "gc/GCLock.h"
64 #include "jit/BaselineJIT.h"
65 #include "jit/Disassemble.h"
66 #include "jit/InlinableNatives.h"
67 #include "jit/Invalidation.h"
69 #include "jit/JitOptions.h"
70 #include "jit/JitRuntime.h"
71 #include "jit/TrialInlining.h"
72 #include "js/Array.h" // JS::NewArrayObject
73 #include "js/ArrayBuffer.h" // JS::{DetachArrayBuffer,GetArrayBufferLengthAndData,NewArrayBufferWithContents}
74 #include "js/CallAndConstruct.h" // JS::Call, JS::IsCallable, JS::IsConstructor, JS_CallFunction
75 #include "js/CharacterEncoding.h"
76 #include "js/CompilationAndEvaluation.h"
77 #include "js/CompileOptions.h"
78 #include "js/Conversions.h"
80 #include "js/experimental/CodeCoverage.h" // js::GetCodeCoverageSummary
81 #include "js/experimental/CompileScript.h" // JS::ParseGlobalScript, JS::PrepareForInstantiate
82 #include "js/experimental/JSStencil.h" // JS::Stencil
83 #include "js/experimental/PCCountProfiling.h" // JS::{Start,Stop}PCCountProfiling, JS::PurgePCCounts, JS::GetPCCountScript{Count,Summary,Contents}
84 #include "js/experimental/TypedData.h" // JS_GetObjectAsUint8Array
85 #include "js/friend/DumpFunctions.h" // js::Dump{Backtrace,Heap,Object}, JS::FormatStackDump, js::IgnoreNurseryObjects
86 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
87 #include "js/friend/WindowProxy.h" // js::ToWindowProxyIfWindow
88 #include "js/GlobalObject.h"
89 #include "js/HashTable.h"
90 #include "js/Interrupt.h"
91 #include "js/LocaleSensitive.h"
92 #include "js/Printf.h"
93 #include "js/PropertyAndElement.h" // JS_DefineProperties, JS_DefineProperty, JS_DefinePropertyById, JS_Enumerate, JS_GetProperty, JS_GetPropertyById, JS_HasProperty, JS_SetElement, JS_SetProperty
94 #include "js/PropertySpec.h"
95 #include "js/SourceText.h"
96 #include "js/StableStringChars.h"
98 #include "js/String.h" // JS::GetLinearStringLength, JS::StringToLinearString
99 #include "js/StructuredClone.h"
100 #include "js/UbiNode.h"
101 #include "js/UbiNodeBreadthFirst.h"
102 #include "js/UbiNodeShortestPaths.h"
103 #include "js/UniquePtr.h"
104 #include "js/Vector.h"
105 #include "js/Wrapper.h"
106 #include "threading/CpuCount.h"
107 #include "util/DifferentialTesting.h"
108 #include "util/StringBuffer.h"
109 #include "util/Text.h"
110 #include "vm/BooleanObject.h"
111 #include "vm/DateObject.h"
112 #include "vm/DateTime.h"
113 #include "vm/ErrorObject.h"
114 #include "vm/GlobalObject.h"
115 #include "vm/HelperThreads.h"
116 #include "vm/HelperThreadState.h"
117 #include "vm/Interpreter.h"
118 #include "vm/JSContext.h"
119 #include "vm/JSObject.h"
120 #include "vm/NumberObject.h"
121 #include "vm/PlainObject.h" // js::PlainObject
122 #include "vm/PromiseObject.h" // js::PromiseObject, js::PromiseSlot_*
123 #include "vm/ProxyObject.h"
124 #include "vm/RealmFuses.h"
125 #include "vm/SavedStacks.h"
126 #include "vm/ScopeKind.h"
127 #include "vm/Stack.h"
128 #include "vm/StencilCache.h" // DelazificationCache
129 #include "vm/StencilObject.h" // StencilObject, StencilXDRBufferObject
130 #include "vm/StringObject.h"
131 #include "vm/StringType.h"
132 #include "wasm/AsmJS.h"
133 #include "wasm/WasmBaselineCompile.h"
134 #include "wasm/WasmBuiltinModule.h"
135 #include "wasm/WasmFeatures.h"
136 #include "wasm/WasmGcObject.h"
137 #include "wasm/WasmInstance.h"
138 #include "wasm/WasmIonCompile.h"
139 #include "wasm/WasmJS.h"
140 #include "wasm/WasmModule.h"
141 #include "wasm/WasmValType.h"
142 #include "wasm/WasmValue.h"
144 #include "debugger/DebugAPI-inl.h"
145 #include "vm/Compartment-inl.h"
146 #include "vm/EnvironmentObject-inl.h"
147 #include "vm/JSContext-inl.h"
148 #include "vm/JSObject-inl.h"
149 #include "vm/NativeObject-inl.h"
150 #include "vm/ObjectFlags-inl.h"
151 #include "vm/StringType-inl.h"
152 #include "wasm/WasmInstance-inl.h"
156 using mozilla::AssertedCast
;
157 using mozilla::AsWritableChars
;
158 using mozilla::Maybe
;
161 using JS::AutoStableStringChars
;
162 using JS::CompileOptions
;
163 using JS::SourceOwnership
;
164 using JS::SourceText
;
166 // If fuzzingSafe is set, remove functionality that could cause problems with
167 // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
168 mozilla::Atomic
<bool> js::fuzzingSafe(false);
170 // If disableOOMFunctions is set, disable functionality that causes artificial
172 static mozilla::Atomic
<bool> disableOOMFunctions(false);
174 static bool EnvVarIsDefined(const char* name
) {
175 const char* value
= getenv(name
);
176 return value
&& *value
;
179 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
180 static bool EnvVarAsInt(const char* name
, int* valueOut
) {
181 if (!EnvVarIsDefined(name
)) {
185 *valueOut
= atoi(getenv(name
));
190 static bool GetRealmConfiguration(JSContext
* cx
, unsigned argc
, Value
* vp
) {
191 CallArgs args
= CallArgsFromVp(argc
, vp
);
192 RootedObject
callee(cx
, &args
.callee());
193 RootedObject
info(cx
, JS_NewPlainObject(cx
));
197 if (args
.length() > 1) {
198 ReportUsageErrorASCII(cx
, callee
, "Must have zero or one arguments");
201 if (args
.length() == 1 && !args
[0].isString()) {
202 ReportUsageErrorASCII(cx
, callee
, "Argument must be a string");
206 bool importAttributes
= cx
->options().importAttributes();
207 if (!JS_SetProperty(cx
, info
, "importAttributes",
208 importAttributes
? TrueHandleValue
: FalseHandleValue
)) {
213 bool arrayGrouping
= cx
->realm()->creationOptions().getArrayGroupingEnabled();
214 if (!JS_SetProperty(cx
, info
, "enableArrayGrouping",
215 arrayGrouping
? TrueHandleValue
: FalseHandleValue
)) {
221 bool newSetMethods
= cx
->realm()->creationOptions().getNewSetMethodsEnabled();
222 if (!JS_SetProperty(cx
, info
, "enableNewSetMethods",
223 newSetMethods
? TrueHandleValue
: FalseHandleValue
)) {
228 if (args
.length() == 1) {
229 RootedString
str(cx
, ToString(cx
, args
[0]));
234 if (!JS_StringToId(cx
, str
, &id
)) {
239 if (JS_HasPropertyById(cx
, info
, id
, &hasProperty
) && hasProperty
) {
240 // Returning a true/false from GetProperty
241 return GetProperty(cx
, info
, info
, id
, args
.rval());
244 ReportUsageErrorASCII(cx
, callee
, "Invalid option name");
248 args
.rval().setObject(*info
);
252 static bool GetBuildConfiguration(JSContext
* cx
, unsigned argc
, Value
* vp
) {
253 CallArgs args
= CallArgsFromVp(argc
, vp
);
254 RootedObject
callee(cx
, &args
.callee());
255 RootedObject
info(cx
, JS_NewPlainObject(cx
));
259 if (args
.length() > 1) {
260 ReportUsageErrorASCII(cx
, callee
, "Must have zero or one arguments");
263 if (args
.length() == 1 && !args
[0].isString()) {
264 ReportUsageErrorASCII(cx
, callee
, "Argument must be a string");
268 if (!JS_SetProperty(cx
, info
, "rooting-analysis", FalseHandleValue
)) {
272 if (!JS_SetProperty(cx
, info
, "exact-rooting", TrueHandleValue
)) {
276 if (!JS_SetProperty(cx
, info
, "trace-jscalls-api", FalseHandleValue
)) {
280 if (!JS_SetProperty(cx
, info
, "incremental-gc", TrueHandleValue
)) {
284 if (!JS_SetProperty(cx
, info
, "generational-gc", TrueHandleValue
)) {
288 if (!JS_SetProperty(cx
, info
, "oom-backtraces", FalseHandleValue
)) {
292 RootedValue
value(cx
);
294 value
= BooleanValue(true);
296 value
= BooleanValue(false);
298 if (!JS_SetProperty(cx
, info
, "debug", value
)) {
302 #ifdef RELEASE_OR_BETA
303 value
= BooleanValue(true);
305 value
= BooleanValue(false);
307 if (!JS_SetProperty(cx
, info
, "release_or_beta", value
)) {
311 #ifdef EARLY_BETA_OR_EARLIER
312 value
= BooleanValue(true);
314 value
= BooleanValue(false);
316 if (!JS_SetProperty(cx
, info
, "early_beta_or_earlier", value
)) {
320 #ifdef MOZ_CODE_COVERAGE
321 value
= BooleanValue(true);
323 value
= BooleanValue(false);
325 if (!JS_SetProperty(cx
, info
, "coverage", value
)) {
330 value
= BooleanValue(true);
332 value
= BooleanValue(false);
334 if (!JS_SetProperty(cx
, info
, "has-ctypes", value
)) {
338 #if defined(_M_IX86) || defined(__i386__)
339 value
= BooleanValue(true);
341 value
= BooleanValue(false);
343 if (!JS_SetProperty(cx
, info
, "x86", value
)) {
347 #if defined(_M_X64) || defined(__x86_64__)
348 value
= BooleanValue(true);
350 value
= BooleanValue(false);
352 if (!JS_SetProperty(cx
, info
, "x64", value
)) {
356 #ifdef JS_CODEGEN_ARM
357 value
= BooleanValue(true);
359 value
= BooleanValue(false);
361 if (!JS_SetProperty(cx
, info
, "arm", value
)) {
365 #ifdef JS_SIMULATOR_ARM
366 value
= BooleanValue(true);
368 value
= BooleanValue(false);
370 if (!JS_SetProperty(cx
, info
, "arm-simulator", value
)) {
375 value
= BooleanValue(true);
377 value
= BooleanValue(false);
379 if (!JS_SetProperty(cx
, info
, "android", value
)) {
384 value
= BooleanValue(true);
386 value
= BooleanValue(false);
388 if (!JS_SetProperty(cx
, info
, "windows", value
)) {
393 value
= BooleanValue(true);
395 value
= BooleanValue(false);
397 if (!JS_SetProperty(cx
, info
, "osx", value
)) {
401 #ifdef JS_CODEGEN_ARM64
402 value
= BooleanValue(true);
404 value
= BooleanValue(false);
406 if (!JS_SetProperty(cx
, info
, "arm64", value
)) {
410 #ifdef JS_SIMULATOR_ARM64
411 value
= BooleanValue(true);
413 value
= BooleanValue(false);
415 if (!JS_SetProperty(cx
, info
, "arm64-simulator", value
)) {
419 #ifdef JS_CODEGEN_MIPS32
420 value
= BooleanValue(true);
422 value
= BooleanValue(false);
424 if (!JS_SetProperty(cx
, info
, "mips32", value
)) {
428 #ifdef JS_CODEGEN_MIPS64
429 value
= BooleanValue(true);
431 value
= BooleanValue(false);
433 if (!JS_SetProperty(cx
, info
, "mips64", value
)) {
437 #ifdef JS_SIMULATOR_MIPS32
438 value
= BooleanValue(true);
440 value
= BooleanValue(false);
442 if (!JS_SetProperty(cx
, info
, "mips32-simulator", value
)) {
446 #ifdef JS_SIMULATOR_MIPS64
447 value
= BooleanValue(true);
449 value
= BooleanValue(false);
451 if (!JS_SetProperty(cx
, info
, "mips64-simulator", value
)) {
456 value
= BooleanValue(true);
458 value
= BooleanValue(false);
460 if (!JS_SetProperty(cx
, info
, "simulator", value
)) {
465 value
= BooleanValue(true);
467 value
= BooleanValue(false);
469 if (!JS_SetProperty(cx
, info
, "wasi", value
)) {
473 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
474 value
= BooleanValue(true);
476 value
= BooleanValue(false);
478 if (!JS_SetProperty(cx
, info
, "pbl", value
)) {
482 #ifdef JS_CODEGEN_LOONG64
483 value
= BooleanValue(true);
485 value
= BooleanValue(false);
487 if (!JS_SetProperty(cx
, info
, "loong64", value
)) {
491 #ifdef JS_SIMULATOR_LOONG64
492 value
= BooleanValue(true);
494 value
= BooleanValue(false);
496 if (!JS_SetProperty(cx
, info
, "loong64-simulator", value
)) {
500 #ifdef JS_CODEGEN_RISCV64
501 value
= BooleanValue(true);
503 value
= BooleanValue(false);
505 if (!JS_SetProperty(cx
, info
, "riscv64", value
)) {
509 #ifdef JS_SIMULATOR_RISCV64
510 value
= BooleanValue(true);
512 value
= BooleanValue(false);
514 if (!JS_SetProperty(cx
, info
, "riscv64-simulator", value
)) {
519 value
= BooleanValue(true);
521 value
= BooleanValue(false);
523 if (!JS_SetProperty(cx
, info
, "asan", value
)) {
528 value
= BooleanValue(true);
530 value
= BooleanValue(false);
532 if (!JS_SetProperty(cx
, info
, "tsan", value
)) {
537 value
= BooleanValue(true);
539 value
= BooleanValue(false);
541 if (!JS_SetProperty(cx
, info
, "ubsan", value
)) {
546 value
= BooleanValue(true);
548 value
= BooleanValue(false);
550 if (!JS_SetProperty(cx
, info
, "has-gczeal", value
)) {
555 value
= BooleanValue(true);
557 value
= BooleanValue(false);
559 if (!JS_SetProperty(cx
, info
, "profiling", value
)) {
563 #ifdef INCLUDE_MOZILLA_DTRACE
564 value
= BooleanValue(true);
566 value
= BooleanValue(false);
568 if (!JS_SetProperty(cx
, info
, "dtrace", value
)) {
573 value
= BooleanValue(true);
575 value
= BooleanValue(false);
577 if (!JS_SetProperty(cx
, info
, "valgrind", value
)) {
581 #ifdef JS_HAS_INTL_API
582 value
= BooleanValue(true);
584 value
= BooleanValue(false);
586 if (!JS_SetProperty(cx
, info
, "intl-api", value
)) {
591 value
= BooleanValue(false);
593 value
= BooleanValue(true);
595 if (!JS_SetProperty(cx
, info
, "mapped-array-buffer", value
)) {
600 value
= BooleanValue(true);
602 value
= BooleanValue(false);
604 if (!JS_SetProperty(cx
, info
, "moz-memory", value
)) {
608 value
.setInt32(sizeof(void*));
609 if (!JS_SetProperty(cx
, info
, "pointer-byte-size", value
)) {
613 #ifdef ENABLE_DECORATORS
614 value
= BooleanValue(true);
616 value
= BooleanValue(false);
618 if (!JS_SetProperty(cx
, info
, "decorators", value
)) {
623 value
= BooleanValue(true);
625 value
= BooleanValue(false);
627 if (!JS_SetProperty(cx
, info
, "fuzzing-defined", value
)) {
631 value
= Int32Value(JSFatInlineString::MAX_LENGTH_LATIN1
);
632 if (!JS_SetProperty(cx
, info
, "inline-latin1-chars", value
)) {
636 value
= Int32Value(JSFatInlineString::MAX_LENGTH_TWO_BYTE
);
637 if (!JS_SetProperty(cx
, info
, "inline-two-byte-chars", value
)) {
641 value
= Int32Value(JSThinInlineString::MAX_LENGTH_LATIN1
);
642 if (!JS_SetProperty(cx
, info
, "thin-inline-latin1-chars", value
)) {
646 value
= Int32Value(JSThinInlineString::MAX_LENGTH_TWO_BYTE
);
647 if (!JS_SetProperty(cx
, info
, "thin-inline-two-byte-chars", value
)) {
651 if (js::ThinInlineAtom::EverInstantiated
) {
652 value
= Int32Value(js::ThinInlineAtom::MAX_LENGTH_LATIN1
);
653 if (!JS_SetProperty(cx
, info
, "thin-inline-atom-latin1-chars", value
)) {
657 value
= Int32Value(js::ThinInlineAtom::MAX_LENGTH_TWO_BYTE
);
658 if (!JS_SetProperty(cx
, info
, "thin-inline-atom-two-byte-chars", value
)) {
663 value
= Int32Value(js::FatInlineAtom::MAX_LENGTH_LATIN1
);
664 if (!JS_SetProperty(cx
, info
, "fat-inline-atom-latin1-chars", value
)) {
668 value
= Int32Value(js::FatInlineAtom::MAX_LENGTH_TWO_BYTE
);
669 if (!JS_SetProperty(cx
, info
, "fat-inline-atom-two-byte-chars", value
)) {
673 if (args
.length() == 1) {
674 RootedString
str(cx
, ToString(cx
, args
[0]));
679 if (!JS_StringToId(cx
, str
, &id
)) {
684 if (JS_HasPropertyById(cx
, info
, id
, &hasProperty
) && hasProperty
) {
685 // Returning a true/false from GetProperty
686 return GetProperty(cx
, info
, info
, id
, args
.rval());
689 ReportUsageErrorASCII(cx
, callee
, "Invalid option name");
693 args
.rval().setObject(*info
);
697 static bool IsLCovEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
698 CallArgs args
= CallArgsFromVp(argc
, vp
);
699 args
.rval().setBoolean(coverage::IsLCovEnabled());
703 static bool TrialInline(JSContext
* cx
, unsigned argc
, Value
* vp
) {
704 CallArgs args
= CallArgsFromVp(argc
, vp
);
705 args
.rval().setUndefined();
708 if (iter
.done() || !iter
.isBaseline() || iter
.realm() != cx
->realm()) {
712 jit::BaselineFrame
* frame
= iter
.abstractFramePtr().asBaselineFrame();
713 if (!jit::CanIonCompileScript(cx
, frame
->script())) {
717 return jit::DoTrialInlining(cx
, frame
);
720 static bool ReturnStringCopy(JSContext
* cx
, CallArgs
& args
,
721 const char* message
) {
722 JSString
* str
= JS_NewStringCopyZ(cx
, message
);
727 args
.rval().setString(str
);
731 static bool MaybeGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
732 CallArgs args
= CallArgsFromVp(argc
, vp
);
734 args
.rval().setUndefined();
738 static bool GC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
739 CallArgs args
= CallArgsFromVp(argc
, vp
);
742 * If the first argument is 'zone', we collect any zones previously
743 * scheduled for GC via schedulegc. If the first argument is an object, we
744 * collect the object's zone (and any other zones scheduled for
745 * GC). Otherwise, we collect all zones.
748 if (args
.length() >= 1) {
750 if (arg
.isString()) {
751 if (!JS_StringEqualsLiteral(cx
, arg
.toString(), "zone", &zone
)) {
754 } else if (arg
.isObject()) {
755 PrepareZoneForGC(cx
, UncheckedUnwrap(&arg
.toObject())->zone());
760 JS::GCOptions options
= JS::GCOptions::Normal
;
761 JS::GCReason reason
= JS::GCReason::API
;
762 if (args
.length() >= 2) {
764 if (arg
.isString()) {
765 bool shrinking
= false;
766 bool last_ditch
= false;
767 if (!JS_StringEqualsLiteral(cx
, arg
.toString(), "shrinking",
771 if (!JS_StringEqualsLiteral(cx
, arg
.toString(), "last-ditch",
776 options
= JS::GCOptions::Shrink
;
777 } else if (last_ditch
) {
778 options
= JS::GCOptions::Shrink
;
779 reason
= JS::GCReason::LAST_DITCH
;
784 size_t preBytes
= cx
->runtime()->gc
.heapSize
.bytes();
787 PrepareForDebugGC(cx
->runtime());
789 JS::PrepareForFullGC(cx
);
792 JS::NonIncrementalGC(cx
, options
, reason
);
794 char buf
[256] = {'\0'};
795 if (!js::SupportDifferentialTesting()) {
796 SprintfLiteral(buf
, "before %zu, after %zu\n", preBytes
,
797 cx
->runtime()->gc
.heapSize
.bytes());
799 return ReturnStringCopy(cx
, args
, buf
);
802 static bool MinorGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
803 CallArgs args
= CallArgsFromVp(argc
, vp
);
804 if (args
.get(0) == BooleanValue(true)) {
805 gc::GCRuntime
& gc
= cx
->runtime()->gc
;
806 if (gc
.nursery().isEnabled()) {
807 gc
.storeBuffer().setAboutToOverflow(JS::GCReason::FULL_GENERIC_BUFFER
);
811 cx
->minorGC(JS::GCReason::API
);
812 args
.rval().setUndefined();
816 #define PARAM_NAME_LIST_ENTRY(name, key, writable) " " name
817 #define GC_PARAMETER_ARGS_LIST FOR_EACH_GC_PARAM(PARAM_NAME_LIST_ENTRY)
819 static bool GCParameter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
820 CallArgs args
= CallArgsFromVp(argc
, vp
);
822 JSString
* str
= ToString(cx
, args
.get(0));
827 UniqueChars name
= EncodeLatin1(cx
, str
);
834 if (!GetGCParameterInfo(name
.get(), ¶m
, &writable
)) {
836 cx
, "the first argument must be one of:" GC_PARAMETER_ARGS_LIST
);
841 if (args
.length() == 1) {
842 uint32_t value
= JS_GetGCParameter(cx
, param
);
843 args
.rval().setNumber(value
);
848 JS_ReportErrorASCII(cx
, "Attempt to change read-only parameter %s",
853 if (disableOOMFunctions
) {
856 case JSGC_MAX_NURSERY_BYTES
:
857 args
.rval().setUndefined();
865 if (!ToNumber(cx
, args
[1], &d
)) {
869 if (d
< 0 || d
> UINT32_MAX
) {
870 JS_ReportErrorASCII(cx
, "Parameter value out of range");
874 uint32_t value
= floor(d
);
875 bool ok
= cx
->runtime()->gc
.setParameter(cx
, param
, value
);
877 JS_ReportErrorASCII(cx
, "Parameter value out of range");
881 args
.rval().setUndefined();
885 static bool RelazifyFunctions(JSContext
* cx
, unsigned argc
, Value
* vp
) {
886 // Relazifying functions on GC is usually only done for compartments that are
887 // not active. To aid fuzzing, this testing function allows us to relazify
888 // even if the compartment is active.
890 CallArgs args
= CallArgsFromVp(argc
, vp
);
892 // Disable relazification of all scripts on stack. It is a pervasive
893 // assumption in the engine that running scripts still have bytecode.
894 for (AllScriptFramesIter
i(cx
); !i
.done(); ++i
) {
895 i
.script()->clearAllowRelazify();
898 cx
->runtime()->allowRelazificationForTesting
= true;
900 JS::PrepareForFullGC(cx
);
901 JS::NonIncrementalGC(cx
, JS::GCOptions::Shrink
, JS::GCReason::API
);
903 cx
->runtime()->allowRelazificationForTesting
= false;
905 args
.rval().setUndefined();
909 static bool IsProxy(JSContext
* cx
, unsigned argc
, Value
* vp
) {
910 CallArgs args
= CallArgsFromVp(argc
, vp
);
911 if (args
.length() != 1) {
912 JS_ReportErrorASCII(cx
, "the function takes exactly one argument");
915 if (!args
[0].isObject()) {
916 args
.rval().setBoolean(false);
919 args
.rval().setBoolean(args
[0].toObject().is
<ProxyObject
>());
923 static bool WasmIsSupported(JSContext
* cx
, unsigned argc
, Value
* vp
) {
924 CallArgs args
= CallArgsFromVp(argc
, vp
);
925 args
.rval().setBoolean(wasm::HasSupport(cx
) &&
926 wasm::AnyCompilerAvailable(cx
));
930 static bool WasmIsSupportedByHardware(JSContext
* cx
, unsigned argc
, Value
* vp
) {
931 CallArgs args
= CallArgsFromVp(argc
, vp
);
932 args
.rval().setBoolean(wasm::HasPlatformSupport());
936 static bool WasmDebuggingEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
937 CallArgs args
= CallArgsFromVp(argc
, vp
);
938 args
.rval().setBoolean(wasm::HasSupport(cx
) && wasm::BaselineAvailable(cx
));
942 static bool WasmStreamingEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
943 CallArgs args
= CallArgsFromVp(argc
, vp
);
944 args
.rval().setBoolean(wasm::StreamingCompilationAvailable(cx
));
948 static bool WasmCachingEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
949 CallArgs args
= CallArgsFromVp(argc
, vp
);
950 args
.rval().setBoolean(wasm::CodeCachingAvailable(cx
));
954 static bool WasmHugeMemorySupported(JSContext
* cx
, unsigned argc
, Value
* vp
) {
955 CallArgs args
= CallArgsFromVp(argc
, vp
);
956 #ifdef WASM_SUPPORTS_HUGE_MEMORY
957 args
.rval().setBoolean(true);
959 args
.rval().setBoolean(false);
964 static bool WasmMaxMemoryPages(JSContext
* cx
, unsigned argc
, Value
* vp
) {
965 CallArgs args
= CallArgsFromVp(argc
, vp
);
966 if (args
.length() < 1) {
967 JS_ReportErrorASCII(cx
, "not enough arguments");
970 if (!args
.get(0).isString()) {
971 JS_ReportErrorASCII(cx
, "index type must be a string");
974 RootedString
s(cx
, args
.get(0).toString());
975 Rooted
<JSLinearString
*> ls(cx
, s
->ensureLinear(cx
));
979 if (StringEqualsLiteral(ls
, "i32")) {
980 args
.rval().setInt32(
981 int32_t(wasm::MaxMemoryPages(wasm::IndexType::I32
).value()));
984 if (StringEqualsLiteral(ls
, "i64")) {
985 #ifdef ENABLE_WASM_MEMORY64
986 if (wasm::Memory64Available(cx
)) {
987 args
.rval().setInt32(
988 int32_t(wasm::MaxMemoryPages(wasm::IndexType::I64
).value()));
992 JS_ReportErrorASCII(cx
, "memory64 not enabled");
995 JS_ReportErrorASCII(cx
, "bad index type");
999 static bool WasmThreadsEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1000 CallArgs args
= CallArgsFromVp(argc
, vp
);
1001 args
.rval().setBoolean(wasm::ThreadsAvailable(cx
));
1005 #define WASM_FEATURE(NAME, ...) \
1006 static bool Wasm##NAME##Enabled(JSContext* cx, unsigned argc, Value* vp) { \
1007 CallArgs args = CallArgsFromVp(argc, vp); \
1008 args.rval().setBoolean(wasm::NAME##Available(cx)); \
1011 JS_FOR_WASM_FEATURES(WASM_FEATURE
);
1014 static bool WasmSimdEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1015 CallArgs args
= CallArgsFromVp(argc
, vp
);
1016 args
.rval().setBoolean(wasm::SimdAvailable(cx
));
1020 static bool WasmCompilersPresent(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1021 CallArgs args
= CallArgsFromVp(argc
, vp
);
1025 if (wasm::BaselinePlatformSupport()) {
1026 strcat(buf
, "baseline");
1028 if (wasm::IonPlatformSupport()) {
1035 JSString
* result
= JS_NewStringCopyZ(cx
, buf
);
1040 args
.rval().setString(result
);
1044 static bool WasmCompileMode(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1045 CallArgs args
= CallArgsFromVp(argc
, vp
);
1047 // This triplet of predicates will select zero or one baseline compiler and
1048 // zero or one optimizing compiler.
1049 bool baseline
= wasm::BaselineAvailable(cx
);
1050 bool ion
= wasm::IonAvailable(cx
);
1051 bool none
= !baseline
&& !ion
;
1052 bool tiered
= baseline
&& ion
;
1054 JSStringBuilder
result(cx
);
1055 if (none
&& !result
.append("none")) {
1058 if (baseline
&& !result
.append("baseline")) {
1061 if (tiered
&& !result
.append("+")) {
1064 if (ion
&& !result
.append("ion")) {
1067 if (JSString
* str
= result
.finishString()) {
1068 args
.rval().setString(str
);
1074 static bool WasmBaselineDisabledByFeatures(JSContext
* cx
, unsigned argc
,
1076 CallArgs args
= CallArgsFromVp(argc
, vp
);
1077 bool isDisabled
= false;
1078 JSStringBuilder
reason(cx
);
1079 if (!wasm::BaselineDisabledByFeatures(cx
, &isDisabled
, &reason
)) {
1083 JSString
* result
= reason
.finishString();
1087 args
.rval().setString(result
);
1089 args
.rval().setBoolean(false);
1094 static bool WasmIonDisabledByFeatures(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1095 CallArgs args
= CallArgsFromVp(argc
, vp
);
1096 bool isDisabled
= false;
1097 JSStringBuilder
reason(cx
);
1098 if (!wasm::IonDisabledByFeatures(cx
, &isDisabled
, &reason
)) {
1102 JSString
* result
= reason
.finishString();
1106 args
.rval().setString(result
);
1108 args
.rval().setBoolean(false);
1113 #ifdef ENABLE_WASM_SIMD
1115 static char lastAnalysisResult
[1024];
1119 void ReportSimdAnalysis(const char* data
) {
1120 strncpy(lastAnalysisResult
, data
, sizeof(lastAnalysisResult
));
1121 lastAnalysisResult
[sizeof(lastAnalysisResult
) - 1] = 0;
1126 // Unstable API for white-box testing of SIMD optimizations.
1128 // Current API: takes no arguments, returns a string describing the last Simd
1129 // simplification applied.
1131 static bool WasmSimdAnalysis(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1132 CallArgs args
= CallArgsFromVp(argc
, vp
);
1134 JS_NewStringCopyZ(cx
, *lastAnalysisResult
? lastAnalysisResult
: "none");
1138 args
.rval().setString(result
);
1139 *lastAnalysisResult
= (char)0;
1145 static bool WasmGlobalFromArrayBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1146 if (!wasm::HasSupport(cx
)) {
1147 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1150 CallArgs args
= CallArgsFromVp(argc
, vp
);
1152 if (args
.length() < 2) {
1153 JS_ReportErrorASCII(cx
, "not enough arguments");
1157 // Get the type of the value
1158 wasm::ValType valType
;
1159 if (!wasm::ToValType(cx
, args
.get(0), &valType
)) {
1163 // Get the array buffer for the value
1164 if (!args
.get(1).isObject() ||
1165 !args
.get(1).toObject().is
<ArrayBufferObject
>()) {
1166 JS_ReportErrorASCII(cx
, "argument is not an array buffer");
1169 Rooted
<ArrayBufferObject
*> buffer(
1170 cx
, &args
.get(1).toObject().as
<ArrayBufferObject
>());
1172 // Only allow POD to be created from bytes
1173 switch (valType
.kind()) {
1174 case wasm::ValType::I32
:
1175 case wasm::ValType::I64
:
1176 case wasm::ValType::F32
:
1177 case wasm::ValType::F64
:
1178 case wasm::ValType::V128
:
1181 JS_ReportErrorASCII(
1182 cx
, "invalid valtype for creating WebAssembly.Global from bytes");
1186 // Check we have all the bytes we need
1187 if (valType
.size() != buffer
->byteLength()) {
1188 JS_ReportErrorASCII(cx
, "array buffer has incorrect size");
1192 // Copy the bytes from buffer into a tagged val
1193 wasm::RootedVal
val(cx
);
1194 val
.get().initFromRootedLocation(valType
, buffer
->dataPointer());
1196 // Create the global object
1198 cx
, GlobalObject::getOrCreatePrototype(cx
, JSProto_WasmGlobal
));
1202 Rooted
<WasmGlobalObject
*> result(
1203 cx
, WasmGlobalObject::create(cx
, val
, false, proto
));
1208 args
.rval().setObject(*result
.get());
1212 enum class LaneInterp
{
1219 size_t LaneInterpLanes(LaneInterp interp
) {
1221 case LaneInterp::I32x4
:
1223 case LaneInterp::I64x2
:
1225 case LaneInterp::F32x4
:
1227 case LaneInterp::F64x2
:
1230 MOZ_ASSERT_UNREACHABLE();
1235 static bool ToLaneInterp(JSContext
* cx
, HandleValue v
, LaneInterp
* out
) {
1236 RootedString
interpStr(cx
, ToString(cx
, v
));
1240 Rooted
<JSLinearString
*> interpLinearStr(cx
, interpStr
->ensureLinear(cx
));
1241 if (!interpLinearStr
) {
1245 if (StringEqualsLiteral(interpLinearStr
, "i32x4")) {
1246 *out
= LaneInterp::I32x4
;
1248 } else if (StringEqualsLiteral(interpLinearStr
, "i64x2")) {
1249 *out
= LaneInterp::I64x2
;
1251 } else if (StringEqualsLiteral(interpLinearStr
, "f32x4")) {
1252 *out
= LaneInterp::F32x4
;
1254 } else if (StringEqualsLiteral(interpLinearStr
, "f64x2")) {
1255 *out
= LaneInterp::F64x2
;
1259 JS_ReportErrorASCII(cx
, "invalid lane interpretation");
1263 static bool WasmGlobalExtractLane(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1264 if (!wasm::HasSupport(cx
)) {
1265 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1268 CallArgs args
= CallArgsFromVp(argc
, vp
);
1270 if (args
.length() < 3) {
1271 JS_ReportErrorASCII(cx
, "not enough arguments");
1275 // Get the global value
1276 if (!args
.get(0).isObject() ||
1277 !args
.get(0).toObject().is
<WasmGlobalObject
>()) {
1278 JS_ReportErrorASCII(cx
, "argument is not wasm value");
1281 Rooted
<WasmGlobalObject
*> global(
1282 cx
, &args
.get(0).toObject().as
<WasmGlobalObject
>());
1284 // Check that we have a v128 value
1285 if (global
->type().kind() != wasm::ValType::V128
) {
1286 JS_ReportErrorASCII(cx
, "global is not a v128 value");
1289 wasm::V128 v128
= global
->val().get().v128();
1291 // Get the passed interpretation of lanes
1293 if (!ToLaneInterp(cx
, args
.get(1), &interp
)) {
1297 // Get the lane to extract
1299 if (!ToInt32(cx
, args
.get(2), &lane
)) {
1303 // Check that the lane interp is valid
1304 if (lane
< 0 || size_t(lane
) >= LaneInterpLanes(interp
)) {
1305 JS_ReportErrorASCII(cx
, "invalid lane for interp");
1309 wasm::RootedVal
val(cx
);
1311 case LaneInterp::I32x4
: {
1313 v128
.extractLane
<uint32_t>(lane
, &i
);
1314 val
.set(wasm::Val(i
));
1317 case LaneInterp::I64x2
: {
1319 v128
.extractLane
<uint64_t>(lane
, &i
);
1320 val
.set(wasm::Val(i
));
1323 case LaneInterp::F32x4
: {
1325 v128
.extractLane
<float>(lane
, &f
);
1326 val
.set(wasm::Val(f
));
1329 case LaneInterp::F64x2
: {
1331 v128
.extractLane
<double>(lane
, &d
);
1332 val
.set(wasm::Val(d
));
1336 MOZ_ASSERT_UNREACHABLE();
1340 cx
, GlobalObject::getOrCreatePrototype(cx
, JSProto_WasmGlobal
));
1341 Rooted
<WasmGlobalObject
*> result(
1342 cx
, WasmGlobalObject::create(cx
, val
, false, proto
));
1343 args
.rval().setObject(*result
.get());
1347 static bool WasmGlobalsEqual(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1348 if (!wasm::HasSupport(cx
)) {
1349 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1352 CallArgs args
= CallArgsFromVp(argc
, vp
);
1354 if (args
.length() < 2) {
1355 JS_ReportErrorASCII(cx
, "not enough arguments");
1359 if (!args
.get(0).isObject() ||
1360 !args
.get(0).toObject().is
<WasmGlobalObject
>() ||
1361 !args
.get(1).isObject() ||
1362 !args
.get(1).toObject().is
<WasmGlobalObject
>()) {
1363 JS_ReportErrorASCII(cx
, "argument is not wasm value");
1367 Rooted
<WasmGlobalObject
*> a(cx
,
1368 &args
.get(0).toObject().as
<WasmGlobalObject
>());
1369 Rooted
<WasmGlobalObject
*> b(cx
,
1370 &args
.get(1).toObject().as
<WasmGlobalObject
>());
1372 if (a
->type().kind() != b
->type().kind()) {
1373 JS_ReportErrorASCII(cx
, "globals are of different kind");
1378 const wasm::Val
& aVal
= a
->val().get();
1379 const wasm::Val
& bVal
= b
->val().get();
1380 switch (a
->type().kind()) {
1381 case wasm::ValType::I32
: {
1382 result
= aVal
.i32() == bVal
.i32();
1385 case wasm::ValType::I64
: {
1386 result
= aVal
.i64() == bVal
.i64();
1389 case wasm::ValType::F32
: {
1390 result
= mozilla::BitwiseCast
<uint32_t>(aVal
.f32()) ==
1391 mozilla::BitwiseCast
<uint32_t>(bVal
.f32());
1394 case wasm::ValType::F64
: {
1395 result
= mozilla::BitwiseCast
<uint64_t>(aVal
.f64()) ==
1396 mozilla::BitwiseCast
<uint64_t>(bVal
.f64());
1399 case wasm::ValType::V128
: {
1400 // Don't know the interpretation of the v128, so we only can do an exact
1401 // bitwise equality. Testing code can use wasmGlobalExtractLane to
1402 // workaround this if needed.
1403 result
= aVal
.v128() == bVal
.v128();
1406 case wasm::ValType::Ref
: {
1407 result
= aVal
.ref() == bVal
.ref();
1411 JS_ReportErrorASCII(cx
, "unsupported type");
1414 args
.rval().setBoolean(result
);
1418 // Flavors of NaN values for WebAssembly.
1420 // https://webassembly.github.io/spec/core/syntax/values.html#floating-point.
1421 enum class NaNFlavor
{
1422 // A canonical NaN value.
1423 // - the sign bit is unspecified,
1424 // - the 8-bit exponent is set to all 1s
1425 // - the MSB of the payload is set to 1 (a quieted NaN) and all others to 0.
1427 // An arithmetic NaN. This is the same as a canonical NaN including that the
1428 // payload MSB is set to 1, but one or more of the remaining payload bits MAY
1429 // BE set to 1 (a canonical NaN specifies all 0s).
1433 static bool IsNaNFlavor(uint32_t bits
, NaNFlavor flavor
) {
1435 case NaNFlavor::Canonical
: {
1436 return (bits
& 0x7fffffff) == 0x7fc00000;
1438 case NaNFlavor::Arithmetic
: {
1439 const uint32_t ArithmeticNaN
= 0x7f800000;
1440 const uint32_t ArithmeticPayloadMSB
= 0x00400000;
1441 bool isNaN
= (bits
& ArithmeticNaN
) == ArithmeticNaN
;
1442 bool isMSBSet
= (bits
& ArithmeticPayloadMSB
) == ArithmeticPayloadMSB
;
1443 return isNaN
&& isMSBSet
;
1450 static bool IsNaNFlavor(uint64_t bits
, NaNFlavor flavor
) {
1452 case NaNFlavor::Canonical
: {
1453 return (bits
& 0x7fffffffffffffff) == 0x7ff8000000000000;
1455 case NaNFlavor::Arithmetic
: {
1456 uint64_t ArithmeticNaN
= 0x7ff0000000000000;
1457 uint64_t ArithmeticPayloadMSB
= 0x0008000000000000;
1458 bool isNaN
= (bits
& ArithmeticNaN
) == ArithmeticNaN
;
1459 bool isMsbSet
= (bits
& ArithmeticPayloadMSB
) == ArithmeticPayloadMSB
;
1460 return isNaN
&& isMsbSet
;
1467 static bool ToNaNFlavor(JSContext
* cx
, HandleValue v
, NaNFlavor
* out
) {
1468 RootedString
flavorStr(cx
, ToString(cx
, v
));
1472 Rooted
<JSLinearString
*> flavorLinearStr(cx
, flavorStr
->ensureLinear(cx
));
1473 if (!flavorLinearStr
) {
1477 if (StringEqualsLiteral(flavorLinearStr
, "canonical_nan")) {
1478 *out
= NaNFlavor::Canonical
;
1480 } else if (StringEqualsLiteral(flavorLinearStr
, "arithmetic_nan")) {
1481 *out
= NaNFlavor::Arithmetic
;
1485 JS_ReportErrorASCII(cx
, "invalid nan flavor");
1489 static bool WasmGlobalIsNaN(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1490 if (!wasm::HasSupport(cx
)) {
1491 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1494 CallArgs args
= CallArgsFromVp(argc
, vp
);
1496 if (args
.length() < 2) {
1497 JS_ReportErrorASCII(cx
, "not enough arguments");
1501 if (!args
.get(0).isObject() ||
1502 !args
.get(0).toObject().is
<WasmGlobalObject
>()) {
1503 JS_ReportErrorASCII(cx
, "argument is not wasm value");
1506 Rooted
<WasmGlobalObject
*> global(
1507 cx
, &args
.get(0).toObject().as
<WasmGlobalObject
>());
1510 if (!ToNaNFlavor(cx
, args
.get(1), &flavor
)) {
1515 const wasm::Val
& val
= global
->val().get();
1516 switch (global
->type().kind()) {
1517 case wasm::ValType::F32
: {
1518 result
= IsNaNFlavor(mozilla::BitwiseCast
<uint32_t>(val
.f32()), flavor
);
1521 case wasm::ValType::F64
: {
1522 result
= IsNaNFlavor(mozilla::BitwiseCast
<uint64_t>(val
.f64()), flavor
);
1526 JS_ReportErrorASCII(cx
, "global is not a floating point value");
1529 args
.rval().setBoolean(result
);
1533 static bool WasmGlobalToString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1534 if (!wasm::HasSupport(cx
)) {
1535 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1538 CallArgs args
= CallArgsFromVp(argc
, vp
);
1540 if (args
.length() < 1) {
1541 JS_ReportErrorASCII(cx
, "not enough arguments");
1544 if (!args
.get(0).isObject() ||
1545 !args
.get(0).toObject().is
<WasmGlobalObject
>()) {
1546 JS_ReportErrorASCII(cx
, "argument is not wasm value");
1549 Rooted
<WasmGlobalObject
*> global(
1550 cx
, &args
.get(0).toObject().as
<WasmGlobalObject
>());
1551 const wasm::Val
& globalVal
= global
->val().get();
1554 switch (globalVal
.type().kind()) {
1555 case wasm::ValType::I32
: {
1556 result
= JS_smprintf("i32:%" PRIx32
, globalVal
.i32());
1559 case wasm::ValType::I64
: {
1560 result
= JS_smprintf("i64:%" PRIx64
, globalVal
.i64());
1563 case wasm::ValType::F32
: {
1564 result
= JS_smprintf("f32:%f", globalVal
.f32());
1567 case wasm::ValType::F64
: {
1568 result
= JS_smprintf("f64:%lf", globalVal
.f64());
1571 case wasm::ValType::V128
: {
1572 wasm::V128 v128
= globalVal
.v128();
1573 result
= JS_smprintf(
1574 "v128:%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x", v128
.bytes
[0],
1575 v128
.bytes
[1], v128
.bytes
[2], v128
.bytes
[3], v128
.bytes
[4],
1576 v128
.bytes
[5], v128
.bytes
[6], v128
.bytes
[7], v128
.bytes
[8],
1577 v128
.bytes
[9], v128
.bytes
[10], v128
.bytes
[11], v128
.bytes
[12],
1578 v128
.bytes
[13], v128
.bytes
[14], v128
.bytes
[15]);
1581 case wasm::ValType::Ref
: {
1582 result
= JS_smprintf("ref:%" PRIxPTR
, globalVal
.ref().rawValue());
1586 MOZ_ASSERT_UNREACHABLE();
1589 args
.rval().setString(JS_NewStringCopyZ(cx
, result
.get()));
1593 static bool WasmLosslessInvoke(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1594 if (!wasm::HasSupport(cx
)) {
1595 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1598 CallArgs args
= CallArgsFromVp(argc
, vp
);
1600 if (args
.length() < 1) {
1601 JS_ReportErrorASCII(cx
, "not enough arguments");
1604 if (!args
.get(0).isObject()) {
1605 JS_ReportErrorASCII(cx
, "argument is not an object");
1609 RootedFunction
func(cx
, args
[0].toObject().maybeUnwrapIf
<JSFunction
>());
1610 if (!func
|| !wasm::IsWasmExportedFunction(func
)) {
1611 JS_ReportErrorASCII(cx
, "argument is not an exported wasm function");
1615 // Get the instance and funcIndex for calling the function
1616 wasm::Instance
& instance
= wasm::ExportedFunctionToInstance(func
);
1617 uint32_t funcIndex
= wasm::ExportedFunctionToFuncIndex(func
);
1619 // Set up a modified call frame following the standard JS
1620 // [callee, this, arguments...] convention.
1621 RootedValueVector
wasmCallFrame(cx
);
1622 size_t len
= 2 + args
.length();
1623 if (!wasmCallFrame
.resize(len
)) {
1626 wasmCallFrame
[0].set(args
.calleev());
1627 wasmCallFrame
[1].set(args
.thisv());
1628 // Copy over the arguments needed to invoke the provided wasm function,
1629 // skipping the wasm function we're calling that is at `args.get(0)`.
1630 for (size_t i
= 1; i
< args
.length(); i
++) {
1631 size_t wasmArg
= i
- 1;
1632 wasmCallFrame
[2 + wasmArg
].set(args
.get(i
));
1634 size_t wasmArgc
= argc
- 1;
1635 CallArgs
wasmCallArgs(CallArgsFromVp(wasmArgc
, wasmCallFrame
.begin()));
1637 // Invoke the function with the new call frame
1638 bool result
= instance
.callExport(cx
, funcIndex
, wasmCallArgs
,
1639 wasm::CoercionLevel::Lossless
);
1640 // Assign the wasm rval to our rval
1641 args
.rval().set(wasmCallArgs
.rval());
1645 static bool ConvertToTier(JSContext
* cx
, HandleValue value
,
1646 const wasm::Code
& code
, wasm::Tier
* tier
) {
1647 RootedString
option(cx
, JS::ToString(cx
, value
));
1653 bool stableTier
= false;
1654 bool bestTier
= false;
1655 bool baselineTier
= false;
1656 bool ionTier
= false;
1658 if (!JS_StringEqualsLiteral(cx
, option
, "stable", &stableTier
) ||
1659 !JS_StringEqualsLiteral(cx
, option
, "best", &bestTier
) ||
1660 !JS_StringEqualsLiteral(cx
, option
, "baseline", &baselineTier
) ||
1661 !JS_StringEqualsLiteral(cx
, option
, "ion", &ionTier
)) {
1666 *tier
= code
.stableTier();
1667 } else if (bestTier
) {
1668 *tier
= code
.bestTier();
1669 } else if (baselineTier
) {
1670 *tier
= wasm::Tier::Baseline
;
1671 } else if (ionTier
) {
1672 *tier
= wasm::Tier::Optimized
;
1674 // You can omit the argument but you can't pass just anything you like
1681 static bool WasmExtractCode(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1682 if (!wasm::HasSupport(cx
)) {
1683 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1687 CallArgs args
= CallArgsFromVp(argc
, vp
);
1689 if (!args
.get(0).isObject()) {
1690 JS_ReportErrorASCII(cx
, "argument is not an object");
1694 Rooted
<WasmModuleObject
*> module(
1695 cx
, args
[0].toObject().maybeUnwrapIf
<WasmModuleObject
>());
1697 JS_ReportErrorASCII(cx
, "argument is not a WebAssembly.Module");
1701 wasm::Tier tier
= module
->module().code().stableTier();
1703 if (args
.length() > 1 &&
1704 !ConvertToTier(cx
, args
[1], module
->module().code(), &tier
)) {
1705 args
.rval().setNull();
1709 RootedValue
result(cx
);
1710 if (!module
->module().extractCode(cx
, tier
, &result
)) {
1714 args
.rval().set(result
);
1718 struct DisasmBuffer
{
1719 JSStringBuilder builder
;
1721 explicit DisasmBuffer(JSContext
* cx
) : builder(cx
), oom(false) {}
1724 static bool HasDisassembler(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1725 CallArgs args
= CallArgsFromVp(argc
, vp
);
1726 args
.rval().setBoolean(jit::HasDisassembler());
1730 MOZ_THREAD_LOCAL(DisasmBuffer
*) disasmBuf
;
1732 static void captureDisasmText(const char* text
) {
1733 DisasmBuffer
* buf
= disasmBuf
.get();
1734 if (!buf
->builder
.append(text
, strlen(text
)) || !buf
->builder
.append('\n')) {
1739 static bool DisassembleNative(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1740 CallArgs args
= CallArgsFromVp(argc
, vp
);
1741 args
.rval().setUndefined();
1743 if (args
.length() < 1) {
1744 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1745 JSMSG_MORE_ARGS_NEEDED
, "disnative", "1", "",
1750 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
1751 JS_ReportErrorASCII(cx
, "The first argument must be a function.");
1755 JSSprinter
sprinter(cx
);
1756 if (!sprinter
.init()) {
1760 RootedFunction
fun(cx
, &args
[0].toObject().as
<JSFunction
>());
1762 uint8_t* jit_begin
= nullptr;
1763 uint8_t* jit_end
= nullptr;
1765 if (fun
->isAsmJSNative() || fun
->isWasmWithJitEntry()) {
1766 if (fun
->isAsmJSNative()) {
1769 sprinter
.printf("; backend=asmjs\n");
1770 sprinter
.printf("; backend=wasm\n");
1772 js::wasm::Instance
& inst
= fun
->wasmInstance();
1773 const js::wasm::Code
& code
= inst
.code();
1774 js::wasm::Tier tier
= code
.bestTier();
1776 const js::wasm::MetadataTier
& meta
= inst
.metadata(tier
);
1778 const js::wasm::CodeSegment
& segment
= code
.segment(tier
);
1779 const uint32_t funcIndex
= code
.getFuncIndex(&*fun
);
1780 const js::wasm::FuncExport
& func
= meta
.lookupFuncExport(funcIndex
);
1781 const js::wasm::CodeRange
& codeRange
= meta
.codeRange(func
);
1783 jit_begin
= segment
.base() + codeRange
.begin();
1784 jit_end
= segment
.base() + codeRange
.end();
1785 } else if (fun
->hasJitScript()) {
1786 JSScript
* script
= fun
->nonLazyScript();
1787 if (script
== nullptr) {
1791 js::jit::IonScript
* ion
=
1792 script
->hasIonScript() ? script
->ionScript() : nullptr;
1793 js::jit::BaselineScript
* baseline
=
1794 script
->hasBaselineScript() ? script
->baselineScript() : nullptr;
1795 if (ion
&& ion
->method()) {
1796 sprinter
.printf("; backend=ion\n");
1797 jit_begin
= ion
->method()->raw();
1798 jit_end
= ion
->method()->rawEnd();
1799 } else if (baseline
) {
1800 sprinter
.printf("; backend=baseline\n");
1801 jit_begin
= baseline
->method()->raw();
1802 jit_end
= baseline
->method()->rawEnd();
1805 JS_ReportErrorASCII(cx
,
1806 "The function hasn't been warmed up, hence no JIT code "
1811 if (jit_begin
== nullptr || jit_end
== nullptr) {
1815 #ifdef JS_CODEGEN_ARM
1816 // The ARM32 disassembler is currently not fuzzing-safe because it doesn't
1817 // handle constant pools correctly (bug 1875363).
1819 JS_ReportErrorASCII(cx
, "disnative is not fuzzing-safe on ARM32");
1824 // Dump the raw code to a file before disassembling in case
1825 // finishString triggers a GC and discards the jitcode.
1826 if (!fuzzingSafe
&& args
.length() > 1 && args
[1].isString()) {
1827 RootedString
str(cx
, args
[1].toString());
1828 JS::UniqueChars fileNameBytes
= JS_EncodeStringToUTF8(cx
, str
);
1830 const char* fileName
= fileNameBytes
.get();
1832 ReportOutOfMemory(cx
);
1836 FILE* f
= fopen(fileName
, "w");
1838 JS_ReportErrorASCII(cx
, "Could not open file for writing.");
1842 uintptr_t expected_length
= reinterpret_cast<uintptr_t>(jit_end
) -
1843 reinterpret_cast<uintptr_t>(jit_begin
);
1844 if (expected_length
!= fwrite(jit_begin
, jit_end
- jit_begin
, 1, f
)) {
1845 JS_ReportErrorASCII(cx
, "Did not write all function bytes to the file.");
1852 DisasmBuffer
buf(cx
);
1853 disasmBuf
.set(&buf
);
1854 auto onFinish
= mozilla::MakeScopeExit([&] { disasmBuf
.set(nullptr); });
1856 jit::Disassemble(jit_begin
, jit_end
- jit_begin
, &captureDisasmText
);
1859 ReportOutOfMemory(cx
);
1862 JSString
* sresult
= buf
.builder
.finishString();
1864 ReportOutOfMemory(cx
);
1867 sprinter
.putString(cx
, sresult
);
1869 JSString
* str
= sprinter
.release(cx
);
1874 args
[0].setUndefined();
1875 args
.rval().setString(str
);
1880 static bool ComputeTier(JSContext
* cx
, const wasm::Code
& code
,
1881 HandleValue tierSelection
, wasm::Tier
* tier
) {
1882 *tier
= code
.stableTier();
1883 if (!tierSelection
.isUndefined() &&
1884 !ConvertToTier(cx
, tierSelection
, code
, tier
)) {
1885 JS_ReportErrorASCII(cx
, "invalid tier");
1889 if (!code
.hasTier(*tier
)) {
1890 JS_ReportErrorASCII(cx
, "function missing selected tier");
1897 template <typename DisasmFunction
>
1898 static bool DisassembleIt(JSContext
* cx
, bool asString
, MutableHandleValue rval
,
1899 DisasmFunction
&& disassembleIt
) {
1901 DisasmBuffer
buf(cx
);
1902 disasmBuf
.set(&buf
);
1903 auto onFinish
= mozilla::MakeScopeExit([&] { disasmBuf
.set(nullptr); });
1904 disassembleIt(captureDisasmText
);
1906 ReportOutOfMemory(cx
);
1909 JSString
* sresult
= buf
.builder
.finishString();
1911 ReportOutOfMemory(cx
);
1914 rval
.setString(sresult
);
1918 disassembleIt([](const char* text
) { fprintf(stderr
, "%s\n", text
); });
1922 static bool WasmDisassembleFunction(JSContext
* cx
, const HandleFunction
& func
,
1923 HandleValue tierSelection
, bool asString
,
1924 MutableHandleValue rval
) {
1925 wasm::Instance
& instance
= wasm::ExportedFunctionToInstance(func
);
1928 if (!ComputeTier(cx
, instance
.code(), tierSelection
, &tier
)) {
1932 uint32_t funcIndex
= wasm::ExportedFunctionToFuncIndex(func
);
1933 return DisassembleIt(
1934 cx
, asString
, rval
, [&](void (*captureText
)(const char*)) {
1935 instance
.disassembleExport(cx
, funcIndex
, tier
, captureText
);
1939 static bool WasmDisassembleCode(JSContext
* cx
, const wasm::Code
& code
,
1940 HandleValue tierSelection
, int kindSelection
,
1941 bool asString
, MutableHandleValue rval
) {
1943 if (!ComputeTier(cx
, code
, tierSelection
, &tier
)) {
1947 return DisassembleIt(cx
, asString
, rval
,
1948 [&](void (*captureText
)(const char*)) {
1949 code
.disassemble(cx
, tier
, kindSelection
, captureText
);
1953 static bool WasmDisassemble(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1954 if (!wasm::HasSupport(cx
)) {
1955 JS_ReportErrorASCII(cx
, "wasm support unavailable");
1959 CallArgs args
= CallArgsFromVp(argc
, vp
);
1961 args
.rval().set(UndefinedValue());
1963 if (!args
.get(0).isObject()) {
1964 JS_ReportErrorASCII(cx
, "argument is not an object");
1968 bool asString
= false;
1969 RootedValue
tierSelection(cx
);
1970 int kindSelection
= (1 << wasm::CodeRange::Function
);
1971 if (args
.length() > 1 && args
[1].isObject()) {
1972 RootedObject
options(cx
, &args
[1].toObject());
1973 RootedValue
val(cx
);
1975 if (!JS_GetProperty(cx
, options
, "asString", &val
)) {
1978 asString
= val
.isBoolean() && val
.toBoolean();
1980 if (!JS_GetProperty(cx
, options
, "tier", &tierSelection
)) {
1984 if (!JS_GetProperty(cx
, options
, "kinds", &val
)) {
1987 if (val
.isString() && val
.toString()->hasLatin1Chars()) {
1988 AutoStableStringChars
stable(cx
);
1989 if (!stable
.init(cx
, val
.toString())) {
1992 const char* p
= (const char*)(stable
.latin1Chars());
1993 const char* end
= p
+ val
.toString()->length();
1996 if (strncmp(p
, "Function", 8) == 0) {
1997 selection
|= (1 << wasm::CodeRange::Function
);
1999 } else if (strncmp(p
, "InterpEntry", 11) == 0) {
2000 selection
|= (1 << wasm::CodeRange::InterpEntry
);
2002 } else if (strncmp(p
, "JitEntry", 8) == 0) {
2003 selection
|= (1 << wasm::CodeRange::JitEntry
);
2005 } else if (strncmp(p
, "ImportInterpExit", 16) == 0) {
2006 selection
|= (1 << wasm::CodeRange::ImportInterpExit
);
2008 } else if (strncmp(p
, "ImportJitExit", 13) == 0) {
2009 selection
|= (1 << wasm::CodeRange::ImportJitExit
);
2011 } else if (strncmp(p
, "all", 3) == 0) {
2017 if (p
== end
|| *p
!= ',') {
2023 kindSelection
= selection
;
2025 JS_ReportErrorASCII(cx
, "argument object has invalid `kinds`");
2031 RootedFunction
func(cx
, args
[0].toObject().maybeUnwrapIf
<JSFunction
>());
2032 if (func
&& wasm::IsWasmExportedFunction(func
)) {
2033 return WasmDisassembleFunction(cx
, func
, tierSelection
, asString
,
2036 if (args
[0].toObject().is
<WasmModuleObject
>()) {
2037 return WasmDisassembleCode(
2038 cx
, args
[0].toObject().as
<WasmModuleObject
>().module().code(),
2039 tierSelection
, kindSelection
, asString
, args
.rval());
2041 if (args
[0].toObject().is
<WasmInstanceObject
>()) {
2042 return WasmDisassembleCode(
2043 cx
, args
[0].toObject().as
<WasmInstanceObject
>().instance().code(),
2044 tierSelection
, kindSelection
, asString
, args
.rval());
2046 JS_ReportErrorASCII(
2047 cx
, "argument is not an exported wasm function or a wasm module");
2051 enum class Flag
{ Tier2Complete
, Deserialized
};
2053 static bool WasmReturnFlag(JSContext
* cx
, unsigned argc
, Value
* vp
, Flag flag
) {
2054 CallArgs args
= CallArgsFromVp(argc
, vp
);
2056 if (!args
.get(0).isObject()) {
2057 JS_ReportErrorASCII(cx
, "argument is not an object");
2061 Rooted
<WasmModuleObject
*> module(
2062 cx
, args
[0].toObject().maybeUnwrapIf
<WasmModuleObject
>());
2064 JS_ReportErrorASCII(cx
, "argument is not a WebAssembly.Module");
2070 case Flag::Tier2Complete
:
2071 b
= !module
->module().testingTier2Active();
2073 case Flag::Deserialized
:
2074 b
= module
->module().loggingDeserialized();
2078 args
.rval().set(BooleanValue(b
));
2083 static bool wasmMetadataAnalysis(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2084 CallArgs args
= CallArgsFromVp(argc
, vp
);
2086 if (!args
.get(0).isObject()) {
2087 JS_ReportErrorASCII(cx
, "argument is not an object");
2091 if (!cx
->options().wasmTestMetadata()) {
2095 if (args
[0].toObject().is
<WasmModuleObject
>()) {
2096 HashMap
<const char*, uint32_t, mozilla::CStringHasher
, SystemAllocPolicy
>
2099 .as
<WasmModuleObject
>()
2102 .metadataAnalysis(cx
);
2103 if (hashmap
.empty()) {
2104 JS_ReportErrorASCII(cx
, "Metadata analysis has failed");
2107 // metadataAnalysis returned a map of {key, value} with various statistics
2108 // convert it into a dictionary to be used by JS
2109 Rooted
<IdValueVector
> props(cx
, IdValueVector(cx
));
2111 for (auto iter
= hashmap
.iter(); !iter
.done(); iter
.next()) {
2112 const auto* key
= iter
.get().key();
2113 auto value
= iter
.get().value();
2115 JSString
* string
= JS_NewStringCopyZ(cx
, key
);
2117 IdValuePair(NameToId(string
->asLinear().toPropertyName(cx
)),
2118 NumberValue(value
)))) {
2119 ReportOutOfMemory(cx
);
2125 NewPlainObjectWithUniqueNames(cx
, props
.begin(), props
.length());
2126 args
.rval().setObject(*results
);
2131 JS_ReportErrorASCII(
2132 cx
, "argument is not an exported wasm function or a wasm module");
2138 static bool WasmHasTier2CompilationCompleted(JSContext
* cx
, unsigned argc
,
2140 return WasmReturnFlag(cx
, argc
, vp
, Flag::Tier2Complete
);
2143 static bool WasmLoadedFromCache(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2144 return WasmReturnFlag(cx
, argc
, vp
, Flag::Deserialized
);
2147 static bool WasmBuiltinI8VecMul(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2148 if (!wasm::HasSupport(cx
)) {
2149 JS_ReportErrorASCII(cx
, "wasm support unavailable");
2153 CallArgs args
= CallArgsFromVp(argc
, vp
);
2155 Rooted
<WasmModuleObject
*> module(cx
);
2156 if (!wasm::CompileBuiltinModule(cx
, wasm::BuiltinModuleId::SelfTest
,
2160 args
.rval().set(ObjectValue(*module
.get()));
2164 #ifdef ENABLE_WASM_GC
2165 static bool WasmGcReadField(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2166 CallArgs args
= CallArgsFromVp(argc
, vp
);
2167 RootedObject
callee(cx
, &args
.callee());
2169 if (!args
.requireAtLeast(cx
, "wasmGcReadField", 2)) {
2173 if (!args
[0].isObject() || !args
[0].toObject().is
<WasmGcObject
>()) {
2174 ReportUsageErrorASCII(cx
, callee
,
2175 "First argument must be a WebAssembly GC object");
2180 if (!JS::ToInt32(cx
, args
[1], &fieldIndex
) || fieldIndex
< 0) {
2181 ReportUsageErrorASCII(cx
, callee
,
2182 "Second argument must be a non-negative integer");
2186 Rooted
<WasmGcObject
*> gcObject(cx
, &args
[0].toObject().as
<WasmGcObject
>());
2187 Rooted
<Value
> gcValue(cx
);
2188 if (!WasmGcObject::loadValue(cx
, gcObject
, jsid::Int(int32_t(fieldIndex
)),
2193 args
.rval().set(gcValue
);
2197 static bool WasmGcArrayLength(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2198 CallArgs args
= CallArgsFromVp(argc
, vp
);
2199 RootedObject
callee(cx
, &args
.callee());
2201 if (!args
.requireAtLeast(cx
, "wasmGcArrayLength", 1)) {
2205 if (!args
[0].isObject() || !args
[0].toObject().is
<WasmArrayObject
>()) {
2206 ReportUsageErrorASCII(cx
, callee
,
2207 "First argument must be a WebAssembly GC array");
2211 WasmArrayObject
& arr
= args
[0].toObject().as
<WasmArrayObject
>();
2212 args
.rval().setInt32(int32_t(arr
.numElements_
));
2215 #endif // ENABLE_WASM_GC
2217 static bool LargeArrayBufferSupported(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2218 CallArgs args
= CallArgsFromVp(argc
, vp
);
2219 args
.rval().setBoolean(ArrayBufferObject::ByteLengthLimit
>
2220 ArrayBufferObject::ByteLengthLimitForSmallBuffer
);
2224 static bool IsLazyFunction(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2225 CallArgs args
= CallArgsFromVp(argc
, vp
);
2226 if (args
.length() != 1) {
2227 JS_ReportErrorASCII(cx
, "The function takes exactly one argument.");
2230 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
2231 JS_ReportErrorASCII(cx
, "The first argument should be a function.");
2234 JSFunction
* fun
= &args
[0].toObject().as
<JSFunction
>();
2235 args
.rval().setBoolean(fun
->isInterpreted() && !fun
->hasBytecode());
2239 static bool IsRelazifiableFunction(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2240 CallArgs args
= CallArgsFromVp(argc
, vp
);
2241 if (args
.length() != 1) {
2242 JS_ReportErrorASCII(cx
, "The function takes exactly one argument.");
2245 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
2246 JS_ReportErrorASCII(cx
, "The first argument should be a function.");
2250 JSFunction
* fun
= &args
[0].toObject().as
<JSFunction
>();
2251 args
.rval().setBoolean(fun
->hasBytecode() &&
2252 fun
->nonLazyScript()->allowRelazify());
2256 static bool IsInStencilCache(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2257 CallArgs args
= CallArgsFromVp(argc
, vp
);
2258 if (args
.length() != 1) {
2259 JS_ReportErrorASCII(cx
, "The function takes exactly one argument.");
2263 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
2264 JS_ReportErrorASCII(cx
, "The first argument should be a function.");
2269 // When running code concurrently to fill-up the stencil cache, the content
2270 // is not garanteed to be present.
2271 args
.rval().setBoolean(false);
2275 JSFunction
* fun
= &args
[0].toObject().as
<JSFunction
>();
2276 BaseScript
* script
= fun
->baseScript();
2277 RefPtr
<ScriptSource
> ss
= script
->scriptSource();
2278 DelazificationCache
& cache
= DelazificationCache::getSingleton();
2279 auto guard
= cache
.isSourceCached(ss
);
2281 args
.rval().setBoolean(false);
2285 StencilContext
key(ss
, script
->extent());
2286 frontend::CompilationStencil
* stencil
= cache
.lookup(guard
, key
);
2287 args
.rval().setBoolean(bool(stencil
));
2291 static bool WaitForStencilCache(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2292 CallArgs args
= CallArgsFromVp(argc
, vp
);
2293 if (args
.length() != 1) {
2294 JS_ReportErrorASCII(cx
, "The function takes exactly one argument.");
2298 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
2299 JS_ReportErrorASCII(cx
, "The first argument should be a function.");
2302 args
.rval().setUndefined();
2304 JSFunction
* fun
= &args
[0].toObject().as
<JSFunction
>();
2305 BaseScript
* script
= fun
->baseScript();
2306 RefPtr
<ScriptSource
> ss
= script
->scriptSource();
2307 DelazificationCache
& cache
= DelazificationCache::getSingleton();
2308 StencilContext
key(ss
, script
->extent());
2310 AutoLockHelperThreadState lock
;
2311 if (!HelperThreadState().isInitialized(lock
)) {
2317 // This capture a Mutex that we have to release before using the wait
2319 auto guard
= cache
.isSourceCached(ss
);
2324 frontend::CompilationStencil
* stencil
= cache
.lookup(guard
, key
);
2330 HelperThreadState().wait(lock
);
2335 static bool HasSameBytecodeData(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2336 CallArgs args
= CallArgsFromVp(argc
, vp
);
2337 if (args
.length() != 2) {
2338 JS_ReportErrorASCII(cx
, "The function takes exactly two argument.");
2342 auto GetSharedData
= [](JSContext
* cx
,
2343 HandleValue v
) -> SharedImmutableScriptData
* {
2344 if (!v
.isObject()) {
2345 JS_ReportErrorASCII(cx
, "The arguments must be interpreted functions.");
2349 RootedObject
obj(cx
, CheckedUnwrapDynamic(&v
.toObject(), cx
));
2354 if (!obj
->is
<JSFunction
>() || !obj
->as
<JSFunction
>().isInterpreted()) {
2355 JS_ReportErrorASCII(cx
, "The arguments must be interpreted functions.");
2359 AutoRealm
ar(cx
, obj
);
2360 RootedFunction
fun(cx
, &obj
->as
<JSFunction
>());
2361 RootedScript
script(cx
, JSFunction::getOrCreateScript(cx
, fun
));
2366 MOZ_ASSERT(script
->sharedData());
2367 return script
->sharedData();
2370 // NOTE: We use RefPtr below to keep the data alive across possible GC since
2371 // the functions may be in different Zones.
2373 RefPtr
<SharedImmutableScriptData
> sharedData1
= GetSharedData(cx
, args
[0]);
2378 RefPtr
<SharedImmutableScriptData
> sharedData2
= GetSharedData(cx
, args
[1]);
2383 args
.rval().setBoolean(sharedData1
== sharedData2
);
2387 static bool InternalConst(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2388 CallArgs args
= CallArgsFromVp(argc
, vp
);
2389 if (args
.length() == 0) {
2390 JS_ReportErrorASCII(cx
, "the function takes exactly one argument");
2394 JSString
* str
= ToString(cx
, args
[0]);
2398 JSLinearString
* linear
= JS_EnsureLinearString(cx
, str
);
2403 if (JS_LinearStringEqualsLiteral(linear
, "MARK_STACK_BASE_CAPACITY")) {
2404 args
.rval().setNumber(uint32_t(js::MARK_STACK_BASE_CAPACITY
));
2406 JS_ReportErrorASCII(cx
, "unknown const name");
2412 static bool GCPreserveCode(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2413 CallArgs args
= CallArgsFromVp(argc
, vp
);
2415 if (args
.length() != 0) {
2416 RootedObject
callee(cx
, &args
.callee());
2417 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2421 cx
->runtime()->gc
.setAlwaysPreserveCode();
2423 args
.rval().setUndefined();
2429 static bool ParseGCZealMode(JSContext
* cx
, const CallArgs
& args
,
2432 if (!ToUint32(cx
, args
.get(0), &value
)) {
2436 if (value
> uint32_t(gc::ZealMode::Limit
)) {
2437 JS_ReportErrorASCII(cx
, "gczeal argument out of range");
2441 *zeal
= static_cast<uint8_t>(value
);
2445 static bool GCZeal(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2446 CallArgs args
= CallArgsFromVp(argc
, vp
);
2448 if (args
.length() > 2) {
2449 RootedObject
callee(cx
, &args
.callee());
2450 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2454 if (args
.length() == 0) {
2455 uint32_t zealBits
, unused1
, unused2
;
2456 cx
->runtime()->gc
.getZealBits(&zealBits
, &unused1
, &unused2
);
2457 args
.rval().setNumber(zealBits
);
2462 if (!ParseGCZealMode(cx
, args
, &zeal
)) {
2466 uint32_t frequency
= JS_DEFAULT_ZEAL_FREQ
;
2467 if (args
.length() >= 2) {
2468 if (!ToUint32(cx
, args
.get(1), &frequency
)) {
2473 JS_SetGCZeal(cx
, zeal
, frequency
);
2474 args
.rval().setUndefined();
2478 static bool UnsetGCZeal(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2479 CallArgs args
= CallArgsFromVp(argc
, vp
);
2481 if (args
.length() > 1) {
2482 RootedObject
callee(cx
, &args
.callee());
2483 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2488 if (!ParseGCZealMode(cx
, args
, &zeal
)) {
2492 JS_UnsetGCZeal(cx
, zeal
);
2493 args
.rval().setUndefined();
2497 static bool ScheduleGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2498 CallArgs args
= CallArgsFromVp(argc
, vp
);
2500 if (args
.length() > 1) {
2501 RootedObject
callee(cx
, &args
.callee());
2502 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2506 if (args
.length() == 0) {
2507 /* Fetch next zeal trigger only. */
2508 } else if (args
[0].isNumber()) {
2509 /* Schedule a GC to happen after |arg| allocations. */
2510 JS_ScheduleGC(cx
, std::max(int(args
[0].toNumber()), 0));
2512 RootedObject
callee(cx
, &args
.callee());
2513 ReportUsageErrorASCII(cx
, callee
, "Bad argument - expecting number");
2520 JS_GetGCZealBits(cx
, &zealBits
, &freq
, &next
);
2521 args
.rval().setInt32(next
);
2525 static bool SelectForGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2526 CallArgs args
= CallArgsFromVp(argc
, vp
);
2529 * The selectedForMarking set is intended to be manually marked at slice
2530 * start to detect missing pre-barriers. It is invalid for nursery things
2531 * to be in the set, so evict the nursery before adding items.
2533 cx
->runtime()->gc
.evictNursery();
2535 for (unsigned i
= 0; i
< args
.length(); i
++) {
2536 if (args
[i
].isObject()) {
2537 if (!cx
->runtime()->gc
.selectForMarking(&args
[i
].toObject())) {
2543 args
.rval().setUndefined();
2547 static bool VerifyPreBarriers(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2548 CallArgs args
= CallArgsFromVp(argc
, vp
);
2550 if (args
.length() > 0) {
2551 RootedObject
callee(cx
, &args
.callee());
2552 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2556 gc::VerifyBarriers(cx
->runtime(), gc::PreBarrierVerifier
);
2557 args
.rval().setUndefined();
2561 static bool VerifyPostBarriers(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2562 // This is a no-op since the post barrier verifier was removed.
2563 CallArgs args
= CallArgsFromVp(argc
, vp
);
2564 if (args
.length()) {
2565 RootedObject
callee(cx
, &args
.callee());
2566 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2569 args
.rval().setUndefined();
2573 static bool CurrentGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2574 CallArgs args
= CallArgsFromVp(argc
, vp
);
2576 if (args
.length() != 0) {
2577 RootedObject
callee(cx
, &args
.callee());
2578 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2582 RootedObject
result(cx
, JS_NewPlainObject(cx
));
2587 js::gc::GCRuntime
& gc
= cx
->runtime()->gc
;
2588 const char* state
= StateName(gc
.state());
2590 RootedString
str(cx
, JS_NewStringCopyZ(cx
, state
));
2594 RootedValue
val(cx
, StringValue(str
));
2595 if (!JS_DefineProperty(cx
, result
, "incrementalState", val
,
2596 JSPROP_ENUMERATE
)) {
2600 if (gc
.state() == js::gc::State::Sweep
) {
2601 val
= Int32Value(gc
.getCurrentSweepGroupIndex());
2602 if (!JS_DefineProperty(cx
, result
, "sweepGroup", val
, JSPROP_ENUMERATE
)) {
2607 val
= BooleanValue(gc
.isIncrementalGCInProgress() && gc
.isShrinkingGC());
2608 if (!JS_DefineProperty(cx
, result
, "isShrinking", val
, JSPROP_ENUMERATE
)) {
2612 val
= Int32Value(gc
.gcNumber());
2613 if (!JS_DefineProperty(cx
, result
, "number", val
, JSPROP_ENUMERATE
)) {
2617 val
= Int32Value(gc
.minorGCCount());
2618 if (!JS_DefineProperty(cx
, result
, "minorCount", val
, JSPROP_ENUMERATE
)) {
2622 val
= Int32Value(gc
.majorGCCount());
2623 if (!JS_DefineProperty(cx
, result
, "majorCount", val
, JSPROP_ENUMERATE
)) {
2627 val
= BooleanValue(gc
.isFullGc());
2628 if (!JS_DefineProperty(cx
, result
, "isFull", val
, JSPROP_ENUMERATE
)) {
2632 val
= BooleanValue(gc
.isCompactingGc());
2633 if (!JS_DefineProperty(cx
, result
, "isCompacting", val
, JSPROP_ENUMERATE
)) {
2638 val
= Int32Value(gc
.testMarkQueuePos());
2639 if (!JS_DefineProperty(cx
, result
, "queuePos", val
, JSPROP_ENUMERATE
)) {
2644 args
.rval().setObject(*result
);
2648 static bool DeterministicGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2649 CallArgs args
= CallArgsFromVp(argc
, vp
);
2651 if (args
.length() != 1) {
2652 RootedObject
callee(cx
, &args
.callee());
2653 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2657 cx
->runtime()->gc
.setDeterministic(ToBoolean(args
[0]));
2658 args
.rval().setUndefined();
2662 static bool DumpGCArenaInfo(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2663 CallArgs args
= CallArgsFromVp(argc
, vp
);
2664 js::gc::DumpArenaInfo();
2665 args
.rval().setUndefined();
2669 static bool SetMarkStackLimit(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2670 CallArgs args
= CallArgsFromVp(argc
, vp
);
2671 if (args
.length() != 1) {
2672 RootedObject
callee(cx
, &args
.callee());
2673 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2678 if (!ToInt32(cx
, args
[0], &value
) || value
<= 0) {
2679 JS_ReportErrorASCII(cx
, "Bad argument to SetMarkStackLimit");
2683 if (JS::IsIncrementalGCInProgress(cx
)) {
2684 JS_ReportErrorASCII(
2685 cx
, "Attempt to set markStackLimit while a GC is in progress");
2689 JSRuntime
* runtime
= cx
->runtime();
2690 AutoLockGC
lock(runtime
);
2691 runtime
->gc
.setMarkStackLimit(value
, lock
);
2692 args
.rval().setUndefined();
2696 #endif /* JS_GC_ZEAL */
2698 static bool SetMallocMaxDirtyPageModifier(JSContext
* cx
, unsigned argc
,
2700 CallArgs args
= CallArgsFromVp(argc
, vp
);
2701 if (args
.length() != 1) {
2702 RootedObject
callee(cx
, &args
.callee());
2703 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2707 constexpr int32_t MinSupportedValue
= -5;
2708 constexpr int32_t MaxSupportedValue
= 16;
2711 if (!ToInt32(cx
, args
[0], &value
)) {
2714 if (value
< MinSupportedValue
|| value
> MaxSupportedValue
) {
2715 JS_ReportErrorASCII(cx
, "Bad argument to setMallocMaxDirtyPageModifier");
2719 moz_set_max_dirty_page_modifier(value
);
2721 args
.rval().setUndefined();
2725 static bool GCState(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2726 CallArgs args
= CallArgsFromVp(argc
, vp
);
2728 if (args
.length() > 1) {
2729 RootedObject
callee(cx
, &args
.callee());
2730 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
2736 if (args
.length() == 1) {
2737 if (!args
[0].isObject()) {
2738 RootedObject
callee(cx
, &args
.callee());
2739 ReportUsageErrorASCII(cx
, callee
, "Expected object");
2743 JSObject
* obj
= UncheckedUnwrap(&args
[0].toObject());
2744 state
= gc::StateName(obj
->zone()->gcState());
2746 state
= gc::StateName(cx
->runtime()->gc
.state());
2749 return ReturnStringCopy(cx
, args
, state
);
2752 static bool ScheduleZoneForGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2753 CallArgs args
= CallArgsFromVp(argc
, vp
);
2755 if (args
.length() != 1) {
2756 RootedObject
callee(cx
, &args
.callee());
2757 ReportUsageErrorASCII(cx
, callee
, "Expecting a single argument");
2761 if (args
[0].isObject()) {
2762 // Ensure that |zone| is collected during the next GC.
2763 Zone
* zone
= UncheckedUnwrap(&args
[0].toObject())->zone();
2764 PrepareZoneForGC(cx
, zone
);
2765 } else if (args
[0].isString()) {
2766 // This allows us to schedule the atoms zone for GC.
2767 Zone
* zone
= args
[0].toString()->zoneFromAnyThread();
2768 if (!CurrentThreadCanAccessZone(zone
)) {
2769 RootedObject
callee(cx
, &args
.callee());
2770 ReportUsageErrorASCII(cx
, callee
, "Specified zone not accessible for GC");
2773 PrepareZoneForGC(cx
, zone
);
2775 RootedObject
callee(cx
, &args
.callee());
2776 ReportUsageErrorASCII(cx
, callee
,
2777 "Bad argument - expecting object or string");
2781 args
.rval().setUndefined();
2785 static bool StartGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2786 CallArgs args
= CallArgsFromVp(argc
, vp
);
2788 if (args
.length() > 2) {
2789 RootedObject
callee(cx
, &args
.callee());
2790 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2794 auto budget
= SliceBudget::unlimited();
2795 if (args
.length() >= 1) {
2797 if (!ToUint32(cx
, args
[0], &work
)) {
2800 budget
= SliceBudget(WorkBudget(work
));
2803 bool shrinking
= false;
2804 if (args
.length() >= 2) {
2805 Value arg
= args
[1];
2806 if (arg
.isString()) {
2807 if (!JS_StringEqualsLiteral(cx
, arg
.toString(), "shrinking",
2814 JSRuntime
* rt
= cx
->runtime();
2815 if (rt
->gc
.isIncrementalGCInProgress()) {
2816 RootedObject
callee(cx
, &args
.callee());
2817 JS_ReportErrorASCII(cx
, "Incremental GC already in progress");
2821 JS::GCOptions options
=
2822 shrinking
? JS::GCOptions::Shrink
: JS::GCOptions::Normal
;
2823 rt
->gc
.startDebugGC(options
, budget
);
2825 args
.rval().setUndefined();
2829 static bool FinishGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2830 CallArgs args
= CallArgsFromVp(argc
, vp
);
2832 if (args
.length() > 0) {
2833 RootedObject
callee(cx
, &args
.callee());
2834 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2838 JSRuntime
* rt
= cx
->runtime();
2839 if (rt
->gc
.isIncrementalGCInProgress()) {
2840 rt
->gc
.finishGC(JS::GCReason::DEBUG_GC
);
2843 args
.rval().setUndefined();
2847 static bool GCSlice(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2848 CallArgs args
= CallArgsFromVp(argc
, vp
);
2850 if (args
.length() > 2) {
2851 RootedObject
callee(cx
, &args
.callee());
2852 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2856 auto budget
= SliceBudget::unlimited();
2857 if (args
.length() >= 1) {
2859 if (!ToUint32(cx
, args
[0], &work
)) {
2862 budget
= SliceBudget(WorkBudget(work
));
2865 bool dontStart
= false;
2866 if (args
.get(1).isObject()) {
2867 RootedObject
options(cx
, &args
[1].toObject());
2869 if (!JS_GetProperty(cx
, options
, "dontStart", &v
)) {
2872 dontStart
= ToBoolean(v
);
2875 JSRuntime
* rt
= cx
->runtime();
2876 if (rt
->gc
.isIncrementalGCInProgress()) {
2877 rt
->gc
.debugGCSlice(budget
);
2878 } else if (!dontStart
) {
2879 rt
->gc
.startDebugGC(JS::GCOptions::Normal
, budget
);
2882 args
.rval().setUndefined();
2886 static bool AbortGC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2887 CallArgs args
= CallArgsFromVp(argc
, vp
);
2889 if (args
.length() != 0) {
2890 RootedObject
callee(cx
, &args
.callee());
2891 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2895 JS::AbortIncrementalGC(cx
);
2896 args
.rval().setUndefined();
2900 static bool FullCompartmentChecks(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2901 CallArgs args
= CallArgsFromVp(argc
, vp
);
2903 if (args
.length() != 1) {
2904 RootedObject
callee(cx
, &args
.callee());
2905 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2909 cx
->runtime()->gc
.setFullCompartmentChecks(ToBoolean(args
[0]));
2910 args
.rval().setUndefined();
2914 static bool NondeterministicGetWeakMapKeys(JSContext
* cx
, unsigned argc
,
2916 CallArgs args
= CallArgsFromVp(argc
, vp
);
2918 if (args
.length() != 1) {
2919 RootedObject
callee(cx
, &args
.callee());
2920 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
2923 if (!args
[0].isObject()) {
2924 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2925 JSMSG_NOT_EXPECTED_TYPE
,
2926 "nondeterministicGetWeakMapKeys", "WeakMap",
2927 InformalValueTypeName(args
[0]));
2930 RootedObject
arr(cx
);
2931 RootedObject
mapObj(cx
, &args
[0].toObject());
2932 if (!JS_NondeterministicGetWeakMapKeys(cx
, mapObj
, &arr
)) {
2936 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
2937 JSMSG_NOT_EXPECTED_TYPE
,
2938 "nondeterministicGetWeakMapKeys", "WeakMap",
2939 args
[0].toObject().getClass()->name
);
2942 args
.rval().setObject(*arr
);
2946 class HasChildTracer final
: public JS::CallbackTracer
{
2950 void onChild(JS::GCCellPtr thing
, const char* name
) override
{
2951 if (thing
.asCell() == child_
.toGCThing()) {
2957 HasChildTracer(JSContext
* cx
, HandleValue child
)
2958 : JS::CallbackTracer(cx
, JS::TracerKind::Callback
,
2959 JS::WeakMapTraceAction::TraceKeysAndValues
),
2963 bool found() const { return found_
; }
2966 static bool HasChild(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2967 CallArgs args
= CallArgsFromVp(argc
, vp
);
2968 RootedValue
parent(cx
, args
.get(0));
2969 RootedValue
child(cx
, args
.get(1));
2971 if (!parent
.isGCThing() || !child
.isGCThing()) {
2972 args
.rval().setBoolean(false);
2976 HasChildTracer
trc(cx
, child
);
2977 TraceChildren(&trc
, JS::GCCellPtr(parent
.toGCThing(), parent
.traceKind()));
2978 args
.rval().setBoolean(trc
.found());
2982 static bool SetSavedStacksRNGState(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2983 CallArgs args
= CallArgsFromVp(argc
, vp
);
2984 if (!args
.requireAtLeast(cx
, "setSavedStacksRNGState", 1)) {
2989 if (!ToInt32(cx
, args
[0], &seed
)) {
2993 // Either one or the other of the seed arguments must be non-zero;
2994 // make this true no matter what value 'seed' has.
2995 cx
->realm()->savedStacks().setRNGState(seed
, (seed
+ 1) * 33);
2999 static bool GetSavedFrameCount(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3000 CallArgs args
= CallArgsFromVp(argc
, vp
);
3001 args
.rval().setNumber(cx
->realm()->savedStacks().count());
3005 static bool ClearSavedFrames(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3006 CallArgs args
= CallArgsFromVp(argc
, vp
);
3008 js::SavedStacks
& savedStacks
= cx
->realm()->savedStacks();
3009 savedStacks
.clear();
3011 for (ActivationIterator
iter(cx
); !iter
.done(); ++iter
) {
3012 iter
->clearLiveSavedFrameCache();
3015 args
.rval().setUndefined();
3019 static bool SaveStack(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3020 CallArgs args
= CallArgsFromVp(argc
, vp
);
3022 JS::StackCapture
capture((JS::AllFrames()));
3023 if (args
.length() >= 1) {
3025 if (!ToNumber(cx
, args
[0], &maxDouble
)) {
3028 if (std::isnan(maxDouble
) || maxDouble
< 0 || maxDouble
> UINT32_MAX
) {
3029 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[0],
3030 nullptr, "not a valid maximum frame count");
3033 uint32_t max
= uint32_t(maxDouble
);
3035 capture
= JS::StackCapture(JS::MaxFrames(max
));
3039 RootedObject
compartmentObject(cx
);
3040 if (args
.length() >= 2) {
3041 if (!args
[1].isObject()) {
3042 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[0],
3043 nullptr, "not an object");
3046 compartmentObject
= UncheckedUnwrap(&args
[1].toObject());
3047 if (!compartmentObject
) {
3052 RootedObject
stack(cx
);
3054 Maybe
<AutoRealm
> ar
;
3055 if (compartmentObject
) {
3056 ar
.emplace(cx
, compartmentObject
);
3058 if (!JS::CaptureCurrentStack(cx
, &stack
, std::move(capture
))) {
3063 if (stack
&& !cx
->compartment()->wrap(cx
, &stack
)) {
3067 args
.rval().setObjectOrNull(stack
);
3071 static bool CaptureFirstSubsumedFrame(JSContext
* cx
, unsigned argc
,
3073 CallArgs args
= CallArgsFromVp(argc
, vp
);
3074 if (!args
.requireAtLeast(cx
, "captureFirstSubsumedFrame", 1)) {
3078 if (!args
[0].isObject()) {
3079 JS_ReportErrorASCII(cx
, "The argument must be an object");
3083 RootedObject
obj(cx
, &args
[0].toObject());
3084 obj
= CheckedUnwrapStatic(obj
);
3086 JS_ReportErrorASCII(cx
, "Denied permission to object.");
3090 JS::StackCapture
capture(
3091 JS::FirstSubsumedFrame(cx
, obj
->nonCCWRealm()->principals()));
3092 if (args
.length() > 1) {
3093 capture
.as
<JS::FirstSubsumedFrame
>().ignoreSelfHosted
=
3094 JS::ToBoolean(args
[1]);
3097 JS::RootedObject
capturedStack(cx
);
3098 if (!JS::CaptureCurrentStack(cx
, &capturedStack
, std::move(capture
))) {
3102 args
.rval().setObjectOrNull(capturedStack
);
3106 static bool CallFunctionFromNativeFrame(JSContext
* cx
, unsigned argc
,
3108 CallArgs args
= CallArgsFromVp(argc
, vp
);
3110 if (args
.length() != 1) {
3111 JS_ReportErrorASCII(cx
, "The function takes exactly one argument.");
3114 if (!args
[0].isObject() || !IsCallable(args
[0])) {
3115 JS_ReportErrorASCII(cx
, "The first argument should be a function.");
3119 RootedObject
function(cx
, &args
[0].toObject());
3120 return Call(cx
, UndefinedHandleValue
, function
, JS::HandleValueArray::empty(),
3124 static bool CallFunctionWithAsyncStack(JSContext
* cx
, unsigned argc
,
3126 CallArgs args
= CallArgsFromVp(argc
, vp
);
3128 if (args
.length() != 3) {
3129 JS_ReportErrorASCII(cx
, "The function takes exactly three arguments.");
3132 if (!args
[0].isObject() || !IsCallable(args
[0])) {
3133 JS_ReportErrorASCII(cx
, "The first argument should be a function.");
3136 if (!args
[1].isObject() || !args
[1].toObject().is
<SavedFrame
>()) {
3137 JS_ReportErrorASCII(cx
, "The second argument should be a SavedFrame.");
3140 if (!args
[2].isString() || args
[2].toString()->empty()) {
3141 JS_ReportErrorASCII(cx
, "The third argument should be a non-empty string.");
3145 RootedObject
function(cx
, &args
[0].toObject());
3146 RootedObject
stack(cx
, &args
[1].toObject());
3147 RootedString
asyncCause(cx
, args
[2].toString());
3148 UniqueChars utf8Cause
= JS_EncodeStringToUTF8(cx
, asyncCause
);
3150 MOZ_ASSERT(cx
->isExceptionPending());
3154 JS::AutoSetAsyncStackForNewCalls
sas(
3155 cx
, stack
, utf8Cause
.get(),
3156 JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT
);
3157 return Call(cx
, UndefinedHandleValue
, function
, JS::HandleValueArray::empty(),
3161 static bool EnableTrackAllocations(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3162 SetAllocationMetadataBuilder(cx
, &SavedStacks::metadataBuilder
);
3166 static bool DisableTrackAllocations(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3167 SetAllocationMetadataBuilder(cx
, nullptr);
3171 static bool SetTestFilenameValidationCallback(JSContext
* cx
, unsigned argc
,
3173 CallArgs args
= CallArgsFromVp(argc
, vp
);
3175 // Accept all filenames that start with "safe". In system code also accept
3176 // filenames starting with "system".
3177 auto testCb
= [](JSContext
* cx
, const char* filename
) -> bool {
3178 if (strstr(filename
, "safe") == filename
) {
3181 if (cx
->realm()->isSystem() && strstr(filename
, "system") == filename
) {
3187 JS::SetFilenameValidationCallback(testCb
);
3189 args
.rval().setUndefined();
3193 static JSAtom
* GetPropertiesAddedName(JSContext
* cx
) {
3194 const char* propName
= "_propertiesAdded";
3195 return Atomize(cx
, propName
, strlen(propName
));
3198 static bool NewObjectWithAddPropertyHook(JSContext
* cx
, unsigned argc
,
3200 CallArgs args
= CallArgsFromVp(argc
, vp
);
3202 auto addPropHook
= [](JSContext
* cx
, HandleObject obj
, HandleId id
,
3203 HandleValue v
) -> bool {
3204 Rooted
<JSAtom
*> propName(cx
, GetPropertiesAddedName(cx
));
3208 // Don't do anything if we're adding the _propertiesAdded property.
3209 RootedId
propId(cx
, AtomToId(propName
));
3213 // Increment _propertiesAdded.
3214 RootedValue
val(cx
);
3215 if (!JS_GetPropertyById(cx
, obj
, propId
, &val
)) {
3218 if (!val
.isInt32() || val
.toInt32() == INT32_MAX
) {
3221 val
.setInt32(val
.toInt32() + 1);
3222 return JS_DefinePropertyById(cx
, obj
, propId
, val
, 0);
3225 static const JSClassOps classOps
= {
3226 addPropHook
, // addProperty
3227 nullptr, // delProperty
3228 nullptr, // enumerate
3229 nullptr, // newEnumerate
3231 nullptr, // mayResolve
3232 nullptr, // finalize
3234 nullptr, // construct
3237 static const JSClass cls
= {
3238 "ObjectWithAddPropHook",
3243 RootedObject
obj(cx
, JS_NewObject(cx
, &cls
));
3248 // Initialize _propertiesAdded to 0.
3249 Rooted
<JSAtom
*> propName(cx
, GetPropertiesAddedName(cx
));
3253 RootedId
propId(cx
, AtomToId(propName
));
3254 RootedValue
val(cx
, Int32Value(0));
3255 if (!JS_DefinePropertyById(cx
, obj
, propId
, val
, 0)) {
3259 args
.rval().setObject(*obj
);
3263 static bool NewObjectWithCallHook(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3264 CallArgs args
= CallArgsFromVp(argc
, vp
);
3266 static auto hookShared
= [](JSContext
* cx
, CallArgs
& args
) {
3267 Rooted
<PlainObject
*> obj(cx
, NewPlainObject(cx
));
3272 // Define |this|. We can't expose the MagicValue to JS, so we use
3273 // "<is_constructing>" in that case.
3274 Rooted
<Value
> thisv(cx
, args
.thisv());
3275 if (thisv
.isMagic(JS_IS_CONSTRUCTING
)) {
3276 JSString
* str
= NewStringCopyZ
<CanGC
>(cx
, "<is_constructing>");
3280 thisv
.setString(str
);
3282 if (!DefineDataProperty(cx
, obj
, cx
->names().this_
, thisv
,
3283 JSPROP_ENUMERATE
)) {
3288 if (!DefineDataProperty(cx
, obj
, cx
->names().callee
, args
.calleev(),
3289 JSPROP_ENUMERATE
)) {
3293 // Define |arguments| array.
3294 Rooted
<ArrayObject
*> arr(
3295 cx
, NewDenseCopiedArray(cx
, args
.length(), args
.array()));
3299 Rooted
<Value
> arrVal(cx
, ObjectValue(*arr
));
3300 if (!DefineDataProperty(cx
, obj
, cx
->names().arguments
, arrVal
,
3301 JSPROP_ENUMERATE
)) {
3305 // Define |newTarget| if constructing.
3306 if (args
.isConstructing()) {
3307 const char* propName
= "newTarget";
3308 Rooted
<JSAtom
*> name(cx
, Atomize(cx
, propName
, strlen(propName
)));
3312 Rooted
<PropertyKey
> key(cx
, NameToId(name
->asPropertyName()));
3313 if (!DefineDataProperty(cx
, obj
, key
, args
.newTarget(),
3314 JSPROP_ENUMERATE
)) {
3319 args
.rval().setObject(*obj
);
3323 static auto callHook
= [](JSContext
* cx
, unsigned argc
, Value
* vp
) {
3324 CallArgs args
= CallArgsFromVp(argc
, vp
);
3325 MOZ_ASSERT(!args
.isConstructing());
3326 return hookShared(cx
, args
);
3328 static auto constructHook
= [](JSContext
* cx
, unsigned argc
, Value
* vp
) {
3329 CallArgs args
= CallArgsFromVp(argc
, vp
);
3330 MOZ_ASSERT(args
.isConstructing());
3331 return hookShared(cx
, args
);
3334 static const JSClassOps classOps
= {
3335 nullptr, // addProperty
3336 nullptr, // delProperty
3337 nullptr, // enumerate
3338 nullptr, // newEnumerate
3340 nullptr, // mayResolve
3341 nullptr, // finalize
3343 constructHook
, // construct
3346 static const JSClass cls
= {
3347 "ObjectWithCallHook",
3352 Rooted
<JSObject
*> obj(cx
, JS_NewObject(cx
, &cls
));
3357 args
.rval().setObject(*obj
);
3361 static constexpr JSClass ObjectWithManyReservedSlotsClass
= {
3362 "ObjectWithManyReservedSlots", JSCLASS_HAS_RESERVED_SLOTS(40)};
3364 static bool NewObjectWithManyReservedSlots(JSContext
* cx
, unsigned argc
,
3366 CallArgs args
= CallArgsFromVp(argc
, vp
);
3368 static constexpr size_t NumReservedSlots
=
3369 JSCLASS_RESERVED_SLOTS(&ObjectWithManyReservedSlotsClass
);
3370 static_assert(NumReservedSlots
> NativeObject::MAX_FIXED_SLOTS
);
3372 RootedObject
obj(cx
, JS_NewObject(cx
, &ObjectWithManyReservedSlotsClass
));
3377 for (size_t i
= 0; i
< NumReservedSlots
; i
++) {
3378 JS_SetReservedSlot(obj
, i
, Int32Value(i
));
3381 args
.rval().setObject(*obj
);
3385 static bool CheckObjectWithManyReservedSlots(JSContext
* cx
, unsigned argc
,
3387 CallArgs args
= CallArgsFromVp(argc
, vp
);
3389 if (args
.length() != 1 || !args
[0].isObject() ||
3390 args
[0].toObject().getClass() != &ObjectWithManyReservedSlotsClass
) {
3391 JS_ReportErrorASCII(cx
,
3392 "Expected object from newObjectWithManyReservedSlots");
3396 JSObject
* obj
= &args
[0].toObject();
3398 static constexpr size_t NumReservedSlots
=
3399 JSCLASS_RESERVED_SLOTS(&ObjectWithManyReservedSlotsClass
);
3401 for (size_t i
= 0; i
< NumReservedSlots
; i
++) {
3402 MOZ_RELEASE_ASSERT(JS::GetReservedSlot(obj
, i
).toInt32() == int32_t(i
));
3405 args
.rval().setUndefined();
3409 static bool GetWatchtowerLog(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3410 CallArgs args
= CallArgsFromVp(argc
, vp
);
3412 Rooted
<GCVector
<Value
>> values(cx
, GCVector
<Value
>(cx
));
3414 if (auto* log
= cx
->runtime()->watchtowerTestingLog
.ref().get()) {
3415 Rooted
<JSObject
*> elem(cx
);
3416 for (PlainObject
* obj
: *log
) {
3418 if (!cx
->compartment()->wrap(cx
, &elem
)) {
3421 if (!values
.append(ObjectValue(*elem
))) {
3425 log
->clearAndFree();
3428 ArrayObject
* arr
= NewDenseCopiedArray(cx
, values
.length(), values
.begin());
3433 args
.rval().setObject(*arr
);
3437 static bool AddWatchtowerTarget(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3438 CallArgs args
= CallArgsFromVp(argc
, vp
);
3440 if (args
.length() != 1 || !args
[0].isObject()) {
3441 JS_ReportErrorASCII(cx
, "Expected a single object argument.");
3445 if (!cx
->runtime()->watchtowerTestingLog
.ref()) {
3446 auto vec
= cx
->make_unique
<JSRuntime::RootedPlainObjVec
>(cx
);
3450 cx
->runtime()->watchtowerTestingLog
= std::move(vec
);
3453 RootedObject
obj(cx
, &args
[0].toObject());
3454 if (!JSObject::setUseWatchtowerTestingLog(cx
, obj
)) {
3458 args
.rval().setUndefined();
3462 struct TestExternalString
: public JSExternalStringCallbacks
{
3463 void finalize(JS::Latin1Char
* chars
) const override
{ js_free(chars
); }
3464 void finalize(char16_t
* chars
) const override
{ js_free(chars
); }
3465 size_t sizeOfBuffer(const JS::Latin1Char
* chars
,
3466 mozilla::MallocSizeOf mallocSizeOf
) const override
{
3467 return mallocSizeOf(chars
);
3469 size_t sizeOfBuffer(const char16_t
* chars
,
3470 mozilla::MallocSizeOf mallocSizeOf
) const override
{
3471 return mallocSizeOf(chars
);
3475 static constexpr TestExternalString TestExternalStringCallbacks
;
3477 static bool NewString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3478 CallArgs args
= CallArgsFromVp(argc
, vp
);
3480 RootedString
src(cx
, ToString(cx
, args
.get(0)));
3485 gc::Heap heap
= gc::Heap::Default
;
3486 bool wantTwoByte
= false;
3487 bool forceExternal
= false;
3488 bool maybeExternal
= false;
3489 uint32_t capacity
= 0;
3491 if (args
.get(1).isObject()) {
3492 RootedObject
options(cx
, &args
[1].toObject());
3494 bool requestTenured
= false;
3495 struct BoolSetting
{
3499 for (auto [name
, setting
] :
3500 {BoolSetting
{"tenured", &requestTenured
},
3501 BoolSetting
{"twoByte", &wantTwoByte
},
3502 BoolSetting
{"external", &forceExternal
},
3503 BoolSetting
{"maybeExternal", &maybeExternal
}}) {
3504 if (!JS_GetProperty(cx
, options
, name
, &v
)) {
3507 *setting
= ToBoolean(v
); // false if not given (or otherwise undefined)
3509 struct Uint32Setting
{
3513 for (auto [name
, setting
] : {Uint32Setting
{"capacity", &capacity
}}) {
3514 if (!JS_GetProperty(cx
, options
, name
, &v
)) {
3518 if (!ToInt32(cx
, v
, &i32
)) {
3522 JS_ReportErrorASCII(cx
, "nonnegative value required");
3525 *setting
= static_cast<uint32_t>(i32
);
3528 heap
= requestTenured
? gc::Heap::Tenured
: gc::Heap::Default
;
3529 if (forceExternal
|| maybeExternal
) {
3531 if (capacity
!= 0) {
3532 JS_ReportErrorASCII(cx
,
3533 "strings cannot be both external and extensible");
3539 auto len
= src
->length();
3540 RootedString
dest(cx
);
3542 if (forceExternal
|| maybeExternal
) {
3543 auto buf
= cx
->make_pod_array
<char16_t
>(len
);
3548 if (!JS_CopyStringChars(cx
, mozilla::Range
<char16_t
>(buf
.get(), len
),
3553 bool isExternal
= true;
3554 if (forceExternal
) {
3555 dest
= JSExternalString::new_(cx
, buf
.get(), len
,
3556 &TestExternalStringCallbacks
);
3558 dest
= NewMaybeExternalString(
3559 cx
, buf
.get(), len
, &TestExternalStringCallbacks
, &isExternal
, heap
);
3561 if (dest
&& isExternal
) {
3562 (void)buf
.release(); // Ownership was transferred.
3565 AutoStableStringChars
stable(cx
);
3566 if (!wantTwoByte
&& src
->hasLatin1Chars()) {
3567 if (!stable
.init(cx
, src
)) {
3571 if (!stable
.initTwoByte(cx
, src
)) {
3576 if (capacity
< len
) {
3580 JS_ReportErrorASCII(cx
, "Cannot set capacity of empty string");
3584 auto createLinearString
= [&](const auto* chars
) -> JSLinearString
* {
3586 std::remove_const_t
<std::remove_pointer_t
<decltype(chars
)>>;
3588 if (JSInlineString::lengthFits
<CharT
>(len
)) {
3589 JS_ReportErrorASCII(cx
, "Cannot create small non-inline strings");
3594 cx
->make_pod_arena_array
<CharT
>(js::StringBufferArena
, capacity
);
3598 mozilla::PodCopy(news
.get(), chars
, len
);
3599 Rooted
<JSString::OwnedChars
<CharT
>> owned(cx
, std::move(news
), len
,
3601 return JSLinearString::newValidLength
<CanGC
, CharT
>(cx
, &owned
, heap
);
3604 if (stable
.isLatin1()) {
3605 dest
= createLinearString(stable
.latin1Chars());
3607 dest
= createLinearString(stable
.twoByteChars());
3610 dest
->asLinear().makeExtensible(capacity
);
3612 } else if (wantTwoByte
) {
3613 dest
= NewStringCopyNDontDeflate
<CanGC
>(cx
, stable
.twoByteChars(), len
,
3615 } else if (stable
.isLatin1()) {
3616 dest
= NewStringCopyN
<CanGC
>(cx
, stable
.latin1Chars(), len
, heap
);
3618 // Normal behavior: auto-deflate to latin1 if possible.
3619 dest
= NewStringCopyN
<CanGC
>(cx
, stable
.twoByteChars(), len
, heap
);
3627 args
.rval().setString(dest
);
3631 // Warning! This will let you create ropes that I'm not sure would be possible
3632 // otherwise, specifically:
3634 // - a rope with a zero-length child
3635 // - a rope that would fit into an inline string
3637 static bool NewRope(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3638 CallArgs args
= CallArgsFromVp(argc
, vp
);
3640 if (!args
.get(0).isString() || !args
.get(1).isString()) {
3641 JS_ReportErrorASCII(cx
, "newRope requires two string arguments.");
3645 gc::Heap heap
= js::gc::Heap::Default
;
3646 if (args
.get(2).isObject()) {
3647 RootedObject
options(cx
, &args
[2].toObject());
3649 if (!JS_GetProperty(cx
, options
, "nursery", &v
)) {
3652 if (!v
.isUndefined() && !ToBoolean(v
)) {
3653 heap
= js::gc::Heap::Tenured
;
3657 RootedString
left(cx
, args
[0].toString());
3658 RootedString
right(cx
, args
[1].toString());
3659 size_t length
= JS_GetStringLength(left
) + JS_GetStringLength(right
);
3660 if (length
> JSString::MAX_LENGTH
) {
3661 JS_ReportErrorASCII(cx
, "rope length exceeds maximum string length");
3665 // Disallow creating ropes where one side is empty.
3666 if (left
->empty() || right
->empty()) {
3667 JS_ReportErrorASCII(cx
, "rope child mustn't be the empty string");
3671 // Disallow creating ropes which fit into inline strings.
3672 if (left
->hasLatin1Chars() && right
->hasLatin1Chars()) {
3673 if (JSInlineString::lengthFits
<JS::Latin1Char
>(length
)) {
3674 JS_ReportErrorASCII(cx
, "Cannot create small non-inline ropes");
3678 if (JSInlineString::lengthFits
<char16_t
>(length
)) {
3679 JS_ReportErrorASCII(cx
, "Cannot create small non-inline ropes");
3684 auto* str
= JSRope::new_
<CanGC
>(cx
, left
, right
, length
, heap
);
3689 args
.rval().setString(str
);
3693 static bool IsRope(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3694 CallArgs args
= CallArgsFromVp(argc
, vp
);
3696 if (!args
.get(0).isString()) {
3697 JS_ReportErrorASCII(cx
, "isRope requires a string argument.");
3701 JSString
* str
= args
[0].toString();
3702 args
.rval().setBoolean(str
->isRope());
3706 static bool EnsureLinearString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3707 CallArgs args
= CallArgsFromVp(argc
, vp
);
3709 if (args
.length() != 1 || !args
[0].isString()) {
3710 JS_ReportErrorASCII(
3711 cx
, "ensureLinearString takes exactly one string argument.");
3715 JSLinearString
* linear
= args
[0].toString()->ensureLinear(cx
);
3720 args
.rval().setString(linear
);
3724 static bool RepresentativeStringArray(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3725 CallArgs args
= CallArgsFromVp(argc
, vp
);
3727 RootedObject
array(cx
, JS::NewArrayObject(cx
, 0));
3732 if (!JSString::fillWithRepresentatives(cx
, array
.as
<ArrayObject
>())) {
3736 args
.rval().setObject(*array
);
3740 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
3742 static bool OOMThreadTypes(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3743 CallArgs args
= CallArgsFromVp(argc
, vp
);
3744 args
.rval().setInt32(js::THREAD_TYPE_MAX
);
3748 static bool CheckCanSimulateOOM(JSContext
* cx
) {
3749 if (js::oom::GetThreadType() != js::THREAD_TYPE_MAIN
) {
3750 JS_ReportErrorASCII(
3751 cx
, "Simulated OOM failure is only supported on the main thread");
3758 static bool SetupOOMFailure(JSContext
* cx
, bool failAlways
, unsigned argc
,
3760 CallArgs args
= CallArgsFromVp(argc
, vp
);
3762 if (disableOOMFunctions
) {
3763 args
.rval().setUndefined();
3767 if (args
.length() < 1) {
3768 JS_ReportErrorASCII(cx
, "Count argument required");
3772 if (args
.length() > 2) {
3773 JS_ReportErrorASCII(cx
, "Too many arguments");
3778 if (!JS::ToInt32(cx
, args
.get(0), &count
)) {
3783 JS_ReportErrorASCII(cx
, "OOM cutoff should be positive");
3787 uint32_t targetThread
= js::THREAD_TYPE_MAIN
;
3788 if (args
.length() > 1 && !ToUint32(cx
, args
[1], &targetThread
)) {
3792 if (targetThread
== js::THREAD_TYPE_NONE
||
3793 targetThread
== js::THREAD_TYPE_WORKER
||
3794 targetThread
>= js::THREAD_TYPE_MAX
) {
3795 JS_ReportErrorASCII(cx
, "Invalid thread type specified");
3799 if (!CheckCanSimulateOOM(cx
)) {
3803 js::oom::simulator
.simulateFailureAfter(js::oom::FailureSimulator::Kind::OOM
,
3804 count
, targetThread
, failAlways
);
3805 args
.rval().setUndefined();
3809 static bool OOMAfterAllocations(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3810 return SetupOOMFailure(cx
, true, argc
, vp
);
3813 static bool OOMAtAllocation(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3814 return SetupOOMFailure(cx
, false, argc
, vp
);
3817 static bool ResetOOMFailure(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3818 CallArgs args
= CallArgsFromVp(argc
, vp
);
3820 if (!CheckCanSimulateOOM(cx
)) {
3824 args
.rval().setBoolean(js::oom::HadSimulatedOOM());
3825 js::oom::simulator
.reset();
3829 static size_t CountCompartments(JSContext
* cx
) {
3831 for (auto zone
: cx
->runtime()->gc
.zones()) {
3832 count
+= zone
->compartments().length();
3837 // Iterative failure testing: test a function by simulating failures at indexed
3838 // locations throughout the normal execution path and checking that the
3839 // resulting state of the environment is consistent with the error result.
3841 // For example, trigger OOM at every allocation point and test that the function
3842 // either recovers and succeeds or raises an exception and fails.
3844 class MOZ_STACK_CLASS IterativeFailureTest
{
3846 struct FailureSimulator
{
3847 virtual void setup(JSContext
* cx
) {}
3848 virtual void teardown(JSContext
* cx
) {}
3849 virtual void startSimulating(JSContext
* cx
, unsigned iteration
,
3850 unsigned thread
, bool keepFailing
) = 0;
3851 virtual bool stopSimulating() = 0;
3852 virtual void cleanup(JSContext
* cx
) {}
3855 IterativeFailureTest(JSContext
* cx
, FailureSimulator
& simulator
);
3856 bool initParams(const CallArgs
& args
);
3861 bool testThread(unsigned thread
);
3862 bool testIteration(unsigned thread
, unsigned iteration
,
3863 bool& failureWasSimulated
, MutableHandleValue exception
);
3867 JSContext
* const cx
;
3868 FailureSimulator
& simulator
;
3869 size_t compartmentCount
;
3871 // Test parameters set by initParams.
3872 RootedFunction testFunction
;
3873 unsigned threadStart
= 0;
3874 unsigned threadEnd
= 0;
3875 bool expectExceptionOnFailure
= true;
3876 bool keepFailing
= false;
3877 bool verbose
= false;
3880 bool RunIterativeFailureTest(
3881 JSContext
* cx
, const CallArgs
& args
,
3882 IterativeFailureTest::FailureSimulator
& simulator
) {
3883 IterativeFailureTest
test(cx
, simulator
);
3884 return test
.initParams(args
) && test
.test();
3887 IterativeFailureTest::IterativeFailureTest(JSContext
* cx
,
3888 FailureSimulator
& simulator
)
3889 : cx(cx
), simulator(simulator
), testFunction(cx
) {}
3891 bool IterativeFailureTest::test() {
3892 if (disableOOMFunctions
) {
3900 auto onExit
= mozilla::MakeScopeExit([this] { teardown(); });
3902 for (unsigned thread
= threadStart
; thread
<= threadEnd
; thread
++) {
3903 if (!testThread(thread
)) {
3911 bool IterativeFailureTest::setup() {
3912 if (!CheckCanSimulateOOM(cx
)) {
3916 // Disallow nested tests.
3917 if (cx
->runningOOMTest
) {
3918 JS_ReportErrorASCII(
3919 cx
, "Nested call to iterative failure test is not allowed.");
3922 cx
->runningOOMTest
= true;
3924 MOZ_ASSERT(!cx
->isExceptionPending());
3927 JS_SetGCZeal(cx
, 0, JS_DEFAULT_ZEAL_FREQ
);
3930 // Delazify the function here if necessary so we don't end up testing that.
3931 if (testFunction
->isInterpreted() &&
3932 !JSFunction::getOrCreateScript(cx
, testFunction
)) {
3936 compartmentCount
= CountCompartments(cx
);
3938 simulator
.setup(cx
);
3943 bool IterativeFailureTest::testThread(unsigned thread
) {
3945 fprintf(stderr
, "thread %u\n", thread
);
3948 RootedValue
exception(cx
);
3950 unsigned iteration
= 1;
3951 bool failureWasSimulated
;
3953 if (!testIteration(thread
, iteration
, failureWasSimulated
, &exception
)) {
3958 } while (failureWasSimulated
);
3961 fprintf(stderr
, " finished after %u iterations\n", iteration
- 1);
3962 if (!exception
.isUndefined()) {
3963 RootedString
str(cx
, JS::ToString(cx
, exception
));
3965 fprintf(stderr
, " error while trying to print exception, giving up\n");
3968 UniqueChars
bytes(JS_EncodeStringToUTF8(cx
, str
));
3972 fprintf(stderr
, " threw %s\n", bytes
.get());
3979 bool IterativeFailureTest::testIteration(unsigned thread
, unsigned iteration
,
3980 bool& failureWasSimulated
,
3981 MutableHandleValue exception
) {
3983 fprintf(stderr
, " iteration %u\n", iteration
);
3986 MOZ_RELEASE_ASSERT(!cx
->isExceptionPending());
3988 simulator
.startSimulating(cx
, iteration
, thread
, keepFailing
);
3990 RootedValue
result(cx
);
3991 bool ok
= JS_CallFunction(cx
, cx
->global(), testFunction
,
3992 HandleValueArray::empty(), &result
);
3994 failureWasSimulated
= simulator
.stopSimulating();
3996 if (ok
&& cx
->isExceptionPending()) {
3998 "Thunk execution succeeded but an exception was raised - missing error "
4002 if (!ok
&& !cx
->isExceptionPending() && expectExceptionOnFailure
) {
4004 "Thunk execution failed but no exception was raised - missing call to "
4005 "js::ReportOutOfMemory()?");
4008 // Note that it is possible that the function throws an exception unconnected
4009 // to the simulated failure, in which case we ignore it. More correct would be
4010 // to have the caller pass some kind of exception specification and to check
4011 // the exception against it.
4012 if (!failureWasSimulated
&& cx
->isExceptionPending()) {
4013 if (!cx
->getPendingException(exception
)) {
4017 cx
->clearPendingException();
4024 void IterativeFailureTest::cleanup() {
4025 simulator
.cleanup(cx
);
4029 // Some tests create a new compartment or zone on every iteration. Our GC is
4030 // triggered by GC allocations and not by number of compartments or zones, so
4031 // these won't normally get cleaned up. The check here stops some tests
4032 // running out of memory. ("Gentlemen, you can't fight in here! This is the
4034 if (CountCompartments(cx
) > compartmentCount
+ 100) {
4036 compartmentCount
= CountCompartments(cx
);
4040 void IterativeFailureTest::teardown() {
4041 simulator
.teardown(cx
);
4043 cx
->runningOOMTest
= false;
4046 bool IterativeFailureTest::initParams(const CallArgs
& args
) {
4047 if (args
.length() < 1 || args
.length() > 2) {
4048 JS_ReportErrorASCII(cx
, "function takes between 1 and 2 arguments.");
4052 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
4053 JS_ReportErrorASCII(cx
, "The first argument must be the function to test.");
4056 testFunction
= &args
[0].toObject().as
<JSFunction
>();
4058 if (args
.length() == 2) {
4059 if (args
[1].isBoolean()) {
4060 expectExceptionOnFailure
= args
[1].toBoolean();
4061 } else if (args
[1].isObject()) {
4062 RootedObject
options(cx
, &args
[1].toObject());
4063 RootedValue
value(cx
);
4065 if (!JS_GetProperty(cx
, options
, "expectExceptionOnFailure", &value
)) {
4068 if (!value
.isUndefined()) {
4069 expectExceptionOnFailure
= ToBoolean(value
);
4072 if (!JS_GetProperty(cx
, options
, "keepFailing", &value
)) {
4075 if (!value
.isUndefined()) {
4076 keepFailing
= ToBoolean(value
);
4079 JS_ReportErrorASCII(
4080 cx
, "The optional second argument must be an object or a boolean.");
4085 // There are some places where we do fail without raising an exception, so
4086 // we can't expose this to the fuzzers by default.
4088 expectExceptionOnFailure
= false;
4091 // Test all threads by default except worker threads.
4092 threadStart
= oom::FirstThreadTypeToTest
;
4093 threadEnd
= oom::LastThreadTypeToTest
;
4095 // Test a single thread type if specified by the OOM_THREAD environment
4097 int threadOption
= 0;
4098 if (EnvVarAsInt("OOM_THREAD", &threadOption
)) {
4099 if (threadOption
< oom::FirstThreadTypeToTest
||
4100 threadOption
> oom::LastThreadTypeToTest
) {
4101 JS_ReportErrorASCII(cx
, "OOM_THREAD value out of range.");
4105 threadStart
= threadOption
;
4106 threadEnd
= threadOption
;
4109 verbose
= EnvVarIsDefined("OOM_VERBOSE");
4114 struct OOMSimulator
: public IterativeFailureTest::FailureSimulator
{
4115 void setup(JSContext
* cx
) override
{ cx
->runtime()->hadOutOfMemory
= false; }
4117 void startSimulating(JSContext
* cx
, unsigned i
, unsigned thread
,
4118 bool keepFailing
) override
{
4119 MOZ_ASSERT(!cx
->runtime()->hadOutOfMemory
);
4120 js::oom::simulator
.simulateFailureAfter(
4121 js::oom::FailureSimulator::Kind::OOM
, i
, thread
, keepFailing
);
4124 bool stopSimulating() override
{
4125 bool handledOOM
= js::oom::HadSimulatedOOM();
4126 js::oom::simulator
.reset();
4130 void cleanup(JSContext
* cx
) override
{
4131 cx
->runtime()->hadOutOfMemory
= false;
4135 static bool OOMTest(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4136 CallArgs args
= CallArgsFromVp(argc
, vp
);
4138 OOMSimulator simulator
;
4139 if (!RunIterativeFailureTest(cx
, args
, simulator
)) {
4143 args
.rval().setUndefined();
4147 struct StackOOMSimulator
: public IterativeFailureTest::FailureSimulator
{
4148 void startSimulating(JSContext
* cx
, unsigned i
, unsigned thread
,
4149 bool keepFailing
) override
{
4150 js::oom::simulator
.simulateFailureAfter(
4151 js::oom::FailureSimulator::Kind::StackOOM
, i
, thread
, keepFailing
);
4154 bool stopSimulating() override
{
4155 bool handledOOM
= js::oom::HadSimulatedStackOOM();
4156 js::oom::simulator
.reset();
4161 static bool StackTest(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4162 CallArgs args
= CallArgsFromVp(argc
, vp
);
4164 StackOOMSimulator simulator
;
4165 if (!RunIterativeFailureTest(cx
, args
, simulator
)) {
4169 args
.rval().setUndefined();
4173 struct FailingIterruptSimulator
4174 : public IterativeFailureTest::FailureSimulator
{
4175 JSInterruptCallback
* prevEnd
= nullptr;
4177 static bool failingInterruptCallback(JSContext
* cx
) { return false; }
4179 void setup(JSContext
* cx
) override
{
4180 prevEnd
= cx
->interruptCallbacks().end();
4181 JS_AddInterruptCallback(cx
, failingInterruptCallback
);
4184 void teardown(JSContext
* cx
) override
{
4185 cx
->interruptCallbacks().erase(prevEnd
, cx
->interruptCallbacks().end());
4188 void startSimulating(JSContext
* cx
, unsigned i
, unsigned thread
,
4189 bool keepFailing
) override
{
4190 js::oom::simulator
.simulateFailureAfter(
4191 js::oom::FailureSimulator::Kind::Interrupt
, i
, thread
, keepFailing
);
4194 bool stopSimulating() override
{
4195 bool handledInterrupt
= js::oom::HadSimulatedInterrupt();
4196 js::oom::simulator
.reset();
4197 return handledInterrupt
;
4201 static bool InterruptTest(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4202 CallArgs args
= CallArgsFromVp(argc
, vp
);
4204 FailingIterruptSimulator simulator
;
4205 if (!RunIterativeFailureTest(cx
, args
, simulator
)) {
4209 args
.rval().setUndefined();
4213 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
4215 static bool SettlePromiseNow(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4216 CallArgs args
= CallArgsFromVp(argc
, vp
);
4217 if (!args
.requireAtLeast(cx
, "settlePromiseNow", 1)) {
4220 if (!args
[0].isObject() || !args
[0].toObject().is
<PromiseObject
>()) {
4221 JS_ReportErrorASCII(cx
, "first argument must be a Promise object");
4225 Rooted
<PromiseObject
*> promise(cx
, &args
[0].toObject().as
<PromiseObject
>());
4226 if (IsPromiseForAsyncFunctionOrGenerator(promise
)) {
4227 JS_ReportErrorASCII(
4228 cx
, "async function/generator's promise shouldn't be manually settled");
4232 if (promise
->state() != JS::PromiseState::Pending
) {
4233 JS_ReportErrorASCII(cx
, "cannot settle an already-resolved promise");
4237 if (IsPromiseWithDefaultResolvingFunction(promise
)) {
4238 SetAlreadyResolvedPromiseWithDefaultResolvingFunction(promise
);
4241 int32_t flags
= promise
->flags();
4242 promise
->setFixedSlot(
4244 Int32Value(flags
| PROMISE_FLAG_RESOLVED
| PROMISE_FLAG_FULFILLED
));
4245 promise
->setFixedSlot(PromiseSlot_ReactionsOrResult
, UndefinedValue());
4247 DebugAPI::onPromiseSettled(cx
, promise
);
4251 static bool GetWaitForAllPromise(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4252 CallArgs args
= CallArgsFromVp(argc
, vp
);
4253 if (!args
.requireAtLeast(cx
, "getWaitForAllPromise", 1)) {
4256 if (!args
[0].isObject() || !args
[0].toObject().is
<ArrayObject
>() ||
4257 args
[0].toObject().as
<NativeObject
>().isIndexed()) {
4258 JS_ReportErrorASCII(
4259 cx
, "first argument must be a dense Array of Promise objects");
4262 Rooted
<NativeObject
*> list(cx
, &args
[0].toObject().as
<NativeObject
>());
4263 RootedObjectVector
promises(cx
);
4264 uint32_t count
= list
->getDenseInitializedLength();
4265 if (!promises
.resize(count
)) {
4269 for (uint32_t i
= 0; i
< count
; i
++) {
4270 RootedValue
elem(cx
, list
->getDenseElement(i
));
4271 if (!elem
.isObject() || !elem
.toObject().is
<PromiseObject
>()) {
4272 JS_ReportErrorASCII(
4273 cx
, "Each entry in the passed-in Array must be a Promise");
4276 promises
[i
].set(&elem
.toObject());
4279 RootedObject
resultPromise(cx
, JS::GetWaitForAllPromise(cx
, promises
));
4280 if (!resultPromise
) {
4284 args
.rval().set(ObjectValue(*resultPromise
));
4288 static bool ResolvePromise(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4289 CallArgs args
= CallArgsFromVp(argc
, vp
);
4290 if (!args
.requireAtLeast(cx
, "resolvePromise", 2)) {
4293 if (!args
[0].isObject() ||
4294 !UncheckedUnwrap(&args
[0].toObject())->is
<PromiseObject
>()) {
4295 JS_ReportErrorASCII(
4296 cx
, "first argument must be a maybe-wrapped Promise object");
4300 RootedObject
promise(cx
, &args
[0].toObject());
4301 RootedValue
resolution(cx
, args
[1]);
4302 mozilla::Maybe
<AutoRealm
> ar
;
4303 if (IsWrapper(promise
)) {
4304 promise
= UncheckedUnwrap(promise
);
4305 ar
.emplace(cx
, promise
);
4306 if (!cx
->compartment()->wrap(cx
, &resolution
)) {
4311 if (IsPromiseForAsyncFunctionOrGenerator(promise
)) {
4312 JS_ReportErrorASCII(
4314 "async function/generator's promise shouldn't be manually resolved");
4318 bool result
= JS::ResolvePromise(cx
, promise
, resolution
);
4320 args
.rval().setUndefined();
4325 static bool RejectPromise(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4326 CallArgs args
= CallArgsFromVp(argc
, vp
);
4327 if (!args
.requireAtLeast(cx
, "rejectPromise", 2)) {
4330 if (!args
[0].isObject() ||
4331 !UncheckedUnwrap(&args
[0].toObject())->is
<PromiseObject
>()) {
4332 JS_ReportErrorASCII(
4333 cx
, "first argument must be a maybe-wrapped Promise object");
4337 RootedObject
promise(cx
, &args
[0].toObject());
4338 RootedValue
reason(cx
, args
[1]);
4339 mozilla::Maybe
<AutoRealm
> ar
;
4340 if (IsWrapper(promise
)) {
4341 promise
= UncheckedUnwrap(promise
);
4342 ar
.emplace(cx
, promise
);
4343 if (!cx
->compartment()->wrap(cx
, &reason
)) {
4348 if (IsPromiseForAsyncFunctionOrGenerator(promise
)) {
4349 JS_ReportErrorASCII(
4351 "async function/generator's promise shouldn't be manually rejected");
4355 bool result
= JS::RejectPromise(cx
, promise
, reason
);
4357 args
.rval().setUndefined();
4362 static unsigned finalizeCount
= 0;
4364 static void finalize_counter_finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
4368 static const JSClassOps FinalizeCounterClassOps
= {
4369 nullptr, // addProperty
4370 nullptr, // delProperty
4371 nullptr, // enumerate
4372 nullptr, // newEnumerate
4374 nullptr, // mayResolve
4375 finalize_counter_finalize
, // finalize
4377 nullptr, // construct
4381 static const JSClass FinalizeCounterClass
= {
4382 "FinalizeCounter", JSCLASS_FOREGROUND_FINALIZE
, &FinalizeCounterClassOps
};
4384 static bool MakeFinalizeObserver(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4385 CallArgs args
= CallArgsFromVp(argc
, vp
);
4388 JS_NewObjectWithGivenProto(cx
, &FinalizeCounterClass
, nullptr);
4393 args
.rval().setObject(*obj
);
4397 static bool FinalizeCount(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4398 CallArgs args
= CallArgsFromVp(argc
, vp
);
4399 args
.rval().setInt32(finalizeCount
);
4403 static bool ResetFinalizeCount(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4404 CallArgs args
= CallArgsFromVp(argc
, vp
);
4406 args
.rval().setUndefined();
4410 static bool DumpHeap(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4411 CallArgs args
= CallArgsFromVp(argc
, vp
);
4413 FILE* dumpFile
= stdout
;
4414 auto closeFile
= mozilla::MakeScopeExit([&dumpFile
] {
4415 if (dumpFile
!= stdout
) {
4420 if (args
.length() > 1) {
4421 RootedObject
callee(cx
, &args
.callee());
4422 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
4426 if (!args
.get(0).isUndefined()) {
4427 RootedString
str(cx
, ToString(cx
, args
[0]));
4432 UniqueChars fileNameBytes
= JS_EncodeStringToUTF8(cx
, str
);
4433 if (!fileNameBytes
) {
4437 UniqueWideChars wideFileNameBytes
=
4438 JS::EncodeUtf8ToWide(cx
, fileNameBytes
.get());
4439 if (!wideFileNameBytes
) {
4442 dumpFile
= _wfopen(wideFileNameBytes
.get(), L
"w");
4444 UniqueChars narrowFileNameBytes
=
4445 JS::EncodeUtf8ToNarrow(cx
, fileNameBytes
.get());
4446 if (!narrowFileNameBytes
) {
4449 dumpFile
= fopen(narrowFileNameBytes
.get(), "w");
4452 JS_ReportErrorUTF8(cx
, "can't open %s", fileNameBytes
.get());
4458 js::DumpHeap(cx
, dumpFile
, js::IgnoreNurseryObjects
);
4460 args
.rval().setUndefined();
4464 static bool Terminate(JSContext
* cx
, unsigned arg
, Value
* vp
) {
4465 // Print a message to stderr in differential testing to help jsfunfuzz
4466 // find uncatchable-exception bugs.
4467 if (js::SupportDifferentialTesting()) {
4468 fprintf(stderr
, "terminate called\n");
4471 JS_ClearPendingException(cx
);
4475 static bool ReadGeckoProfilingStack(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4476 CallArgs args
= CallArgsFromVp(argc
, vp
);
4477 args
.rval().setUndefined();
4479 // Return boolean 'false' if profiler is not enabled.
4480 if (!cx
->runtime()->geckoProfiler().enabled()) {
4481 args
.rval().setBoolean(false);
4485 // Array holding physical jit stack frames.
4486 RootedObject
stack(cx
, NewDenseEmptyArray(cx
));
4491 // If profiler sampling has been suppressed, return an empty
4493 if (!cx
->isProfilerSamplingEnabled()) {
4494 args
.rval().setObject(*stack
);
4498 struct InlineFrameInfo
{
4499 InlineFrameInfo(const char* kind
, UniqueChars label
)
4500 : kind(kind
), label(std::move(label
)) {}
4505 Vector
<Vector
<InlineFrameInfo
, 0, TempAllocPolicy
>, 0, TempAllocPolicy
>
4508 JS::ProfilingFrameIterator::RegisterState state
;
4509 for (JS::ProfilingFrameIterator
i(cx
, state
); !i
.done(); ++i
) {
4510 MOZ_ASSERT(i
.stackAddress() != nullptr);
4512 if (!frameInfo
.emplaceBack(cx
)) {
4516 const size_t MaxInlineFrames
= 16;
4517 JS::ProfilingFrameIterator::Frame frames
[MaxInlineFrames
];
4518 uint32_t nframes
= i
.extractStack(frames
, 0, MaxInlineFrames
);
4519 MOZ_ASSERT(nframes
<= MaxInlineFrames
);
4520 for (uint32_t i
= 0; i
< nframes
; i
++) {
4521 const char* frameKindStr
= nullptr;
4522 switch (frames
[i
].kind
) {
4523 case JS::ProfilingFrameIterator::Frame_BaselineInterpreter
:
4524 frameKindStr
= "baseline-interpreter";
4526 case JS::ProfilingFrameIterator::Frame_Baseline
:
4527 frameKindStr
= "baseline-jit";
4529 case JS::ProfilingFrameIterator::Frame_Ion
:
4530 frameKindStr
= "ion";
4532 case JS::ProfilingFrameIterator::Frame_Wasm
:
4533 frameKindStr
= "wasm";
4536 frameKindStr
= "unknown";
4540 DuplicateStringToArena(js::StringBufferArena
, cx
, frames
[i
].label
);
4545 if (!frameInfo
.back().emplaceBack(frameKindStr
, std::move(label
))) {
4551 RootedObject
inlineFrameInfo(cx
);
4552 RootedString
frameKind(cx
);
4553 RootedString
frameLabel(cx
);
4556 const unsigned propAttrs
= JSPROP_ENUMERATE
;
4558 uint32_t physicalFrameNo
= 0;
4559 for (auto& frame
: frameInfo
) {
4560 // Array holding all inline frames in a single physical jit stack frame.
4561 RootedObject
inlineStack(cx
, NewDenseEmptyArray(cx
));
4566 uint32_t inlineFrameNo
= 0;
4567 for (auto& inlineFrame
: frame
) {
4568 // Object holding frame info.
4569 RootedObject
inlineFrameInfo(cx
, NewPlainObject(cx
));
4570 if (!inlineFrameInfo
) {
4574 frameKind
= NewStringCopyZ
<CanGC
>(cx
, inlineFrame
.kind
);
4579 if (!JS_DefineProperty(cx
, inlineFrameInfo
, "kind", frameKind
,
4584 frameLabel
= NewLatin1StringZ(cx
, std::move(inlineFrame
.label
));
4589 if (!JS_DefineProperty(cx
, inlineFrameInfo
, "label", frameLabel
,
4594 idx
= PropertyKey::Int(inlineFrameNo
);
4595 if (!JS_DefinePropertyById(cx
, inlineStack
, idx
, inlineFrameInfo
, 0)) {
4602 // Push inline array into main array.
4603 idx
= PropertyKey::Int(physicalFrameNo
);
4604 if (!JS_DefinePropertyById(cx
, stack
, idx
, inlineStack
, 0)) {
4611 args
.rval().setObject(*stack
);
4615 static bool ReadGeckoInterpProfilingStack(JSContext
* cx
, unsigned argc
,
4617 CallArgs args
= CallArgsFromVp(argc
, vp
);
4618 args
.rval().setUndefined();
4620 // Return boolean 'false' if profiler is not enabled.
4621 if (!cx
->runtime()->geckoProfiler().enabled()) {
4622 args
.rval().setBoolean(false);
4626 // Array with information about each frame.
4627 Rooted
<JSObject
*> stack(cx
, NewDenseEmptyArray(cx
));
4631 uint32_t stackIndex
= 0;
4633 ProfilingStack
* profStack
= cx
->geckoProfiler().getProfilingStack();
4634 MOZ_ASSERT(profStack
);
4636 for (size_t i
= 0; i
< profStack
->stackSize(); i
++) {
4637 const auto& frame
= profStack
->frames
[i
];
4638 if (!frame
.isJsFrame()) {
4642 // Skip fake JS frame pushed for js::RunScript by GeckoProfilerEntryMarker.
4643 const char* dynamicStr
= frame
.dynamicString();
4648 Rooted
<PlainObject
*> frameInfo(cx
, NewPlainObject(cx
));
4653 Rooted
<JSString
*> dynamicString(
4654 cx
, JS_NewStringCopyUTF8Z(
4655 cx
, JS::ConstUTF8CharsZ(dynamicStr
, strlen(dynamicStr
))));
4656 if (!dynamicString
) {
4659 if (!JS_DefineProperty(cx
, frameInfo
, "dynamicString", dynamicString
,
4660 JSPROP_ENUMERATE
)) {
4664 if (!JS_DefineElement(cx
, stack
, stackIndex
, frameInfo
, JSPROP_ENUMERATE
)) {
4670 args
.rval().setObject(*stack
);
4674 static bool EnableOsiPointRegisterChecks(JSContext
*, unsigned argc
, Value
* vp
) {
4675 CallArgs args
= CallArgsFromVp(argc
, vp
);
4676 #ifdef CHECK_OSIPOINT_REGISTERS
4677 jit::JitOptions
.checkOsiPointRegisters
= true;
4679 args
.rval().setUndefined();
4683 static bool DisplayName(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4684 CallArgs args
= CallArgsFromVp(argc
, vp
);
4685 if (!args
.get(0).isObject() || !args
[0].toObject().is
<JSFunction
>()) {
4686 RootedObject
arg(cx
, &args
.callee());
4687 ReportUsageErrorASCII(cx
, arg
, "Must have one function argument");
4691 JSFunction
* fun
= &args
[0].toObject().as
<JSFunction
>();
4692 JS::Rooted
<JSAtom
*> str(cx
);
4693 if (!fun
->getDisplayAtom(cx
, &str
)) {
4696 args
.rval().setString(str
? str
: cx
->runtime()->emptyString
.ref());
4700 static bool IsAvxPresent(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4701 CallArgs args
= CallArgsFromVp(argc
, vp
);
4702 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
4704 if (argc
> 0 && args
.get(0).isNumber()) {
4705 minVersion
= std::max(1, int(args
[0].toNumber()));
4707 switch (minVersion
) {
4709 args
.rval().setBoolean(jit::Assembler::HasAVX());
4712 args
.rval().setBoolean(jit::Assembler::HasAVX2());
4716 args
.rval().setBoolean(false);
4720 class ShellAllocationMetadataBuilder
: public AllocationMetadataBuilder
{
4722 ShellAllocationMetadataBuilder() = default;
4724 virtual JSObject
* build(JSContext
* cx
, HandleObject
,
4725 AutoEnterOOMUnsafeRegion
& oomUnsafe
) const override
;
4727 static const ShellAllocationMetadataBuilder metadataBuilder
;
4730 JSObject
* ShellAllocationMetadataBuilder::build(
4731 JSContext
* cx
, HandleObject
, AutoEnterOOMUnsafeRegion
& oomUnsafe
) const {
4732 RootedObject
obj(cx
, NewPlainObject(cx
));
4734 oomUnsafe
.crash("ShellAllocationMetadataBuilder::build");
4737 RootedObject
stack(cx
, NewDenseEmptyArray(cx
));
4739 oomUnsafe
.crash("ShellAllocationMetadataBuilder::build");
4742 static int createdIndex
= 0;
4745 if (!JS_DefineProperty(cx
, obj
, "index", createdIndex
, 0)) {
4746 oomUnsafe
.crash("ShellAllocationMetadataBuilder::build");
4749 if (!JS_DefineProperty(cx
, obj
, "stack", stack
, 0)) {
4750 oomUnsafe
.crash("ShellAllocationMetadataBuilder::build");
4755 RootedValue
callee(cx
);
4756 for (NonBuiltinScriptFrameIter
iter(cx
); !iter
.done(); ++iter
) {
4757 if (iter
.isFunctionFrame() && iter
.compartment() == cx
->compartment()) {
4758 id
= PropertyKey::Int(stackIndex
);
4759 RootedObject
callee(cx
, iter
.callee(cx
));
4760 if (!JS_DefinePropertyById(cx
, stack
, id
, callee
, JSPROP_ENUMERATE
)) {
4761 oomUnsafe
.crash("ShellAllocationMetadataBuilder::build");
4770 const ShellAllocationMetadataBuilder
4771 ShellAllocationMetadataBuilder::metadataBuilder
;
4773 static bool EnableShellAllocationMetadataBuilder(JSContext
* cx
, unsigned argc
,
4775 CallArgs args
= CallArgsFromVp(argc
, vp
);
4777 SetAllocationMetadataBuilder(
4778 cx
, &ShellAllocationMetadataBuilder::metadataBuilder
);
4780 args
.rval().setUndefined();
4784 static bool GetAllocationMetadata(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4785 CallArgs args
= CallArgsFromVp(argc
, vp
);
4786 if (args
.length() != 1 || !args
[0].isObject()) {
4787 JS_ReportErrorASCII(cx
, "Argument must be an object");
4791 args
.rval().setObjectOrNull(GetAllocationMetadata(&args
[0].toObject()));
4795 static bool testingFunc_bailout(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4796 CallArgs args
= CallArgsFromVp(argc
, vp
);
4798 // NOP when not in IonMonkey
4799 args
.rval().setUndefined();
4803 static bool testingFunc_bailAfter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4804 CallArgs args
= CallArgsFromVp(argc
, vp
);
4805 if (args
.length() != 1 || !args
[0].isInt32() || args
[0].toInt32() < 0) {
4806 JS_ReportErrorASCII(
4807 cx
, "Argument must be a positive number that fits in an int32");
4812 if (auto* jitRuntime
= cx
->runtime()->jitRuntime()) {
4813 uint32_t bailAfter
= args
[0].toInt32();
4814 bool enableBailAfter
= bailAfter
> 0;
4815 if (jitRuntime
->ionBailAfterEnabled() != enableBailAfter
) {
4816 // Force JIT code to be recompiled with (or without) instrumentation.
4817 ReleaseAllJITCode(cx
->gcContext());
4818 jitRuntime
->setIonBailAfterEnabled(enableBailAfter
);
4820 jitRuntime
->setIonBailAfterCounter(bailAfter
);
4824 args
.rval().setUndefined();
4828 static bool testingFunc_invalidate(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4829 CallArgs args
= CallArgsFromVp(argc
, vp
);
4831 // If the topmost frame is Ion/Warp, find the IonScript and invalidate it.
4833 if (!iter
.done() && iter
.isIon()) {
4834 while (!iter
.isPhysicalJitFrame()) {
4837 if (iter
.script()->hasIonScript()) {
4838 js::jit::Invalidate(cx
, iter
.script());
4842 args
.rval().setUndefined();
4846 static constexpr unsigned JitWarmupResetLimit
= 20;
4847 static_assert(JitWarmupResetLimit
<=
4848 unsigned(JSScript::MutableFlags::WarmupResets_MASK
),
4849 "JitWarmupResetLimit exceeds max value");
4851 static bool testingFunc_inJit(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4852 CallArgs args
= CallArgsFromVp(argc
, vp
);
4854 if (!jit::IsBaselineJitEnabled(cx
)) {
4855 return ReturnStringCopy(cx
, args
, "Baseline is disabled.");
4858 // Use frame iterator to inspect caller.
4861 // We may be invoked directly, not in a JS context, e.g. if inJit is added as
4862 // a callback on the event queue.
4864 args
.rval().setBoolean(false);
4868 if (iter
.hasScript()) {
4869 // Detect repeated attempts to compile, resetting the counter if inJit
4870 // succeeds. Note: This script may have be inlined into its caller.
4871 if (iter
.isJSJit()) {
4872 iter
.script()->resetWarmUpResetCounter();
4873 } else if (iter
.script()->getWarmUpResetCount() >= JitWarmupResetLimit
) {
4874 return ReturnStringCopy(
4875 cx
, args
, "Compilation is being repeatedly prevented. Giving up.");
4879 // Returns true for any JIT (including WASM).
4880 MOZ_ASSERT_IF(iter
.isJSJit(), cx
->currentlyRunningInJit());
4881 args
.rval().setBoolean(cx
->currentlyRunningInJit());
4885 static bool testingFunc_inIon(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4886 CallArgs args
= CallArgsFromVp(argc
, vp
);
4888 if (!jit::IsIonEnabled(cx
)) {
4889 return ReturnStringCopy(cx
, args
, "Ion is disabled.");
4892 // Use frame iterator to inspect caller.
4895 // We may be invoked directly, not in a JS context, e.g. if inIon is added as
4896 // a callback on the event queue.
4898 args
.rval().setBoolean(false);
4902 if (iter
.hasScript()) {
4903 // Detect repeated attempts to compile, resetting the counter if inIon
4904 // succeeds. Note: This script may have be inlined into its caller.
4906 iter
.script()->resetWarmUpResetCounter();
4907 } else if (!iter
.script()->canIonCompile()) {
4908 return ReturnStringCopy(cx
, args
, "Unable to Ion-compile this script.");
4909 } else if (iter
.script()->getWarmUpResetCount() >= JitWarmupResetLimit
) {
4910 return ReturnStringCopy(
4911 cx
, args
, "Compilation is being repeatedly prevented. Giving up.");
4915 args
.rval().setBoolean(iter
.isIon());
4919 bool js::testingFunc_assertFloat32(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4920 CallArgs args
= CallArgsFromVp(argc
, vp
);
4921 if (args
.length() != 2) {
4922 JS_ReportErrorASCII(cx
, "Expects only 2 arguments");
4926 // NOP when not in IonMonkey
4927 args
.rval().setUndefined();
4931 static bool TestingFunc_assertJitStackInvariants(JSContext
* cx
, unsigned argc
,
4933 CallArgs args
= CallArgsFromVp(argc
, vp
);
4935 jit::AssertJitStackInvariants(cx
);
4936 args
.rval().setUndefined();
4940 bool js::testingFunc_assertRecoveredOnBailout(JSContext
* cx
, unsigned argc
,
4942 CallArgs args
= CallArgsFromVp(argc
, vp
);
4943 if (args
.length() != 2) {
4944 JS_ReportErrorASCII(cx
, "Expects only 2 arguments");
4948 // NOP when not in IonMonkey
4949 args
.rval().setUndefined();
4953 static bool GetJitCompilerOptions(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4954 CallArgs args
= CallArgsFromVp(argc
, vp
);
4955 RootedObject
info(cx
, JS_NewPlainObject(cx
));
4960 uint32_t intValue
= 0;
4961 RootedValue
value(cx
);
4963 #define JIT_COMPILER_MATCH(key, string) \
4964 opt = JSJITCOMPILER_##key; \
4965 if (JS_GetGlobalJitCompilerOption(cx, opt, &intValue)) { \
4966 value.setInt32(intValue); \
4967 if (!JS_SetProperty(cx, info, string, value)) return false; \
4970 JSJitCompilerOption opt
= JSJITCOMPILER_NOT_AN_OPTION
;
4971 JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH
);
4972 #undef JIT_COMPILER_MATCH
4974 args
.rval().setObject(*info
);
4978 static bool SetIonCheckGraphCoherency(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4979 CallArgs args
= CallArgsFromVp(argc
, vp
);
4980 jit::JitOptions
.checkGraphConsistency
= ToBoolean(args
.get(0));
4981 args
.rval().setUndefined();
4985 // A JSObject that holds structured clone data, similar to the C++ class
4986 // JSAutoStructuredCloneBuffer.
4987 class CloneBufferObject
: public NativeObject
{
4988 static const JSPropertySpec props_
[3];
4990 static const size_t DATA_SLOT
= 0;
4991 static const size_t SYNTHETIC_SLOT
= 1;
4992 static const size_t NUM_SLOTS
= 2;
4995 static const JSClass class_
;
4997 static CloneBufferObject
* Create(JSContext
* cx
) {
4998 RootedObject
obj(cx
, JS_NewObject(cx
, &class_
));
5002 obj
->as
<CloneBufferObject
>().setReservedSlot(DATA_SLOT
,
5003 PrivateValue(nullptr));
5004 obj
->as
<CloneBufferObject
>().setReservedSlot(SYNTHETIC_SLOT
,
5005 BooleanValue(false));
5007 if (!JS_DefineProperties(cx
, obj
, props_
)) {
5011 return &obj
->as
<CloneBufferObject
>();
5014 static CloneBufferObject
* Create(JSContext
* cx
,
5015 JSAutoStructuredCloneBuffer
* buffer
) {
5016 Rooted
<CloneBufferObject
*> obj(cx
, Create(cx
));
5020 auto data
= js::MakeUnique
<JSStructuredCloneData
>(buffer
->scope());
5022 ReportOutOfMemory(cx
);
5025 buffer
->giveTo(data
.get());
5026 obj
->setData(data
.release(), false);
5030 JSStructuredCloneData
* data() const {
5031 return static_cast<JSStructuredCloneData
*>(
5032 getReservedSlot(DATA_SLOT
).toPrivate());
5035 bool isSynthetic() const {
5036 return getReservedSlot(SYNTHETIC_SLOT
).toBoolean();
5039 void setData(JSStructuredCloneData
* aData
, bool synthetic
) {
5040 MOZ_ASSERT(!data());
5041 setReservedSlot(DATA_SLOT
, PrivateValue(aData
));
5042 setReservedSlot(SYNTHETIC_SLOT
, BooleanValue(synthetic
));
5045 // Discard an owned clone buffer.
5048 setReservedSlot(DATA_SLOT
, PrivateValue(nullptr));
5051 static bool setCloneBuffer_impl(JSContext
* cx
, const CallArgs
& args
) {
5052 Rooted
<CloneBufferObject
*> obj(
5053 cx
, &args
.thisv().toObject().as
<CloneBufferObject
>());
5055 const char* data
= nullptr;
5056 UniqueChars dataOwner
;
5059 if (args
.get(0).isObject() && args
[0].toObject().is
<ArrayBufferObject
>()) {
5060 ArrayBufferObject
* buffer
= &args
[0].toObject().as
<ArrayBufferObject
>();
5061 bool isSharedMemory
;
5062 uint8_t* dataBytes
= nullptr;
5063 JS::GetArrayBufferLengthAndData(buffer
, &nbytes
, &isSharedMemory
,
5065 MOZ_ASSERT(!isSharedMemory
);
5066 data
= reinterpret_cast<char*>(dataBytes
);
5068 JSString
* str
= JS::ToString(cx
, args
.get(0));
5072 dataOwner
= JS_EncodeStringToLatin1(cx
, str
);
5076 data
= dataOwner
.get();
5077 nbytes
= JS_GetStringLength(str
);
5080 if (nbytes
== 0 || (nbytes
% sizeof(uint64_t) != 0)) {
5081 JS_ReportErrorASCII(cx
, "Invalid length for clonebuffer data");
5085 auto buf
= js::MakeUnique
<JSStructuredCloneData
>(
5086 JS::StructuredCloneScope::DifferentProcess
);
5087 if (!buf
|| !buf
->Init(nbytes
)) {
5088 ReportOutOfMemory(cx
);
5092 MOZ_ALWAYS_TRUE(buf
->AppendBytes(data
, nbytes
));
5094 obj
->setData(buf
.release(), true);
5096 args
.rval().setUndefined();
5100 static bool is(HandleValue v
) {
5101 return v
.isObject() && v
.toObject().is
<CloneBufferObject
>();
5104 static bool setCloneBuffer(JSContext
* cx
, unsigned int argc
, JS::Value
* vp
) {
5105 CallArgs args
= CallArgsFromVp(argc
, vp
);
5106 return CallNonGenericMethod
<is
, setCloneBuffer_impl
>(cx
, args
);
5109 static bool getData(JSContext
* cx
, Handle
<CloneBufferObject
*> obj
,
5110 JSStructuredCloneData
** data
) {
5116 bool hasTransferable
;
5117 if (!JS_StructuredCloneHasTransferables(*obj
->data(), &hasTransferable
)) {
5121 if (hasTransferable
) {
5122 JS_ReportErrorASCII(
5123 cx
, "cannot retrieve structured clone buffer with transferables");
5127 *data
= obj
->data();
5131 static bool getCloneBuffer_impl(JSContext
* cx
, const CallArgs
& args
) {
5132 Rooted
<CloneBufferObject
*> obj(
5133 cx
, &args
.thisv().toObject().as
<CloneBufferObject
>());
5134 MOZ_ASSERT(args
.length() == 0);
5136 JSStructuredCloneData
* data
;
5137 if (!getData(cx
, obj
, &data
)) {
5141 size_t size
= data
->Size();
5142 UniqueChars
buffer(js_pod_malloc
<char>(size
));
5144 ReportOutOfMemory(cx
);
5147 auto iter
= data
->Start();
5148 if (!data
->ReadBytes(iter
, buffer
.get(), size
)) {
5149 ReportOutOfMemory(cx
);
5152 JSString
* str
= JS_NewStringCopyN(cx
, buffer
.get(), size
);
5156 args
.rval().setString(str
);
5160 static bool getCloneBuffer(JSContext
* cx
, unsigned int argc
, JS::Value
* vp
) {
5161 CallArgs args
= CallArgsFromVp(argc
, vp
);
5162 return CallNonGenericMethod
<is
, getCloneBuffer_impl
>(cx
, args
);
5165 static bool getCloneBufferAsArrayBuffer_impl(JSContext
* cx
,
5166 const CallArgs
& args
) {
5167 Rooted
<CloneBufferObject
*> obj(
5168 cx
, &args
.thisv().toObject().as
<CloneBufferObject
>());
5169 MOZ_ASSERT(args
.length() == 0);
5171 JSStructuredCloneData
* data
;
5172 if (!getData(cx
, obj
, &data
)) {
5176 size_t size
= data
->Size();
5177 UniqueChars
buffer(js_pod_malloc
<char>(size
));
5179 ReportOutOfMemory(cx
);
5182 auto iter
= data
->Start();
5183 if (!data
->ReadBytes(iter
, buffer
.get(), size
)) {
5184 ReportOutOfMemory(cx
);
5188 JSObject
* arrayBuffer
=
5189 JS::NewArrayBufferWithContents(cx
, size
, std::move(buffer
));
5194 args
.rval().setObject(*arrayBuffer
);
5198 static bool getCloneBufferAsArrayBuffer(JSContext
* cx
, unsigned int argc
,
5200 CallArgs args
= CallArgsFromVp(argc
, vp
);
5201 return CallNonGenericMethod
<is
, getCloneBufferAsArrayBuffer_impl
>(cx
, args
);
5204 static void Finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
5205 obj
->as
<CloneBufferObject
>().discard();
5209 static const JSClassOps CloneBufferObjectClassOps
= {
5210 nullptr, // addProperty
5211 nullptr, // delProperty
5212 nullptr, // enumerate
5213 nullptr, // newEnumerate
5215 nullptr, // mayResolve
5216 CloneBufferObject::Finalize
, // finalize
5218 nullptr, // construct
5222 const JSClass
CloneBufferObject::class_
= {
5224 JSCLASS_HAS_RESERVED_SLOTS(CloneBufferObject::NUM_SLOTS
) |
5225 JSCLASS_FOREGROUND_FINALIZE
,
5226 &CloneBufferObjectClassOps
};
5228 const JSPropertySpec
CloneBufferObject::props_
[] = {
5229 JS_PSGS("clonebuffer", getCloneBuffer
, setCloneBuffer
, 0),
5230 JS_PSGS("arraybuffer", getCloneBufferAsArrayBuffer
, setCloneBuffer
, 0),
5233 static mozilla::Maybe
<JS::StructuredCloneScope
> ParseCloneScope(
5234 JSContext
* cx
, HandleString str
) {
5235 mozilla::Maybe
<JS::StructuredCloneScope
> scope
;
5237 JSLinearString
* scopeStr
= str
->ensureLinear(cx
);
5242 if (StringEqualsLiteral(scopeStr
, "SameProcess")) {
5243 scope
.emplace(JS::StructuredCloneScope::SameProcess
);
5244 } else if (StringEqualsLiteral(scopeStr
, "DifferentProcess")) {
5245 scope
.emplace(JS::StructuredCloneScope::DifferentProcess
);
5246 } else if (StringEqualsLiteral(scopeStr
, "DifferentProcessForIndexedDB")) {
5247 scope
.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB
);
5253 // A custom object that is serializable and transferable using
5254 // the engine's custom hooks. The callbacks log their activity
5255 // to a JSRuntime-wide log (tagging actions with IDs to distinguish them).
5256 class CustomSerializableObject
: public NativeObject
{
5257 static const size_t ID_SLOT
= 0;
5258 static const size_t DETACHED_SLOT
= 1;
5259 static const size_t BEHAVIOR_SLOT
= 2;
5260 static const size_t NUM_SLOTS
= 3;
5262 static constexpr size_t MAX_LOG_LEN
= 100;
5264 // The activity log should be specific to a JSRuntime.
5265 struct ActivityLog
{
5266 uint32_t buffer
[MAX_LOG_LEN
];
5269 static MOZ_THREAD_LOCAL(ActivityLog
*) self
;
5270 static ActivityLog
* getThreadLog() {
5271 if (!self
.initialized() || !self
.get()) {
5272 self
.infallibleInit();
5273 self
.set(js_new
<ActivityLog
>());
5274 MOZ_RELEASE_ASSERT(self
.get());
5275 if (!TlsContext
.get()->runtime()->atExit(
5277 auto* log
= static_cast<ActivityLog
*>(vpData
);
5281 AutoEnterOOMUnsafeRegion oomUnsafe
;
5282 oomUnsafe
.crash("atExit");
5288 static bool log(int32_t id
, char action
) {
5289 return getThreadLog()->logImpl(id
, action
);
5292 bool logImpl(int32_t id
, char action
) {
5293 if (length
+ 2 > MAX_LOG_LEN
) {
5296 buffer
[length
++] = id
;
5297 buffer
[length
++] = uint32_t(action
);
5303 enum class Behavior
{
5305 FailDuringReadTransfer
= 1,
5309 static constexpr JSClass class_
= {"CustomSerializable",
5310 JSCLASS_HAS_RESERVED_SLOTS(NUM_SLOTS
)};
5312 static bool is(HandleValue v
) {
5313 return v
.isObject() && v
.toObject().is
<CustomSerializableObject
>();
5316 static CustomSerializableObject
* Create(JSContext
* cx
, int32_t id
,
5317 Behavior behavior
) {
5318 Rooted
<CustomSerializableObject
*> obj(
5319 cx
, static_cast<CustomSerializableObject
*>(JS_NewObject(cx
, &class_
)));
5323 obj
->setReservedSlot(ID_SLOT
, Int32Value(id
));
5324 obj
->setReservedSlot(DETACHED_SLOT
, BooleanValue(false));
5325 obj
->setReservedSlot(BEHAVIOR_SLOT
,
5326 Int32Value(static_cast<int32_t>(behavior
)));
5328 if (!JS_DefineProperty(cx
, obj
, "log", getLog
, clearLog
, 0)) {
5336 static uint32_t tag() { return JS_SCTAG_USER_MIN
; }
5338 static bool log(int32_t id
, char action
) {
5339 return ActivityLog::log(id
, action
);
5341 bool log(char action
) {
5342 return log(getReservedSlot(ID_SLOT
).toInt32(), action
);
5345 void detach() { setReservedSlot(DETACHED_SLOT
, BooleanValue(true)); }
5346 bool isDetached() { return getReservedSlot(DETACHED_SLOT
).toBoolean(); }
5348 uint32_t id() const { return getReservedSlot(ID_SLOT
).toInt32(); }
5349 Behavior
behavior() {
5350 return static_cast<Behavior
>(getReservedSlot(BEHAVIOR_SLOT
).toInt32());
5353 static bool getLog(JSContext
* cx
, unsigned int argc
, JS::Value
* vp
) {
5354 CallArgs args
= CallArgsFromVp(argc
, vp
);
5355 return CallNonGenericMethod
<is
, getLog_impl
>(cx
, args
);
5358 static bool getLog_impl(JSContext
* cx
, const CallArgs
& args
) {
5359 Rooted
<CustomSerializableObject
*> obj(
5360 cx
, &args
.thisv().toObject().as
<CustomSerializableObject
>());
5362 size_t len
= ActivityLog::getThreadLog()->length
;
5363 uint32_t* logBuffer
= ActivityLog::getThreadLog()->buffer
;
5365 Rooted
<ArrayObject
*> result(cx
, NewDenseFullyAllocatedArray(cx
, len
));
5369 result
->ensureDenseInitializedLength(0, len
);
5371 for (size_t p
= 0; p
< len
; p
+= 2) {
5372 int32_t id
= int32_t(logBuffer
[p
]);
5373 char action
= char(logBuffer
[p
+ 1]);
5374 result
->setDenseElement(p
, Int32Value(id
));
5375 JSString
* str
= JS_NewStringCopyN(cx
, &action
, 1);
5379 result
->setDenseElement(p
+ 1, StringValue(str
));
5382 args
.rval().setObject(*result
);
5386 static bool clearLog(JSContext
* cx
, unsigned int argc
, JS::Value
* vp
) {
5387 CallArgs args
= CallArgsFromVp(argc
, vp
);
5388 if (!args
.get(0).isNullOrUndefined()) {
5389 JS_ReportErrorASCII(cx
, "log may only be assigned null/undefined");
5392 ActivityLog::getThreadLog()->length
= 0;
5393 args
.rval().setUndefined();
5397 static bool Write(JSContext
* cx
, JSStructuredCloneWriter
* w
,
5398 JS::HandleObject aObj
, bool* sameProcessScopeRequired
,
5400 Rooted
<CustomSerializableObject
*> obj(cx
);
5402 if ((obj
= aObj
->maybeUnwrapIf
<CustomSerializableObject
>())) {
5404 // Write a regular clone as a <tag, id> pair, followed by <0, behavior>.
5405 // Note that transferring will communicate the behavior via a different
5407 return JS_WriteUint32Pair(w
, obj
->tag(), obj
->id()) &&
5408 JS_WriteUint32Pair(w
, 0, static_cast<uint32_t>(obj
->behavior()));
5411 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
5412 JSMSG_SC_UNSUPPORTED_TYPE
);
5416 static JSObject
* Read(JSContext
* cx
, JSStructuredCloneReader
* r
,
5417 const JS::CloneDataPolicy
& cloneDataPolicy
,
5418 uint32_t tag
, uint32_t id
, void* closure
) {
5419 uint32_t dummy
, behaviorData
;
5420 if (!JS_ReadUint32Pair(r
, &dummy
, &behaviorData
)) {
5423 if (dummy
!= 0 || id
> INT32_MAX
) {
5424 JS_ReportErrorASCII(cx
, "out of range");
5428 auto b
= static_cast<Behavior
>(behaviorData
);
5429 Rooted
<CustomSerializableObject
*> obj(
5430 cx
, Create(cx
, static_cast<int32_t>(id
), b
));
5436 if (obj
->behavior() == Behavior::FailDuringRead
) {
5437 JS_ReportErrorASCII(cx
,
5438 "Failed as requested in read during deserialization");
5444 static bool CanTransfer(JSContext
* cx
, JS::Handle
<JSObject
*> wrapped
,
5445 bool* sameProcessScopeRequired
, void* closure
) {
5446 Rooted
<CustomSerializableObject
*> obj(cx
);
5448 if ((obj
= wrapped
->maybeUnwrapIf
<CustomSerializableObject
>())) {
5450 // For now, all CustomSerializable objects are considered to be
5458 static bool WriteTransfer(JSContext
* cx
, JS::Handle
<JSObject
*> aObj
,
5459 void* closure
, uint32_t* tag
,
5460 JS::TransferableOwnership
* ownership
,
5461 void** content
, uint64_t* extraData
) {
5462 Rooted
<CustomSerializableObject
*> obj(cx
);
5464 if ((obj
= aObj
->maybeUnwrapIf
<CustomSerializableObject
>())) {
5465 if (obj
->isDetached()) {
5466 JS_ReportErrorASCII(cx
, "Attempted to transfer detached object");
5470 *content
= reinterpret_cast<void*>(obj
->id());
5471 *extraData
= static_cast<uint64_t>(obj
->behavior());
5473 *ownership
= JS::SCTAG_TMO_CUSTOM
;
5478 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
5479 JSMSG_SC_NOT_TRANSFERABLE
);
5483 static bool ReadTransfer(JSContext
* cx
, JSStructuredCloneReader
* r
,
5484 const JS::CloneDataPolicy
& cloneDataPolicy
,
5485 uint32_t tag
, void* content
, uint64_t extraData
,
5487 JS::MutableHandleObject returnObject
) {
5488 if (tag
== CustomSerializableObject::tag()) {
5489 int32_t id
= int32_t(reinterpret_cast<uintptr_t>(content
));
5490 Rooted
<CustomSerializableObject
*> obj(
5491 cx
, CustomSerializableObject::Create(
5492 cx
, id
, static_cast<Behavior
>(extraData
)));
5497 if (obj
->behavior() == Behavior::FailDuringReadTransfer
) {
5500 returnObject
.set(obj
);
5507 static void FreeTransfer(uint32_t tag
, JS::TransferableOwnership ownership
,
5508 void* content
, uint64_t extraData
, void* closure
) {
5509 CustomSerializableObject::log(uint32_t(reinterpret_cast<intptr_t>(content
)),
5514 MOZ_THREAD_LOCAL(CustomSerializableObject::ActivityLog
*)
5515 CustomSerializableObject::ActivityLog::self
;
5517 static bool MakeSerializable(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5518 CallArgs args
= CallArgsFromVp(argc
, vp
);
5521 if (args
.get(0).isInt32()) {
5522 id
= args
[0].toInt32();
5524 JS_ReportErrorASCII(cx
, "id out of range");
5528 CustomSerializableObject::Behavior behavior
=
5529 CustomSerializableObject::Behavior::Nothing
;
5530 if (args
.get(1).isInt32()) {
5531 int32_t iv
= args
[1].toInt32();
5532 constexpr int32_t min
=
5533 static_cast<int32_t>(CustomSerializableObject::Behavior::Nothing
);
5534 constexpr int32_t max
= static_cast<int32_t>(
5535 CustomSerializableObject::Behavior::FailDuringRead
);
5536 if (iv
< min
|| iv
> max
) {
5537 JS_ReportErrorASCII(cx
, "behavior out of range");
5540 behavior
= static_cast<CustomSerializableObject::Behavior
>(iv
);
5543 JSObject
* obj
= CustomSerializableObject::Create(cx
, id
, behavior
);
5548 args
.rval().setObject(*obj
);
5552 static JSStructuredCloneCallbacks gCloneCallbacks
= {
5553 .read
= CustomSerializableObject::Read
,
5554 .write
= CustomSerializableObject::Write
,
5555 .reportError
= nullptr,
5556 .readTransfer
= CustomSerializableObject::ReadTransfer
,
5557 .writeTransfer
= CustomSerializableObject::WriteTransfer
,
5558 .freeTransfer
= CustomSerializableObject::FreeTransfer
,
5559 .canTransfer
= CustomSerializableObject::CanTransfer
,
5560 .sabCloned
= nullptr};
5562 bool js::testingFunc_serialize(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5563 CallArgs args
= CallArgsFromVp(argc
, vp
);
5565 if (js::SupportDifferentialTesting()) {
5566 RootedObject
callee(cx
, &args
.callee());
5567 ReportUsageErrorASCII(cx
, callee
,
5568 "Function unavailable in differential testing mode.");
5572 mozilla::Maybe
<JSAutoStructuredCloneBuffer
> clonebuf
;
5573 JS::CloneDataPolicy policy
;
5575 if (!args
.get(2).isUndefined()) {
5576 RootedObject
opts(cx
, ToObject(cx
, args
.get(2)));
5582 if (!JS_GetProperty(cx
, opts
, "SharedArrayBuffer", &v
)) {
5586 if (!v
.isUndefined()) {
5587 JSString
* str
= JS::ToString(cx
, v
);
5591 JSLinearString
* poli
= str
->ensureLinear(cx
);
5596 if (StringEqualsLiteral(poli
, "allow")) {
5597 policy
.allowSharedMemoryObjects();
5598 policy
.allowIntraClusterClonableSharedObjects();
5599 } else if (StringEqualsLiteral(poli
, "deny")) {
5602 JS_ReportErrorASCII(cx
, "Invalid policy value for 'SharedArrayBuffer'");
5607 if (!JS_GetProperty(cx
, opts
, "scope", &v
)) {
5611 if (!v
.isUndefined()) {
5612 RootedString
str(cx
, JS::ToString(cx
, v
));
5616 auto scope
= ParseCloneScope(cx
, str
);
5618 JS_ReportErrorASCII(cx
, "Invalid structured clone scope");
5621 clonebuf
.emplace(*scope
, &gCloneCallbacks
, nullptr);
5626 clonebuf
.emplace(JS::StructuredCloneScope::SameProcess
, &gCloneCallbacks
,
5630 if (!clonebuf
->write(cx
, args
.get(0), args
.get(1), policy
)) {
5634 RootedObject
obj(cx
, CloneBufferObject::Create(cx
, clonebuf
.ptr()));
5639 args
.rval().setObject(*obj
);
5643 static bool Deserialize(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5644 CallArgs args
= CallArgsFromVp(argc
, vp
);
5646 if (js::SupportDifferentialTesting()) {
5647 RootedObject
callee(cx
, &args
.callee());
5648 ReportUsageErrorASCII(cx
, callee
,
5649 "Function unavailable in differential testing mode.");
5653 if (!args
.get(0).isObject() || !args
[0].toObject().is
<CloneBufferObject
>()) {
5654 JS_ReportErrorASCII(cx
, "deserialize requires a clonebuffer argument");
5657 Rooted
<CloneBufferObject
*> obj(cx
,
5658 &args
[0].toObject().as
<CloneBufferObject
>());
5660 JS::CloneDataPolicy policy
;
5662 JS::StructuredCloneScope scope
=
5663 obj
->isSynthetic() ? JS::StructuredCloneScope::DifferentProcess
5664 : JS::StructuredCloneScope::SameProcess
;
5665 if (args
.get(1).isObject()) {
5666 RootedObject
opts(cx
, &args
[1].toObject());
5672 if (!JS_GetProperty(cx
, opts
, "SharedArrayBuffer", &v
)) {
5676 if (!v
.isUndefined()) {
5677 JSString
* str
= JS::ToString(cx
, v
);
5681 JSLinearString
* poli
= str
->ensureLinear(cx
);
5686 if (StringEqualsLiteral(poli
, "allow")) {
5687 policy
.allowSharedMemoryObjects();
5688 policy
.allowIntraClusterClonableSharedObjects();
5689 } else if (StringEqualsLiteral(poli
, "deny")) {
5692 JS_ReportErrorASCII(cx
, "Invalid policy value for 'SharedArrayBuffer'");
5697 if (!JS_GetProperty(cx
, opts
, "scope", &v
)) {
5701 if (!v
.isUndefined()) {
5702 RootedString
str(cx
, JS::ToString(cx
, v
));
5706 auto maybeScope
= ParseCloneScope(cx
, str
);
5708 JS_ReportErrorASCII(cx
, "Invalid structured clone scope");
5712 if (*maybeScope
< scope
) {
5713 JS_ReportErrorASCII(cx
,
5714 "Cannot use less restrictive scope "
5715 "than the deserialized clone buffer's scope");
5719 scope
= *maybeScope
;
5723 // Clone buffer was already consumed?
5725 JS_ReportErrorASCII(cx
,
5726 "deserialize given invalid clone buffer "
5727 "(transferables already consumed?)");
5731 bool hasTransferable
;
5732 if (!JS_StructuredCloneHasTransferables(*obj
->data(), &hasTransferable
)) {
5736 RootedValue
deserialized(cx
);
5737 if (!JS_ReadStructuredClone(cx
, *obj
->data(), JS_STRUCTURED_CLONE_VERSION
,
5738 scope
, &deserialized
, policy
, &gCloneCallbacks
,
5742 args
.rval().set(deserialized
);
5744 // Consume any clone buffer with transferables; throw an error if it is
5745 // deserialized again.
5746 if (hasTransferable
) {
5753 static bool DetachArrayBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5754 CallArgs args
= CallArgsFromVp(argc
, vp
);
5756 if (args
.length() != 1) {
5757 JS_ReportErrorASCII(cx
, "detachArrayBuffer() requires a single argument");
5761 if (!args
[0].isObject()) {
5762 JS_ReportErrorASCII(cx
, "detachArrayBuffer must be passed an object");
5766 RootedObject
obj(cx
, &args
[0].toObject());
5767 if (!JS::DetachArrayBuffer(cx
, obj
)) {
5771 args
.rval().setUndefined();
5775 static bool EnsureNonInline(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5776 CallArgs args
= CallArgsFromVp(argc
, vp
);
5778 if (args
.length() != 1) {
5779 JS_ReportErrorASCII(cx
, "ensureNonInline() requires a single argument");
5783 if (!args
[0].isObject()) {
5784 JS_ReportErrorASCII(cx
, "ensureNonInline must be passed an object");
5788 RootedObject
obj(cx
, &args
[0].toObject());
5789 if (!JS::EnsureNonInlineArrayBufferOrView(cx
, obj
)) {
5793 args
.rval().setUndefined();
5797 static bool JSONStringify(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5798 CallArgs args
= CallArgsFromVp(argc
, vp
);
5800 RootedValue
value(cx
, args
.get(0));
5801 RootedValue
behaviorVal(cx
, args
.get(1));
5802 StringifyBehavior behavior
= StringifyBehavior::Normal
;
5803 if (behaviorVal
.isString()) {
5805 #define MATCH(name) \
5806 if (!JS_StringEqualsLiteral(cx, behaviorVal.toString(), #name, &matches)) { \
5810 behavior = StringifyBehavior::name; \
5819 JSStringBuilder
sb(cx
);
5820 if (!Stringify(cx
, &value
, nullptr, UndefinedValue(), sb
, behavior
)) {
5825 JSString
* str
= sb
.finishString();
5829 args
.rval().setString(str
);
5831 args
.rval().setUndefined();
5837 static bool HelperThreadCount(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5838 CallArgs args
= CallArgsFromVp(argc
, vp
);
5840 if (js::SupportDifferentialTesting()) {
5841 // Always return 0 to get consistent output with and without --no-threads.
5842 args
.rval().setInt32(0);
5846 if (CanUseExtraThreads()) {
5847 args
.rval().setInt32(GetHelperThreadCount());
5849 args
.rval().setInt32(0);
5854 static bool EnableShapeConsistencyChecks(JSContext
* cx
, unsigned argc
,
5856 CallArgs args
= CallArgsFromVp(argc
, vp
);
5858 NativeObject::enableShapeConsistencyChecks();
5860 args
.rval().setUndefined();
5864 // ShapeSnapshot holds information about an object's properties. This is used
5865 // for checking object and shape changes between two points in time.
5866 class ShapeSnapshot
{
5867 HeapPtr
<JSObject
*> object_
;
5868 HeapPtr
<Shape
*> shape_
;
5869 HeapPtr
<BaseShape
*> baseShape_
;
5870 ObjectFlags objectFlags_
;
5872 GCVector
<HeapPtr
<Value
>, 8> slots_
;
5874 struct PropertySnapshot
{
5875 HeapPtr
<PropMap
*> propMap
;
5876 uint32_t propMapIndex
;
5877 HeapPtr
<PropertyKey
> key
;
5880 explicit PropertySnapshot(PropMap
* map
, uint32_t index
)
5882 propMapIndex(index
),
5883 key(map
->getKey(index
)),
5884 prop(map
->getPropertyInfo(index
)) {}
5886 void trace(JSTracer
* trc
) {
5887 TraceEdge(trc
, &propMap
, "propMap");
5888 TraceEdge(trc
, &key
, "key");
5891 bool operator==(const PropertySnapshot
& other
) const {
5892 return propMap
== other
.propMap
&& propMapIndex
== other
.propMapIndex
&&
5893 key
== other
.key
&& prop
== other
.prop
;
5895 bool operator!=(const PropertySnapshot
& other
) const {
5896 return !operator==(other
);
5899 GCVector
<PropertySnapshot
, 8> properties_
;
5902 explicit ShapeSnapshot(JSContext
* cx
) : slots_(cx
), properties_(cx
) {}
5903 void checkSelf(JSContext
* cx
) const;
5904 void check(JSContext
* cx
, const ShapeSnapshot
& other
) const;
5905 bool init(JSObject
* obj
);
5906 void trace(JSTracer
* trc
);
5908 JSObject
* object() const { return object_
; }
5911 // A JSObject that holds a ShapeSnapshot.
5912 class ShapeSnapshotObject
: public NativeObject
{
5913 static constexpr size_t SnapshotSlot
= 0;
5914 static constexpr size_t ReservedSlots
= 1;
5917 static const JSClassOps classOps_
;
5918 static const JSClass class_
;
5920 bool hasSnapshot() const {
5921 // The snapshot may not be present yet if we GC during initialization.
5922 return !getReservedSlot(SnapshotSlot
).isUndefined();
5925 ShapeSnapshot
& snapshot() const {
5926 void* ptr
= getReservedSlot(SnapshotSlot
).toPrivate();
5928 return *static_cast<ShapeSnapshot
*>(ptr
);
5931 static ShapeSnapshotObject
* create(JSContext
* cx
, HandleObject obj
);
5933 static void finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
5934 if (obj
->as
<ShapeSnapshotObject
>().hasSnapshot()) {
5935 js_delete(&obj
->as
<ShapeSnapshotObject
>().snapshot());
5938 static void trace(JSTracer
* trc
, JSObject
* obj
) {
5939 if (obj
->as
<ShapeSnapshotObject
>().hasSnapshot()) {
5940 obj
->as
<ShapeSnapshotObject
>().snapshot().trace(trc
);
5945 /*static */ const JSClassOps
ShapeSnapshotObject::classOps_
= {
5946 nullptr, // addProperty
5947 nullptr, // delProperty
5948 nullptr, // enumerate
5949 nullptr, // newEnumerate
5951 nullptr, // mayResolve
5952 ShapeSnapshotObject::finalize
, // finalize
5954 nullptr, // construct
5955 ShapeSnapshotObject::trace
, // trace
5958 /*static */ const JSClass
ShapeSnapshotObject::class_
= {
5959 "ShapeSnapshotObject",
5960 JSCLASS_HAS_RESERVED_SLOTS(ShapeSnapshotObject::ReservedSlots
) |
5961 JSCLASS_BACKGROUND_FINALIZE
,
5962 &ShapeSnapshotObject::classOps_
};
5964 bool ShapeSnapshot::init(JSObject
* obj
) {
5966 shape_
= obj
->shape();
5967 baseShape_
= shape_
->base();
5968 objectFlags_
= shape_
->objectFlags();
5970 if (obj
->is
<NativeObject
>()) {
5971 NativeObject
* nobj
= &obj
->as
<NativeObject
>();
5973 // Snapshot the slot values.
5974 size_t slotSpan
= nobj
->slotSpan();
5975 if (!slots_
.growBy(slotSpan
)) {
5978 for (size_t i
= 0; i
< slotSpan
; i
++) {
5979 slots_
[i
] = nobj
->getSlot(i
);
5982 // Snapshot property information.
5983 if (uint32_t len
= nobj
->shape()->propMapLength(); len
> 0) {
5984 PropMap
* map
= nobj
->shape()->propMap();
5986 for (uint32_t i
= 0; i
< len
; i
++) {
5987 if (!map
->hasKey(i
)) {
5990 if (!properties_
.append(PropertySnapshot(map
, i
))) {
5994 if (!map
->hasPrevious()) {
5997 map
= map
->asLinked()->previous();
5998 len
= PropMap::Capacity
;
6006 void ShapeSnapshot::trace(JSTracer
* trc
) {
6007 TraceEdge(trc
, &object_
, "object");
6008 TraceEdge(trc
, &shape_
, "shape");
6009 TraceEdge(trc
, &baseShape_
, "baseShape");
6011 properties_
.trace(trc
);
6014 void ShapeSnapshot::checkSelf(JSContext
* cx
) const {
6015 // Assertions based on a single snapshot.
6017 // Non-dictionary shapes must not be mutated.
6018 if (!shape_
->isDictionary()) {
6019 MOZ_RELEASE_ASSERT(shape_
->base() == baseShape_
);
6020 MOZ_RELEASE_ASSERT(shape_
->objectFlags() == objectFlags_
);
6023 for (const PropertySnapshot
& propSnapshot
: properties_
) {
6024 PropMap
* propMap
= propSnapshot
.propMap
;
6025 uint32_t propMapIndex
= propSnapshot
.propMapIndex
;
6026 PropertyInfo prop
= propSnapshot
.prop
;
6028 // Skip if the map no longer matches the snapshotted data. This can
6029 // only happen for dictionary maps because they can be mutated or compacted
6030 // after a shape change.
6031 if (!propMap
->hasKey(propMapIndex
) ||
6032 PropertySnapshot(propMap
, propMapIndex
) != propSnapshot
) {
6033 MOZ_RELEASE_ASSERT(propMap
->isDictionary());
6034 MOZ_RELEASE_ASSERT(object_
->shape() != shape_
);
6038 // Ensure ObjectFlags depending on property information are set if needed.
6039 ObjectFlags expectedFlags
= GetObjectFlagsForNewProperty(
6040 shape_
->getObjectClass(), shape_
->objectFlags(), propSnapshot
.key
,
6042 MOZ_RELEASE_ASSERT(expectedFlags
== objectFlags_
);
6044 // Accessors must have a PrivateGCThingValue(GetterSetter*) slot value.
6045 if (prop
.isAccessorProperty()) {
6046 Value slotVal
= slots_
[prop
.slot()];
6047 MOZ_RELEASE_ASSERT(slotVal
.isPrivateGCThing());
6048 MOZ_RELEASE_ASSERT(slotVal
.toGCThing()->is
<GetterSetter
>());
6051 // Data properties must not have a PrivateGCThingValue slot value.
6052 if (prop
.isDataProperty()) {
6053 Value slotVal
= slots_
[prop
.slot()];
6054 MOZ_RELEASE_ASSERT(!slotVal
.isPrivateGCThing());
6059 void ShapeSnapshot::check(JSContext
* cx
, const ShapeSnapshot
& later
) const {
6061 later
.checkSelf(cx
);
6063 if (object_
!= later
.object_
) {
6064 // Snapshots are for different objects. Assert dictionary shapes aren't
6066 if (object_
->is
<NativeObject
>()) {
6067 NativeObject
* nobj
= &object_
->as
<NativeObject
>();
6068 if (nobj
->inDictionaryMode()) {
6069 MOZ_RELEASE_ASSERT(shape_
!= later
.shape_
);
6075 // We have two snapshots for the same object. Check the shape information
6076 // wasn't changed in invalid ways.
6078 // If the Shape is still the same, the object must have the same BaseShape,
6079 // ObjectFlags and property information.
6080 if (shape_
== later
.shape_
) {
6081 MOZ_RELEASE_ASSERT(objectFlags_
== later
.objectFlags_
);
6082 MOZ_RELEASE_ASSERT(baseShape_
== later
.baseShape_
);
6083 MOZ_RELEASE_ASSERT(slots_
.length() == later
.slots_
.length());
6084 MOZ_RELEASE_ASSERT(properties_
.length() == later
.properties_
.length());
6086 for (size_t i
= 0; i
< properties_
.length(); i
++) {
6087 MOZ_RELEASE_ASSERT(properties_
[i
] == later
.properties_
[i
]);
6088 // Non-configurable accessor properties and non-configurable, non-writable
6089 // data properties shouldn't have had their slot mutated.
6090 PropertyInfo prop
= properties_
[i
].prop
;
6091 if (!prop
.configurable()) {
6092 if (prop
.isAccessorProperty() ||
6093 (prop
.isDataProperty() && !prop
.writable())) {
6094 size_t slot
= prop
.slot();
6095 MOZ_RELEASE_ASSERT(slots_
[slot
] == later
.slots_
[slot
]);
6101 // Object flags should not be lost. The exception is the Indexed flag, it
6102 // can be cleared when densifying elements, so clear that flag first.
6104 ObjectFlags flags
= objectFlags_
;
6105 ObjectFlags flagsLater
= later
.objectFlags_
;
6106 flags
.clearFlag(ObjectFlag::Indexed
);
6107 flagsLater
.clearFlag(ObjectFlag::Indexed
);
6108 MOZ_RELEASE_ASSERT((flags
.toRaw() & flagsLater
.toRaw()) == flags
.toRaw());
6111 // If the HadGetterSetterChange flag wasn't set, all GetterSetter slots must
6113 if (!later
.objectFlags_
.hasFlag(ObjectFlag::HadGetterSetterChange
)) {
6114 for (size_t i
= 0; i
< slots_
.length(); i
++) {
6115 if (slots_
[i
].isPrivateGCThing() &&
6116 slots_
[i
].toGCThing()->is
<GetterSetter
>()) {
6117 MOZ_RELEASE_ASSERT(i
< later
.slots_
.length());
6118 MOZ_RELEASE_ASSERT(later
.slots_
[i
] == slots_
[i
]);
6125 ShapeSnapshotObject
* ShapeSnapshotObject::create(JSContext
* cx
,
6127 Rooted
<UniquePtr
<ShapeSnapshot
>> snapshot(cx
,
6128 cx
->make_unique
<ShapeSnapshot
>(cx
));
6129 if (!snapshot
|| !snapshot
->init(obj
)) {
6133 auto* snapshotObj
= NewObjectWithGivenProto
<ShapeSnapshotObject
>(cx
, nullptr);
6137 snapshotObj
->initReservedSlot(SnapshotSlot
, PrivateValue(snapshot
.release()));
6141 static bool CreateShapeSnapshot(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6142 CallArgs args
= CallArgsFromVp(argc
, vp
);
6144 if (!args
.get(0).isObject()) {
6145 JS_ReportErrorASCII(cx
, "createShapeSnapshot requires an object argument");
6149 RootedObject
obj(cx
, &args
[0].toObject());
6150 auto* res
= ShapeSnapshotObject::create(cx
, obj
);
6155 res
->snapshot().check(cx
, res
->snapshot());
6157 args
.rval().setObject(*res
);
6161 static bool CheckShapeSnapshot(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6162 CallArgs args
= CallArgsFromVp(argc
, vp
);
6164 if (!args
.get(0).isObject() ||
6165 !args
[0].toObject().is
<ShapeSnapshotObject
>()) {
6166 JS_ReportErrorASCII(cx
, "checkShapeSnapshot requires a snapshot argument");
6170 // Get the object to use from the snapshot if the second argument is not an
6172 RootedObject
obj(cx
);
6173 if (args
.get(1).isObject()) {
6174 obj
= &args
[1].toObject();
6176 auto& snapshot
= args
[0].toObject().as
<ShapeSnapshotObject
>().snapshot();
6177 obj
= snapshot
.object();
6180 RootedObject
otherSnapshot(cx
, ShapeSnapshotObject::create(cx
, obj
));
6181 if (!otherSnapshot
) {
6185 auto& snapshot1
= args
[0].toObject().as
<ShapeSnapshotObject
>().snapshot();
6186 auto& snapshot2
= otherSnapshot
->as
<ShapeSnapshotObject
>().snapshot();
6187 snapshot1
.check(cx
, snapshot2
);
6189 args
.rval().setUndefined();
6193 #if defined(DEBUG) || defined(JS_JITSPEW)
6194 static bool DumpObject(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6195 CallArgs args
= CallArgsFromVp(argc
, vp
);
6196 RootedObject
obj(cx
, ToObject(cx
, args
.get(0)));
6203 args
.rval().setUndefined();
6208 static bool SharedMemoryEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6209 CallArgs args
= CallArgsFromVp(argc
, vp
);
6210 args
.rval().setBoolean(
6211 cx
->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
6215 static bool SharedArrayRawBufferRefcount(JSContext
* cx
, unsigned argc
,
6217 CallArgs args
= CallArgsFromVp(argc
, vp
);
6218 if (args
.length() != 1 || !args
[0].isObject()) {
6219 JS_ReportErrorASCII(cx
, "Expected SharedArrayBuffer object");
6222 RootedObject
obj(cx
, &args
[0].toObject());
6223 if (!obj
->is
<SharedArrayBufferObject
>()) {
6224 JS_ReportErrorASCII(cx
, "Expected SharedArrayBuffer object");
6227 args
.rval().setInt32(
6228 obj
->as
<SharedArrayBufferObject
>().rawBufferObject()->refcount());
6232 #ifdef NIGHTLY_BUILD
6233 static bool ObjectAddress(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6234 CallArgs args
= CallArgsFromVp(argc
, vp
);
6236 if (js::SupportDifferentialTesting()) {
6237 RootedObject
callee(cx
, &args
.callee());
6238 ReportUsageErrorASCII(cx
, callee
,
6239 "Function unavailable in differential testing mode.");
6243 if (args
.length() != 1) {
6244 RootedObject
callee(cx
, &args
.callee());
6245 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
6248 if (!args
[0].isObject()) {
6249 RootedObject
callee(cx
, &args
.callee());
6250 ReportUsageErrorASCII(cx
, callee
, "Expected object");
6254 void* ptr
= js::UncheckedUnwrap(&args
[0].toObject(), true);
6256 SprintfLiteral(buffer
, "%p", ptr
);
6258 return ReturnStringCopy(cx
, args
, buffer
);
6261 static bool SharedAddress(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6262 CallArgs args
= CallArgsFromVp(argc
, vp
);
6264 if (js::SupportDifferentialTesting()) {
6265 RootedObject
callee(cx
, &args
.callee());
6266 ReportUsageErrorASCII(cx
, callee
,
6267 "Function unavailable in differential testing mode.");
6271 if (args
.length() != 1) {
6272 RootedObject
callee(cx
, &args
.callee());
6273 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
6276 if (!args
[0].isObject()) {
6277 RootedObject
callee(cx
, &args
.callee());
6278 ReportUsageErrorASCII(cx
, callee
, "Expected object");
6282 RootedObject
obj(cx
, CheckedUnwrapStatic(&args
[0].toObject()));
6284 ReportAccessDenied(cx
);
6287 if (!obj
->is
<SharedArrayBufferObject
>()) {
6288 JS_ReportErrorASCII(cx
, "Argument must be a SharedArrayBuffer");
6292 uint32_t nchar
= SprintfLiteral(
6294 obj
->as
<SharedArrayBufferObject
>().dataPointerShared().unwrap(
6297 JSString
* str
= JS_NewStringCopyN(cx
, buffer
, nchar
);
6302 args
.rval().setString(str
);
6308 static bool HasInvalidatedTeleporting(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6309 CallArgs args
= CallArgsFromVp(argc
, vp
);
6311 if (args
.length() != 1 || !args
[0].isObject()) {
6312 RootedObject
callee(cx
, &args
.callee());
6313 ReportUsageErrorASCII(cx
, callee
, "Expected single object argument");
6317 args
.rval().setBoolean(args
[0].toObject().hasInvalidatedTeleporting());
6321 static bool DumpBacktrace(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6322 CallArgs args
= CallArgsFromVp(argc
, vp
);
6324 args
.rval().setUndefined();
6328 static bool GetBacktrace(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6329 CallArgs args
= CallArgsFromVp(argc
, vp
);
6331 bool showArgs
= false;
6332 bool showLocals
= false;
6333 bool showThisProps
= false;
6335 if (args
.length() > 1) {
6336 RootedObject
callee(cx
, &args
.callee());
6337 ReportUsageErrorASCII(cx
, callee
, "Too many arguments");
6341 if (args
.length() == 1) {
6342 RootedObject
cfg(cx
, ToObject(cx
, args
[0]));
6348 if (!JS_GetProperty(cx
, cfg
, "args", &v
)) {
6351 showArgs
= ToBoolean(v
);
6353 if (!JS_GetProperty(cx
, cfg
, "locals", &v
)) {
6356 showLocals
= ToBoolean(v
);
6358 if (!JS_GetProperty(cx
, cfg
, "thisprops", &v
)) {
6361 showThisProps
= ToBoolean(v
);
6364 JS::UniqueChars buf
=
6365 JS::FormatStackDump(cx
, showArgs
, showLocals
, showThisProps
);
6371 UniqueTwoByteChars
ucbuf(JS::LossyUTF8CharsToNewTwoByteCharsZ(
6372 cx
, JS::UTF8Chars(buf
.get(), strlen(buf
.get())),
6373 &len
, js::MallocArena
)
6378 JSString
* str
= JS_NewUCStringCopyN(cx
, ucbuf
.get(), len
);
6383 args
.rval().setString(str
);
6387 static bool ReportOutOfMemory(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6388 CallArgs args
= CallArgsFromVp(argc
, vp
);
6389 JS_ReportOutOfMemory(cx
);
6390 cx
->clearPendingException();
6391 args
.rval().setUndefined();
6395 static bool ThrowOutOfMemory(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6396 JS_ReportOutOfMemory(cx
);
6400 static bool ReportLargeAllocationFailure(JSContext
* cx
, unsigned argc
,
6402 CallArgs args
= CallArgsFromVp(argc
, vp
);
6404 size_t bytes
= JSRuntime::LARGE_ALLOCATION
;
6405 if (args
.length() >= 1) {
6406 if (!args
[0].isInt32()) {
6407 RootedObject
callee(cx
, &args
.callee());
6408 ReportUsageErrorASCII(cx
, callee
,
6409 "First argument must be an integer if specified.");
6412 bytes
= args
[0].toInt32();
6415 void* buf
= cx
->runtime()->onOutOfMemoryCanGC(AllocFunction::Malloc
,
6416 js::MallocArena
, bytes
);
6419 args
.rval().setUndefined();
6423 namespace heaptools
{
6425 using EdgeName
= UniqueTwoByteChars
;
6427 // An edge to a node from its predecessor in a path through the graph.
6429 // The node from which this edge starts.
6430 JS::ubi::Node predecessor_
;
6432 // The name of this edge.
6436 BackEdge() : name_(nullptr) {}
6437 // Construct an initialized back edge, taking ownership of |name|.
6438 BackEdge(JS::ubi::Node predecessor
, EdgeName name
)
6439 : predecessor_(predecessor
), name_(std::move(name
)) {}
6440 BackEdge(BackEdge
&& rhs
)
6441 : predecessor_(rhs
.predecessor_
), name_(std::move(rhs
.name_
)) {}
6442 BackEdge
& operator=(BackEdge
&& rhs
) {
6443 MOZ_ASSERT(&rhs
!= this);
6445 new (this) BackEdge(std::move(rhs
));
6449 EdgeName
forgetName() { return std::move(name_
); }
6450 JS::ubi::Node
predecessor() const { return predecessor_
; }
6453 // No copy constructor or copying assignment.
6454 BackEdge(const BackEdge
&) = delete;
6455 BackEdge
& operator=(const BackEdge
&) = delete;
6458 // A path-finding handler class for use with JS::ubi::BreadthFirst.
6459 struct FindPathHandler
{
6460 using NodeData
= BackEdge
;
6461 using Traversal
= JS::ubi::BreadthFirst
<FindPathHandler
>;
6463 FindPathHandler(JSContext
* cx
, JS::ubi::Node start
, JS::ubi::Node target
,
6464 MutableHandle
<GCVector
<Value
>> nodes
, Vector
<EdgeName
>& edges
)
6472 bool operator()(Traversal
& traversal
, JS::ubi::Node origin
,
6473 const JS::ubi::Edge
& edge
, BackEdge
* backEdge
, bool first
) {
6474 // We take care of each node the first time we visit it, so there's
6475 // nothing to be done on subsequent visits.
6480 // Record how we reached this node. This is the last edge on a
6481 // shortest path to this node.
6483 DuplicateStringToArena(js::StringBufferArena
, cx
, edge
.name
.get());
6487 *backEdge
= BackEdge(origin
, std::move(edgeName
));
6489 // Have we reached our final target node?
6490 if (edge
.referent
== target
) {
6491 // Record the path that got us here, which must be a shortest path.
6492 if (!recordPath(traversal
, backEdge
)) {
6502 // We've found a path to our target. Walk the backlinks to produce the
6503 // (reversed) path, saving the path in |nodes| and |edges|. |nodes| is
6504 // rooted, so it can hold the path's nodes as we leave the scope of
6505 // the AutoCheckCannotGC. Note that nodes are added to |visited| after we
6506 // return from operator() so we have to pass the target BackEdge* to this
6508 bool recordPath(Traversal
& traversal
, BackEdge
* targetBackEdge
) {
6509 JS::ubi::Node here
= target
;
6512 BackEdge
* backEdge
= targetBackEdge
;
6513 if (here
!= target
) {
6514 Traversal::NodeMap::Ptr p
= traversal
.visited
.lookup(here
);
6516 backEdge
= &p
->value();
6518 JS::ubi::Node predecessor
= backEdge
->predecessor();
6519 if (!nodes
.append(predecessor
.exposeToJS()) ||
6520 !edges
.append(backEdge
->forgetName())) {
6524 } while (here
!= start
);
6531 // The node we're starting from.
6532 JS::ubi::Node start
;
6534 // The node we're looking for.
6535 JS::ubi::Node target
;
6537 // True if we found a path to target, false if we didn't.
6540 // The nodes and edges of the path --- should we find one. The path is
6541 // stored in reverse order, because that's how it's easiest for us to
6543 // - edges[i] is the name of the edge from nodes[i] to nodes[i-1].
6544 // - edges[0] is the name of the edge from nodes[0] to the target.
6545 // - The last node, nodes[n-1], is the start node.
6546 MutableHandle
<GCVector
<Value
>> nodes
;
6547 Vector
<EdgeName
>& edges
;
6550 } // namespace heaptools
6552 static bool FindPath(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6553 CallArgs args
= CallArgsFromVp(argc
, vp
);
6554 if (!args
.requireAtLeast(cx
, "findPath", 2)) {
6558 // We don't ToString non-objects given as 'start' or 'target', because this
6559 // test is all about object identity, and ToString doesn't preserve that.
6560 // Non-GCThing endpoints don't make much sense.
6561 if (!args
[0].isObject() && !args
[0].isString() && !args
[0].isSymbol()) {
6562 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[0],
6563 nullptr, "not an object, string, or symbol");
6567 if (!args
[1].isObject() && !args
[1].isString() && !args
[1].isSymbol()) {
6568 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[0],
6569 nullptr, "not an object, string, or symbol");
6573 Rooted
<GCVector
<Value
>> nodes(cx
, GCVector
<Value
>(cx
));
6574 Vector
<heaptools::EdgeName
> edges(cx
);
6577 // We can't tolerate the GC moving things around while we're searching
6578 // the heap. Check that nothing we do causes a GC.
6579 JS::AutoCheckCannotGC autoCannotGC
;
6581 JS::ubi::Node
start(args
[0]), target(args
[1]);
6583 heaptools::FindPathHandler
handler(cx
, start
, target
, &nodes
, edges
);
6584 heaptools::FindPathHandler::Traversal
traversal(cx
, handler
, autoCannotGC
);
6585 if (!traversal
.addStart(start
)) {
6586 ReportOutOfMemory(cx
);
6590 if (!traversal
.traverse()) {
6591 if (!cx
->isExceptionPending()) {
6592 ReportOutOfMemory(cx
);
6597 if (!handler
.foundPath
) {
6598 // We didn't find any paths from the start to the target.
6599 args
.rval().setUndefined();
6604 // |nodes| and |edges| contain the path from |start| to |target|, reversed.
6605 // Construct a JavaScript array describing the path from the start to the
6606 // target. Each element has the form:
6609 // node: <object or string or symbol>,
6610 // edge: <string describing outgoing edge from node>
6613 // or, if the node is some internal thing that isn't a proper JavaScript
6616 // { node: undefined, edge: <string> }
6617 size_t length
= nodes
.length();
6618 Rooted
<ArrayObject
*> result(cx
, NewDenseFullyAllocatedArray(cx
, length
));
6622 result
->ensureDenseInitializedLength(0, length
);
6624 // Walk |nodes| and |edges| in the stored order, and construct the result
6625 // array in start-to-target order.
6626 for (size_t i
= 0; i
< length
; i
++) {
6627 // Build an object describing the node and edge.
6628 RootedObject
obj(cx
, NewPlainObject(cx
));
6633 // Only define the "node" property if we're not fuzzing, to prevent the
6634 // fuzzers from messing with internal objects that we don't want to expose
6637 RootedValue
wrapped(cx
, nodes
[i
]);
6638 if (!cx
->compartment()->wrap(cx
, &wrapped
)) {
6642 if (!JS_DefineProperty(cx
, obj
, "node", wrapped
, JSPROP_ENUMERATE
)) {
6647 heaptools::EdgeName edgeName
= std::move(edges
[i
]);
6649 size_t edgeNameLength
= js_strlen(edgeName
.get());
6650 RootedString
edgeStr(
6651 cx
, NewString
<CanGC
>(cx
, std::move(edgeName
), edgeNameLength
));
6656 if (!JS_DefineProperty(cx
, obj
, "edge", edgeStr
, JSPROP_ENUMERATE
)) {
6660 result
->setDenseElement(length
- i
- 1, ObjectValue(*obj
));
6663 args
.rval().setObject(*result
);
6667 static bool ShortestPaths(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6668 CallArgs args
= CallArgsFromVp(argc
, vp
);
6669 if (!args
.requireAtLeast(cx
, "shortestPaths", 1)) {
6673 if (!args
[0].isObject() || !args
[0].toObject().is
<ArrayObject
>()) {
6674 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[0],
6675 nullptr, "not an array object");
6679 Rooted
<ArrayObject
*> objs(cx
, &args
[0].toObject().as
<ArrayObject
>());
6681 RootedValue
start(cx
, NullValue());
6682 int32_t maxNumPaths
= 3;
6684 if (!args
.get(1).isUndefined()) {
6685 if (!args
[1].isObject()) {
6686 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[1],
6687 nullptr, "not an options object");
6691 RootedObject
options(cx
, &args
[1].toObject());
6693 if (!JS_HasProperty(cx
, options
, "start", &exists
)) {
6697 if (!JS_GetProperty(cx
, options
, "start", &start
)) {
6701 // Non-GCThing endpoints don't make much sense.
6702 if (!start
.isGCThing()) {
6703 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, start
,
6704 nullptr, "not a GC thing");
6709 RootedValue
v(cx
, Int32Value(maxNumPaths
));
6710 if (!JS_HasProperty(cx
, options
, "maxNumPaths", &exists
)) {
6714 if (!JS_GetProperty(cx
, options
, "maxNumPaths", &v
)) {
6717 if (!JS::ToInt32(cx
, v
, &maxNumPaths
)) {
6721 if (maxNumPaths
<= 0) {
6722 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, v
,
6723 nullptr, "not greater than 0");
6728 // Ensure we have at least one target.
6729 size_t length
= objs
->getDenseInitializedLength();
6731 ReportValueError(cx
, JSMSG_UNEXPECTED_TYPE
, JSDVG_SEARCH_STACK
, args
[0],
6733 "not a dense array object with one or more elements");
6737 for (size_t i
= 0; i
< length
; i
++) {
6738 RootedValue
el(cx
, objs
->getDenseElement(i
));
6739 if (!el
.isGCThing()) {
6740 JS_ReportErrorASCII(cx
, "Each target must be a GC thing");
6745 // We accumulate the results into a GC-stable form, due to the fact that the
6746 // JS::ubi::ShortestPaths lifetime (when operating on the live heap graph)
6747 // is bounded within an AutoCheckCannotGC.
6748 Rooted
<GCVector
<GCVector
<GCVector
<Value
>>>> values(
6749 cx
, GCVector
<GCVector
<GCVector
<Value
>>>(cx
));
6750 Vector
<Vector
<Vector
<JS::ubi::EdgeName
>>> names(cx
);
6755 JS::ubi::RootList
rootList(cx
, true);
6756 if (start
.isNull()) {
6757 auto [ok
, nogc
] = rootList
.init();
6758 (void)nogc
; // Old compilers get anxious about nogc being unused.
6760 ReportOutOfMemory(cx
);
6763 root
= JS::ubi::Node(&rootList
);
6765 root
= JS::ubi::Node(start
);
6767 JS::AutoCheckCannotGC
noGC(cx
);
6769 JS::ubi::NodeSet targets
;
6771 for (size_t i
= 0; i
< length
; i
++) {
6772 RootedValue
val(cx
, objs
->getDenseElement(i
));
6773 JS::ubi::Node
node(val
);
6774 if (!targets
.put(node
)) {
6775 ReportOutOfMemory(cx
);
6780 auto maybeShortestPaths
= JS::ubi::ShortestPaths::Create(
6781 cx
, noGC
, maxNumPaths
, root
, std::move(targets
));
6782 if (maybeShortestPaths
.isNothing()) {
6783 ReportOutOfMemory(cx
);
6786 auto& shortestPaths
= *maybeShortestPaths
;
6788 for (size_t i
= 0; i
< length
; i
++) {
6789 if (!values
.append(GCVector
<GCVector
<Value
>>(cx
)) ||
6790 !names
.append(Vector
<Vector
<JS::ubi::EdgeName
>>(cx
))) {
6794 RootedValue
val(cx
, objs
->getDenseElement(i
));
6795 JS::ubi::Node
target(val
);
6797 bool ok
= shortestPaths
.forEachPath(target
, [&](JS::ubi::Path
& path
) {
6798 Rooted
<GCVector
<Value
>> pathVals(cx
, GCVector
<Value
>(cx
));
6799 Vector
<JS::ubi::EdgeName
> pathNames(cx
);
6801 for (auto& part
: path
) {
6802 if (!pathVals
.append(part
->predecessor().exposeToJS()) ||
6803 !pathNames
.append(std::move(part
->name()))) {
6808 return values
.back().append(std::move(pathVals
.get())) &&
6809 names
.back().append(std::move(pathNames
));
6818 MOZ_ASSERT(values
.length() == names
.length());
6819 MOZ_ASSERT(values
.length() == length
);
6821 Rooted
<ArrayObject
*> results(cx
, NewDenseFullyAllocatedArray(cx
, length
));
6825 results
->ensureDenseInitializedLength(0, length
);
6827 for (size_t i
= 0; i
< length
; i
++) {
6828 size_t numPaths
= values
[i
].length();
6829 MOZ_ASSERT(names
[i
].length() == numPaths
);
6831 Rooted
<ArrayObject
*> pathsArray(cx
,
6832 NewDenseFullyAllocatedArray(cx
, numPaths
));
6836 pathsArray
->ensureDenseInitializedLength(0, numPaths
);
6838 for (size_t j
= 0; j
< numPaths
; j
++) {
6839 size_t pathLength
= values
[i
][j
].length();
6840 MOZ_ASSERT(names
[i
][j
].length() == pathLength
);
6842 Rooted
<ArrayObject
*> path(cx
,
6843 NewDenseFullyAllocatedArray(cx
, pathLength
));
6847 path
->ensureDenseInitializedLength(0, pathLength
);
6849 for (size_t k
= 0; k
< pathLength
; k
++) {
6850 Rooted
<PlainObject
*> part(cx
, NewPlainObject(cx
));
6855 RootedValue
predecessor(cx
, values
[i
][j
][k
]);
6856 if (!cx
->compartment()->wrap(cx
, &predecessor
) ||
6857 !JS_DefineProperty(cx
, part
, "predecessor", predecessor
,
6858 JSPROP_ENUMERATE
)) {
6862 if (names
[i
][j
][k
]) {
6863 RootedString
edge(cx
,
6864 NewStringCopyZ
<CanGC
>(cx
, names
[i
][j
][k
].get()));
6866 !JS_DefineProperty(cx
, part
, "edge", edge
, JSPROP_ENUMERATE
)) {
6871 path
->setDenseElement(k
, ObjectValue(*part
));
6874 pathsArray
->setDenseElement(j
, ObjectValue(*path
));
6877 results
->setDenseElement(i
, ObjectValue(*pathsArray
));
6880 args
.rval().setObject(*results
);
6884 static bool EvalReturningScope(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6885 CallArgs args
= CallArgsFromVp(argc
, vp
);
6886 if (!args
.requireAtLeast(cx
, "evalReturningScope", 1)) {
6890 RootedString
str(cx
, ToString(cx
, args
[0]));
6895 JS::AutoFilename filename
;
6898 JS::DescribeScriptedCaller(cx
, &filename
, &lineno
);
6900 // CompileOption should be created in the target global's realm.
6901 RootedObject
global(cx
);
6902 Maybe
<JS::CompileOptions
> maybeOptions
;
6903 if (args
.hasDefined(1)) {
6904 global
= ToObject(cx
, args
[1]);
6909 global
= CheckedUnwrapDynamic(global
, cx
, /* stopAtWindowProxy = */ false);
6911 JS_ReportErrorASCII(cx
, "Permission denied to access global");
6914 if (!global
->is
<GlobalObject
>()) {
6915 JS_ReportErrorASCII(cx
, "Argument must be a global object");
6919 JSAutoRealm
ar(cx
, global
);
6920 maybeOptions
.emplace(cx
);
6922 global
= JS::CurrentGlobalOrNull(cx
);
6923 maybeOptions
.emplace(cx
);
6926 CompileOptions
& options
= maybeOptions
.ref();
6927 options
.setFileAndLine(filename
.get(), lineno
);
6928 options
.setNoScriptRval(true);
6929 options
.setNonSyntacticScope(true);
6931 AutoStableStringChars
linearChars(cx
);
6932 if (!linearChars
.initTwoByte(cx
, str
)) {
6935 JS::SourceText
<char16_t
> srcBuf
;
6936 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
6940 RootedObject
varObj(cx
);
6943 // ExecuteInFrameScriptEnvironment requires the script be in the same
6944 // realm as the global. The script compilation should be done after
6945 // switching globals.
6946 AutoRealm
ar(cx
, global
);
6948 RootedScript
script(cx
, JS::Compile(cx
, options
, srcBuf
));
6953 JS::RootedObject
obj(cx
, JS_NewPlainObject(cx
));
6958 RootedObject
lexicalScope(cx
);
6959 if (!js::ExecuteInFrameScriptEnvironment(cx
, obj
, script
, &lexicalScope
)) {
6963 varObj
= lexicalScope
->enclosingEnvironment()->enclosingEnvironment();
6964 MOZ_ASSERT(varObj
->is
<NonSyntacticVariablesObject
>());
6967 RootedValue
varObjVal(cx
, ObjectValue(*varObj
));
6968 if (!cx
->compartment()->wrap(cx
, &varObjVal
)) {
6972 args
.rval().set(varObjVal
);
6976 static bool ByteSize(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6977 CallArgs args
= CallArgsFromVp(argc
, vp
);
6978 mozilla::MallocSizeOf mallocSizeOf
= cx
->runtime()->debuggerMallocSizeOf
;
6981 // We can't tolerate the GC moving things around while we're using a
6982 // ubi::Node. Check that nothing we do causes a GC.
6983 JS::AutoCheckCannotGC autoCannotGC
;
6985 JS::ubi::Node node
= args
.get(0);
6987 args
.rval().setNumber(uint32_t(node
.size(mallocSizeOf
)));
6989 args
.rval().setUndefined();
6995 static bool ByteSizeOfScript(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6996 CallArgs args
= CallArgsFromVp(argc
, vp
);
6997 if (!args
.requireAtLeast(cx
, "byteSizeOfScript", 1)) {
7000 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
7001 JS_ReportErrorASCII(cx
, "Argument must be a Function object");
7005 RootedFunction
fun(cx
, &args
[0].toObject().as
<JSFunction
>());
7006 if (fun
->isNativeFun()) {
7007 JS_ReportErrorASCII(cx
, "Argument must be a scripted function");
7011 RootedScript
script(cx
, JSFunction::getOrCreateScript(cx
, fun
));
7016 mozilla::MallocSizeOf mallocSizeOf
= cx
->runtime()->debuggerMallocSizeOf
;
7019 // We can't tolerate the GC moving things around while we're using a
7020 // ubi::Node. Check that nothing we do causes a GC.
7021 JS::AutoCheckCannotGC autoCannotGC
;
7023 JS::ubi::Node node
= script
;
7025 args
.rval().setNumber(uint32_t(node
.size(mallocSizeOf
)));
7027 args
.rval().setUndefined();
7033 static bool SetImmutablePrototype(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7034 CallArgs args
= CallArgsFromVp(argc
, vp
);
7035 if (!args
.get(0).isObject()) {
7036 JS_ReportErrorASCII(cx
, "setImmutablePrototype: object expected");
7040 RootedObject
obj(cx
, &args
[0].toObject());
7043 if (!js::SetImmutablePrototype(cx
, obj
, &succeeded
)) {
7047 args
.rval().setBoolean(succeeded
);
7052 static bool DumpStringRepresentation(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7053 CallArgs args
= CallArgsFromVp(argc
, vp
);
7055 RootedString
str(cx
, ToString(cx
, args
.get(0)));
7060 Fprinter
out(stderr
);
7061 str
->dumpRepresentation(out
, 0);
7063 args
.rval().setUndefined();
7067 static bool GetStringRepresentation(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7068 CallArgs args
= CallArgsFromVp(argc
, vp
);
7070 RootedString
str(cx
, ToString(cx
, args
.get(0)));
7079 str
->dumpRepresentation(out
, 0);
7081 JSString
* rep
= out
.release(cx
);
7086 args
.rval().setString(rep
);
7092 static bool ParseCompileOptionsForModule(JSContext
* cx
,
7093 JS::CompileOptions
& options
,
7094 JS::Handle
<JSObject
*> opts
,
7096 JS::Rooted
<JS::Value
> v(cx
);
7098 if (!JS_GetProperty(cx
, opts
, "module", &v
)) {
7101 if (!v
.isUndefined() && JS::ToBoolean(v
)) {
7102 options
.setModule();
7105 // js::ParseCompileOptions should already be called.
7106 if (options
.lineno
== 0) {
7107 JS_ReportErrorASCII(cx
, "Module cannot be compiled with lineNumber == 0");
7117 static bool ParseCompileOptionsForInstantiate(JSContext
* cx
,
7118 JS::CompileOptions
& options
,
7119 JS::Handle
<JSObject
*> opts
,
7120 bool& prepareForInstantiate
) {
7121 JS::Rooted
<JS::Value
> v(cx
);
7123 if (!JS_GetProperty(cx
, opts
, "prepareForInstantiate", &v
)) {
7126 if (!v
.isUndefined()) {
7127 prepareForInstantiate
= JS::ToBoolean(v
);
7129 prepareForInstantiate
= false;
7135 static bool CompileToStencil(JSContext
* cx
, uint32_t argc
, Value
* vp
) {
7136 CallArgs args
= CallArgsFromVp(argc
, vp
);
7138 if (!args
.requireAtLeast(cx
, "compileToStencil", 1)) {
7141 if (!args
[0].isString()) {
7142 const char* typeName
= InformalValueTypeName(args
[0]);
7143 JS_ReportErrorASCII(cx
, "expected string to parse, got %s", typeName
);
7147 RootedString
src(cx
, ToString
<CanGC
>(cx
, args
[0]));
7152 /* Linearize the string to obtain a char16_t* range. */
7153 AutoStableStringChars
linearChars(cx
);
7154 if (!linearChars
.initTwoByte(cx
, src
)) {
7157 JS::SourceText
<char16_t
> srcBuf
;
7158 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
7162 CompileOptions
options(cx
);
7163 RootedString
displayURL(cx
);
7164 RootedString
sourceMapURL(cx
);
7165 UniqueChars fileNameBytes
;
7166 bool isModule
= false;
7167 bool prepareForInstantiate
= false;
7168 if (args
.length() == 2) {
7169 if (!args
[1].isObject()) {
7170 JS_ReportErrorASCII(
7171 cx
, "compileToStencil: The 2nd argument must be an object");
7175 RootedObject
opts(cx
, &args
[1].toObject());
7177 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
7180 if (!ParseCompileOptionsForModule(cx
, options
, opts
, isModule
)) {
7183 if (!ParseCompileOptionsForInstantiate(cx
, options
, opts
,
7184 prepareForInstantiate
)) {
7187 if (!js::ParseSourceOptions(cx
, opts
, &displayURL
, &sourceMapURL
)) {
7192 AutoReportFrontendContext
fc(cx
);
7193 RefPtr
<JS::Stencil
> stencil
;
7194 JS::CompilationStorage compileStorage
;
7197 JS::CompileModuleScriptToStencil(&fc
, options
, srcBuf
, compileStorage
);
7200 JS::CompileGlobalScriptToStencil(&fc
, options
, srcBuf
, compileStorage
);
7206 if (!SetSourceOptions(cx
, &fc
, stencil
->source
, displayURL
, sourceMapURL
)) {
7210 JS::InstantiationStorage storage
;
7211 if (prepareForInstantiate
) {
7212 if (!JS::PrepareForInstantiate(&fc
, *stencil
, storage
)) {
7217 Rooted
<js::StencilObject
*> stencilObj(
7218 cx
, js::StencilObject::create(cx
, std::move(stencil
)));
7223 args
.rval().setObject(*stencilObj
);
7227 static bool EvalStencil(JSContext
* cx
, uint32_t argc
, Value
* vp
) {
7228 CallArgs args
= CallArgsFromVp(argc
, vp
);
7230 if (!args
.requireAtLeast(cx
, "evalStencil", 1)) {
7234 /* Prepare the input byte array. */
7235 if (!args
[0].isObject()) {
7236 JS_ReportErrorASCII(cx
, "evalStencil: Stencil object expected");
7239 Rooted
<js::StencilObject
*> stencilObj(
7240 cx
, args
[0].toObject().maybeUnwrapIf
<js::StencilObject
>());
7242 JS_ReportErrorASCII(cx
, "evalStencil: Stencil object expected");
7246 if (stencilObj
->stencil()->isModule()) {
7247 JS_ReportErrorASCII(cx
,
7248 "evalStencil: Module stencil cannot be evaluated. Use "
7249 "instantiateModuleStencil instead");
7253 CompileOptions
options(cx
);
7254 UniqueChars fileNameBytes
;
7255 Rooted
<JS::Value
> privateValue(cx
);
7256 Rooted
<JSString
*> elementAttributeName(cx
);
7257 if (args
.length() == 2) {
7258 if (!args
[1].isObject()) {
7259 JS_ReportErrorASCII(cx
,
7260 "evalStencil: The 2nd argument must be an object");
7264 RootedObject
opts(cx
, &args
[1].toObject());
7266 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
7269 if (!js::ParseDebugMetadata(cx
, opts
, &privateValue
,
7270 &elementAttributeName
)) {
7275 bool useDebugMetadata
= !privateValue
.isUndefined() || elementAttributeName
;
7277 JS::InstantiateOptions
instantiateOptions(options
);
7278 if (useDebugMetadata
) {
7279 instantiateOptions
.hideScriptFromDebugger
= true;
7282 if (!js::ValidateLazinessOfStencilAndGlobal(cx
, *stencilObj
->stencil())) {
7286 RootedScript
script(cx
, JS::InstantiateGlobalStencil(cx
, instantiateOptions
,
7287 stencilObj
->stencil()));
7292 if (useDebugMetadata
) {
7293 instantiateOptions
.hideScriptFromDebugger
= false;
7294 if (!JS::UpdateDebugMetadata(cx
, script
, instantiateOptions
, privateValue
,
7295 elementAttributeName
, nullptr, nullptr)) {
7300 RootedValue
retVal(cx
);
7301 if (!JS_ExecuteScript(cx
, script
, &retVal
)) {
7305 args
.rval().set(retVal
);
7309 static bool CompileToStencilXDR(JSContext
* cx
, uint32_t argc
, Value
* vp
) {
7310 CallArgs args
= CallArgsFromVp(argc
, vp
);
7312 if (!args
.requireAtLeast(cx
, "compileToStencilXDR", 1)) {
7316 RootedString
src(cx
, ToString
<CanGC
>(cx
, args
[0]));
7321 /* Linearize the string to obtain a char16_t* range. */
7322 AutoStableStringChars
linearChars(cx
);
7323 if (!linearChars
.initTwoByte(cx
, src
)) {
7326 JS::SourceText
<char16_t
> srcBuf
;
7327 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
7331 CompileOptions
options(cx
);
7332 RootedString
displayURL(cx
);
7333 RootedString
sourceMapURL(cx
);
7334 UniqueChars fileNameBytes
;
7335 bool isModule
= false;
7336 if (args
.length() == 2) {
7337 if (!args
[1].isObject()) {
7338 JS_ReportErrorASCII(
7339 cx
, "compileToStencilXDR: The 2nd argument must be an object");
7343 RootedObject
opts(cx
, &args
[1].toObject());
7345 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
7348 if (!ParseCompileOptionsForModule(cx
, options
, opts
, isModule
)) {
7351 if (!js::ParseSourceOptions(cx
, opts
, &displayURL
, &sourceMapURL
)) {
7356 /* Compile the script text to stencil. */
7357 AutoReportFrontendContext
fc(cx
);
7358 frontend::NoScopeBindingCache scopeCache
;
7359 Rooted
<frontend::CompilationInput
> input(cx
,
7360 frontend::CompilationInput(options
));
7361 UniquePtr
<frontend::ExtensibleCompilationStencil
> stencil
;
7363 stencil
= frontend::ParseModuleToExtensibleStencil(
7364 cx
, &fc
, cx
->tempLifoAlloc(), input
.get(), &scopeCache
, srcBuf
);
7366 stencil
= frontend::CompileGlobalScriptToExtensibleStencil(
7367 cx
, &fc
, input
.get(), &scopeCache
, srcBuf
, ScopeKind::Global
);
7373 if (!SetSourceOptions(cx
, &fc
, stencil
->source
, displayURL
, sourceMapURL
)) {
7377 /* Serialize the stencil to XDR. */
7378 JS::TranscodeBuffer xdrBytes
;
7380 frontend::BorrowingCompilationStencil
borrowingStencil(*stencil
);
7381 bool succeeded
= false;
7382 if (!borrowingStencil
.serializeStencils(cx
, input
.get(), xdrBytes
,
7387 fc
.clearAutoReport();
7388 JS_ReportErrorASCII(cx
, "Encoding failure");
7393 Rooted
<StencilXDRBufferObject
*> xdrObj(
7395 StencilXDRBufferObject::create(cx
, xdrBytes
.begin(), xdrBytes
.length()));
7400 args
.rval().setObject(*xdrObj
);
7404 static bool EvalStencilXDR(JSContext
* cx
, uint32_t argc
, Value
* vp
) {
7405 CallArgs args
= CallArgsFromVp(argc
, vp
);
7407 if (!args
.requireAtLeast(cx
, "evalStencilXDR", 1)) {
7411 /* Prepare the input byte array. */
7412 if (!args
[0].isObject()) {
7413 JS_ReportErrorASCII(cx
, "evalStencilXDR: Stencil XDR object expected");
7416 Rooted
<StencilXDRBufferObject
*> xdrObj(
7417 cx
, args
[0].toObject().maybeUnwrapIf
<StencilXDRBufferObject
>());
7419 JS_ReportErrorASCII(cx
, "evalStencilXDR: Stencil XDR object expected");
7422 MOZ_ASSERT(xdrObj
->hasBuffer());
7424 CompileOptions
options(cx
);
7425 UniqueChars fileNameBytes
;
7426 Rooted
<JS::Value
> privateValue(cx
);
7427 Rooted
<JSString
*> elementAttributeName(cx
);
7428 if (args
.length() == 2) {
7429 if (!args
[1].isObject()) {
7430 JS_ReportErrorASCII(cx
,
7431 "evalStencilXDR: The 2nd argument must be an object");
7435 RootedObject
opts(cx
, &args
[1].toObject());
7437 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
7440 if (!js::ParseDebugMetadata(cx
, opts
, &privateValue
,
7441 &elementAttributeName
)) {
7446 /* Prepare the CompilationStencil for decoding. */
7447 AutoReportFrontendContext
fc(cx
);
7448 frontend::CompilationStencil
stencil(nullptr);
7450 /* Deserialize the stencil from XDR. */
7451 JS::TranscodeRange
xdrRange(xdrObj
->buffer(), xdrObj
->bufferLength());
7452 bool succeeded
= false;
7453 if (!stencil
.deserializeStencils(&fc
, options
, xdrRange
, &succeeded
)) {
7457 fc
.clearAutoReport();
7458 JS_ReportErrorASCII(cx
, "Decoding failure");
7462 if (stencil
.isModule()) {
7463 fc
.clearAutoReport();
7464 JS_ReportErrorASCII(cx
,
7465 "evalStencilXDR: Module stencil cannot be evaluated. "
7466 "Use instantiateModuleStencilXDR instead");
7470 if (!js::ValidateLazinessOfStencilAndGlobal(cx
, stencil
)) {
7474 bool useDebugMetadata
= !privateValue
.isUndefined() || elementAttributeName
;
7476 JS::InstantiateOptions
instantiateOptions(options
);
7477 if (useDebugMetadata
) {
7478 instantiateOptions
.hideScriptFromDebugger
= true;
7480 RootedScript
script(
7481 cx
, JS::InstantiateGlobalStencil(cx
, instantiateOptions
, &stencil
));
7486 if (useDebugMetadata
) {
7487 instantiateOptions
.hideScriptFromDebugger
= false;
7488 if (!JS::UpdateDebugMetadata(cx
, script
, instantiateOptions
, privateValue
,
7489 elementAttributeName
, nullptr, nullptr)) {
7494 RootedValue
retVal(cx
);
7495 if (!JS_ExecuteScript(cx
, script
, &retVal
)) {
7499 args
.rval().set(retVal
);
7503 static bool GetExceptionInfo(JSContext
* cx
, uint32_t argc
, Value
* vp
) {
7504 CallArgs args
= CallArgsFromVp(argc
, vp
);
7506 if (!args
.requireAtLeast(cx
, "getExceptionInfo", 1)) {
7510 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>()) {
7511 JS_ReportErrorASCII(cx
, "getExceptionInfo: expected function argument");
7515 RootedValue
rval(cx
);
7516 if (JS_CallFunctionValue(cx
, nullptr, args
[0], JS::HandleValueArray::empty(),
7518 // Function didn't throw.
7519 args
.rval().setNull();
7523 // We currently don't support interrupts or forced returns.
7524 if (!cx
->isExceptionPending()) {
7525 JS_ReportErrorASCII(cx
, "getExceptionInfo: unsupported exception status");
7529 RootedValue
excVal(cx
);
7530 Rooted
<SavedFrame
*> stack(cx
);
7531 if (!GetAndClearExceptionAndStack(cx
, &excVal
, &stack
)) {
7535 RootedValue
stackVal(cx
);
7537 RootedString
stackString(cx
);
7538 if (!BuildStackString(cx
, cx
->realm()->principals(), stack
, &stackString
)) {
7541 stackVal
.setString(stackString
);
7546 RootedObject
obj(cx
, NewPlainObject(cx
));
7551 if (!JS_DefineProperty(cx
, obj
, "exception", excVal
, JSPROP_ENUMERATE
)) {
7555 if (!JS_DefineProperty(cx
, obj
, "stack", stackVal
, JSPROP_ENUMERATE
)) {
7559 args
.rval().setObject(*obj
);
7563 class AllocationMarkerObject
: public NativeObject
{
7565 static const JSClass class_
;
7568 const JSClass
AllocationMarkerObject::class_
= {"AllocationMarker"};
7570 static bool AllocationMarker(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7571 CallArgs args
= CallArgsFromVp(argc
, vp
);
7573 bool allocateInsideNursery
= true;
7574 if (args
.length() > 0 && args
[0].isObject()) {
7575 RootedObject
options(cx
, &args
[0].toObject());
7577 RootedValue
nurseryVal(cx
);
7578 if (!JS_GetProperty(cx
, options
, "nursery", &nurseryVal
)) {
7581 allocateInsideNursery
= ToBoolean(nurseryVal
);
7585 allocateInsideNursery
7586 ? NewObjectWithGivenProto
<AllocationMarkerObject
>(cx
, nullptr)
7587 : NewTenuredObjectWithGivenProto
<AllocationMarkerObject
>(cx
, nullptr);
7592 args
.rval().setObject(*obj
);
7596 namespace gcCallback
{
7603 static void majorGC(JSContext
* cx
, JSGCStatus status
, JS::GCReason reason
,
7605 auto info
= static_cast<MajorGC
*>(data
);
7606 if (!(info
->phases
& (1 << status
))) {
7610 if (info
->depth
> 0) {
7612 JS::PrepareForFullGC(cx
);
7613 JS::NonIncrementalGC(cx
, JS::GCOptions::Normal
, JS::GCReason::API
);
7623 static void minorGC(JSContext
* cx
, JSGCStatus status
, JS::GCReason reason
,
7625 auto info
= static_cast<MinorGC
*>(data
);
7626 if (!(info
->phases
& (1 << status
))) {
7631 info
->active
= false;
7632 if (cx
->zone() && !cx
->zone()->isAtomsZone()) {
7633 cx
->runtime()->gc
.evictNursery(JS::GCReason::DEBUG_GC
);
7635 info
->active
= true;
7639 // Process global, should really be runtime-local.
7640 static MajorGC majorGCInfo
;
7641 static MinorGC minorGCInfo
;
7643 static void enterNullRealm(JSContext
* cx
, JSGCStatus status
,
7644 JS::GCReason reason
, void* data
) {
7645 JSAutoNullableRealm
enterRealm(cx
, nullptr);
7648 } /* namespace gcCallback */
7650 static bool SetGCCallback(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7651 CallArgs args
= CallArgsFromVp(argc
, vp
);
7653 if (args
.length() != 1) {
7654 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
7658 RootedObject
opts(cx
, ToObject(cx
, args
[0]));
7664 if (!JS_GetProperty(cx
, opts
, "action", &v
)) {
7668 JSString
* str
= JS::ToString(cx
, v
);
7672 Rooted
<JSLinearString
*> action(cx
, str
->ensureLinear(cx
));
7678 if (StringEqualsLiteral(action
, "minorGC") ||
7679 StringEqualsLiteral(action
, "majorGC")) {
7680 if (!JS_GetProperty(cx
, opts
, "phases", &v
)) {
7683 if (v
.isUndefined()) {
7684 phases
= (1 << JSGC_END
);
7686 JSString
* str
= JS::ToString(cx
, v
);
7690 JSLinearString
* phasesStr
= str
->ensureLinear(cx
);
7695 if (StringEqualsLiteral(phasesStr
, "begin")) {
7696 phases
= (1 << JSGC_BEGIN
);
7697 } else if (StringEqualsLiteral(phasesStr
, "end")) {
7698 phases
= (1 << JSGC_END
);
7699 } else if (StringEqualsLiteral(phasesStr
, "both")) {
7700 phases
= (1 << JSGC_BEGIN
) | (1 << JSGC_END
);
7702 JS_ReportErrorASCII(cx
, "Invalid callback phase");
7708 if (StringEqualsLiteral(action
, "minorGC")) {
7709 gcCallback::minorGCInfo
.phases
= phases
;
7710 gcCallback::minorGCInfo
.active
= true;
7711 JS_SetGCCallback(cx
, gcCallback::minorGC
, &gcCallback::minorGCInfo
);
7712 } else if (StringEqualsLiteral(action
, "majorGC")) {
7713 if (!JS_GetProperty(cx
, opts
, "depth", &v
)) {
7717 if (!v
.isUndefined()) {
7718 if (!ToInt32(cx
, v
, &depth
)) {
7723 JS_ReportErrorASCII(cx
, "Nesting depth cannot be negative");
7726 if (depth
+ gcstats::MAX_PHASE_NESTING
>
7727 gcstats::Statistics::MAX_SUSPENDED_PHASES
) {
7728 JS_ReportErrorASCII(cx
, "Nesting depth too large, would overflow");
7732 gcCallback::majorGCInfo
.phases
= phases
;
7733 gcCallback::majorGCInfo
.depth
= depth
;
7734 JS_SetGCCallback(cx
, gcCallback::majorGC
, &gcCallback::majorGCInfo
);
7735 } else if (StringEqualsLiteral(action
, "enterNullRealm")) {
7736 JS_SetGCCallback(cx
, gcCallback::enterNullRealm
, nullptr);
7738 JS_ReportErrorASCII(cx
, "Unknown GC callback action");
7742 args
.rval().setUndefined();
7747 static bool EnqueueMark(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7748 CallArgs args
= CallArgsFromVp(argc
, vp
);
7749 gc::GCRuntime
* gc
= &cx
->runtime()->gc
;
7751 if (args
.get(0).isString()) {
7752 RootedString
val(cx
, args
[0].toString());
7753 if (!val
->ensureLinear(cx
)) {
7756 if (!gc
->appendTestMarkQueue(StringValue(val
))) {
7757 JS_ReportOutOfMemory(cx
);
7760 } else if (args
.get(0).isObject()) {
7761 if (!gc
->appendTestMarkQueue(args
[0])) {
7762 JS_ReportOutOfMemory(cx
);
7766 JS_ReportErrorASCII(cx
, "Argument must be a string or object");
7770 args
.rval().setUndefined();
7774 static bool GetMarkQueue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7775 CallArgs args
= CallArgsFromVp(argc
, vp
);
7777 const auto& queue
= cx
->runtime()->gc
.getTestMarkQueue();
7779 RootedObject
result(cx
, JS::NewArrayObject(cx
, queue
.length()));
7783 for (size_t i
= 0; i
< queue
.length(); i
++) {
7784 RootedValue
val(cx
, queue
[i
]);
7785 if (!JS_WrapValue(cx
, &val
)) {
7788 if (!JS_SetElement(cx
, result
, i
, val
)) {
7793 args
.rval().setObject(*result
);
7797 static bool ClearMarkQueue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7798 CallArgs args
= CallArgsFromVp(argc
, vp
);
7800 cx
->runtime()->gc
.clearTestMarkQueue();
7801 args
.rval().setUndefined();
7806 static bool NurseryStringsEnabled(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7807 CallArgs args
= CallArgsFromVp(argc
, vp
);
7808 args
.rval().setBoolean(cx
->zone()->allocNurseryStrings());
7812 static bool IsNurseryAllocated(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7813 CallArgs args
= CallArgsFromVp(argc
, vp
);
7814 if (!args
.get(0).isGCThing()) {
7815 JS_ReportErrorASCII(
7816 cx
, "The function takes one argument, which must be a GC thing");
7820 args
.rval().setBoolean(IsInsideNursery(args
[0].toGCThing()));
7824 static bool NumAllocSitesPretenured(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7825 CallArgs args
= CallArgsFromVp(argc
, vp
);
7826 args
.rval().setInt32(cx
->realm()->numAllocSitesPretenured
);
7830 static bool GetLcovInfo(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7831 CallArgs args
= CallArgsFromVp(argc
, vp
);
7833 if (args
.length() > 1) {
7834 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
7838 if (!coverage::IsLCovEnabled()) {
7839 JS_ReportErrorASCII(cx
, "Coverage not enabled for process.");
7843 RootedObject
global(cx
);
7844 if (args
.hasDefined(0)) {
7845 global
= ToObject(cx
, args
[0]);
7847 JS_ReportErrorASCII(cx
, "Permission denied to access global");
7850 global
= CheckedUnwrapDynamic(global
, cx
, /* stopAtWindowProxy = */ false);
7852 ReportAccessDenied(cx
);
7855 if (!global
->is
<GlobalObject
>()) {
7856 JS_ReportErrorASCII(cx
, "Argument must be a global object");
7860 global
= JS::CurrentGlobalOrNull(cx
);
7864 UniqueChars content
;
7866 AutoRealm
ar(cx
, global
);
7867 content
= js::GetCodeCoverageSummary(cx
, &length
);
7875 JS_NewStringCopyUTF8N(cx
, JS::UTF8Chars(content
.get(), length
));
7880 args
.rval().setString(str
);
7885 static bool SetRNGState(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7886 CallArgs args
= CallArgsFromVp(argc
, vp
);
7887 if (!args
.requireAtLeast(cx
, "SetRNGState", 2)) {
7892 if (!ToNumber(cx
, args
[0], &d0
)) {
7897 if (!ToNumber(cx
, args
[1], &d1
)) {
7901 uint64_t seed0
= static_cast<uint64_t>(d0
);
7902 uint64_t seed1
= static_cast<uint64_t>(d1
);
7904 if (seed0
== 0 && seed1
== 0) {
7905 JS_ReportErrorASCII(cx
, "RNG requires non-zero seed");
7909 cx
->realm()->getOrCreateRandomNumberGenerator().setState(seed0
, seed1
);
7911 args
.rval().setUndefined();
7916 static bool GetTimeZone(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7917 CallArgs args
= CallArgsFromVp(argc
, vp
);
7918 RootedObject
callee(cx
, &args
.callee());
7920 if (args
.length() != 0) {
7921 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
7926 auto getTimeZone
= [](std::time_t* now
) -> const char* {
7928 # if defined(_WIN32)
7930 if (localtime_s(&local
, now
) == 0) {
7931 return _tzname
[local
.tm_isdst
> 0];
7935 # if defined(HAVE_LOCALTIME_R)
7936 if (localtime_r(now
, &local
)) {
7938 std::tm
* localtm
= std::localtime(now
);
7941 # endif /* HAVE_LOCALTIME_R */
7943 # if defined(HAVE_TM_ZONE_TM_GMTOFF)
7944 return local
.tm_zone
;
7946 return tzname
[local
.tm_isdst
> 0];
7947 # endif /* HAVE_TM_ZONE_TM_GMTOFF */
7949 # endif /* _WIN32 */
7953 std::time_t now
= std::time(nullptr);
7954 if (now
!= static_cast<std::time_t>(-1)) {
7955 if (const char* tz
= getTimeZone(&now
)) {
7956 return ReturnStringCopy(cx
, args
, tz
);
7959 #endif /* __wasi__ */
7960 args
.rval().setUndefined();
7965 * Validate time zone input. Accepts the following formats:
7966 * - "America/Chicago" (raw time zone)
7967 * - ":America/Chicago"
7968 * - "/this-part-is-ignored/zoneinfo/America/Chicago"
7969 * - ":/this-part-is-ignored/zoneinfo/America/Chicago"
7970 * - "/etc/localtime"
7971 * - ":/etc/localtime"
7972 * Once the raw time zone is parsed out of the string, it is checked
7973 * against the time zones from GetAvailableTimeZones(). Throws an
7974 * Error if the time zone is invalid.
7976 #if defined(JS_HAS_INTL_API) && !defined(__wasi__)
7977 static bool ValidateTimeZone(JSContext
* cx
, const char* timeZone
) {
7978 static constexpr char zoneInfo
[] = "/zoneinfo/";
7979 static constexpr size_t zoneInfoLength
= sizeof(zoneInfo
) - 1;
7982 if (timeZone
[i
] == ':') {
7985 const char* zoneInfoPtr
= strstr(timeZone
, zoneInfo
);
7986 const char* timeZonePart
= timeZone
[i
] == '/' && zoneInfoPtr
7987 ? zoneInfoPtr
+ zoneInfoLength
7990 if (!*timeZonePart
) {
7991 JS_ReportErrorASCII(cx
, "Invalid time zone format");
7995 if (!strcmp(timeZonePart
, "/etc/localtime")) {
7999 auto timeZones
= mozilla::intl::TimeZone::GetAvailableTimeZones();
8000 if (timeZones
.isErr()) {
8001 intl::ReportInternalError(cx
, timeZones
.unwrapErr());
8004 for (auto timeZoneName
: timeZones
.unwrap()) {
8005 if (timeZoneName
.isErr()) {
8006 intl::ReportInternalError(cx
);
8010 if (!strcmp(timeZonePart
, timeZoneName
.unwrap().data())) {
8015 JS_ReportErrorASCII(cx
, "Unsupported time zone name: %s", timeZonePart
);
8020 static bool SetTimeZone(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8021 CallArgs args
= CallArgsFromVp(argc
, vp
);
8022 RootedObject
callee(cx
, &args
.callee());
8024 if (args
.length() != 1) {
8025 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8029 if (!args
[0].isString() && !args
[0].isUndefined()) {
8030 ReportUsageErrorASCII(cx
, callee
,
8031 "First argument should be a string or undefined");
8036 auto setTimeZone
= [](const char* value
) {
8037 # if defined(_WIN32)
8038 return _putenv_s("TZ", value
) == 0;
8040 return setenv("TZ", value
, true) == 0;
8041 # endif /* _WIN32 */
8044 auto unsetTimeZone
= []() {
8045 # if defined(_WIN32)
8046 return _putenv_s("TZ", "") == 0;
8048 return unsetenv("TZ") == 0;
8049 # endif /* _WIN32 */
8052 if (args
[0].isString() && !args
[0].toString()->empty()) {
8053 Rooted
<JSLinearString
*> str(cx
, args
[0].toString()->ensureLinear(cx
));
8058 if (!StringIsAscii(str
)) {
8059 ReportUsageErrorASCII(cx
, callee
,
8060 "First argument contains non-ASCII characters");
8064 UniqueChars timeZone
= JS_EncodeStringToASCII(cx
, str
);
8069 const char* timeZoneStr
= timeZone
.get();
8070 # ifdef JS_HAS_INTL_API
8071 if (!ValidateTimeZone(cx
, timeZoneStr
)) {
8076 if (!setTimeZone(timeZoneStr
)) {
8077 JS_ReportErrorASCII(cx
, "Failed to set 'TZ' environment variable");
8081 if (!unsetTimeZone()) {
8082 JS_ReportErrorASCII(cx
, "Failed to unset 'TZ' environment variable");
8087 # if defined(_WIN32)
8091 # endif /* _WIN32 */
8093 JS::ResetTimeZone();
8095 #endif /* __wasi__ */
8096 args
.rval().setUndefined();
8100 static bool GetCoreCount(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8101 CallArgs args
= CallArgsFromVp(argc
, vp
);
8102 RootedObject
callee(cx
, &args
.callee());
8104 if (args
.length() != 0) {
8105 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8109 args
.rval().setInt32(GetCPUCount());
8113 static bool GetDefaultLocale(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8114 CallArgs args
= CallArgsFromVp(argc
, vp
);
8115 RootedObject
callee(cx
, &args
.callee());
8117 if (args
.length() != 0) {
8118 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8122 UniqueChars locale
= JS_GetDefaultLocale(cx
);
8124 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
8125 JSMSG_DEFAULT_LOCALE_ERROR
);
8129 return ReturnStringCopy(cx
, args
, locale
.get());
8132 static bool SetDefaultLocale(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8133 CallArgs args
= CallArgsFromVp(argc
, vp
);
8134 RootedObject
callee(cx
, &args
.callee());
8136 if (args
.length() != 1) {
8137 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8141 if (!args
[0].isString() && !args
[0].isUndefined()) {
8142 ReportUsageErrorASCII(cx
, callee
,
8143 "First argument should be a string or undefined");
8147 if (args
[0].isString() && !args
[0].toString()->empty()) {
8148 RootedString
str(cx
, args
[0].toString());
8149 UniqueChars locale
= StringToLocale(cx
, callee
, str
);
8154 if (!JS_SetDefaultLocale(cx
->runtime(), locale
.get())) {
8155 ReportOutOfMemory(cx
);
8159 JS_ResetDefaultLocale(cx
->runtime());
8162 args
.rval().setUndefined();
8167 static bool AflLoop(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8168 CallArgs args
= CallArgsFromVp(argc
, vp
);
8171 if (!ToUint32(cx
, args
.get(0), &max_cnt
)) {
8175 args
.rval().setBoolean(!!__AFL_LOOP(max_cnt
));
8180 static bool MonotonicNow(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8181 CallArgs args
= CallArgsFromVp(argc
, vp
);
8184 // The std::chrono symbols are too new to be present in STL on all platforms we
8185 // care about, so use raw POSIX clock APIs when it might be necessary.
8186 #if defined(XP_UNIX) && !defined(XP_DARWIN)
8187 auto ComputeNow
= [](const timespec
& ts
) {
8188 return ts
.tv_sec
* 1000 + ts
.tv_nsec
/ 1000000;
8192 if (clock_gettime(CLOCK_MONOTONIC
, &ts
) == 0) {
8193 // Use a monotonic clock if available.
8194 now
= ComputeNow(ts
);
8196 // Use a realtime clock as fallback.
8197 if (clock_gettime(CLOCK_REALTIME
, &ts
) != 0) {
8198 // Fail if no clock is available.
8199 JS_ReportErrorASCII(cx
, "can't retrieve system clock");
8203 now
= ComputeNow(ts
);
8205 // Manually enforce atomicity on a non-monotonic clock.
8207 static mozilla::Atomic
<bool, mozilla::ReleaseAcquire
> spinLock
;
8208 while (!spinLock
.compareExchange(false, true)) {
8212 static double lastNow
= -FLT_MAX
;
8213 now
= lastNow
= std::max(now
, lastNow
);
8219 using std::chrono::duration_cast
;
8220 using std::chrono::milliseconds
;
8221 using std::chrono::steady_clock
;
8222 now
= duration_cast
<milliseconds
>(steady_clock::now().time_since_epoch())
8224 #endif // XP_UNIX && !XP_DARWIN
8226 args
.rval().setNumber(now
);
8230 static bool TimeSinceCreation(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8231 CallArgs args
= CallArgsFromVp(argc
, vp
);
8233 (mozilla::TimeStamp::Now() - mozilla::TimeStamp::ProcessCreation())
8235 args
.rval().setNumber(when
);
8239 static bool GetInnerMostEnvironmentObject(JSContext
* cx
, unsigned argc
,
8241 CallArgs args
= CallArgsFromVp(argc
, vp
);
8245 args
.rval().setNull();
8249 args
.rval().setObjectOrNull(iter
.environmentChain(cx
));
8253 static bool GetEnclosingEnvironmentObject(JSContext
* cx
, unsigned argc
,
8255 CallArgs args
= CallArgsFromVp(argc
, vp
);
8256 if (!args
.requireAtLeast(cx
, "getEnclosingEnvironmentObject", 1)) {
8260 if (!args
[0].isObject()) {
8261 args
.rval().setUndefined();
8265 JSObject
* envObj
= &args
[0].toObject();
8267 if (envObj
->is
<EnvironmentObject
>()) {
8268 EnvironmentObject
* env
= &envObj
->as
<EnvironmentObject
>();
8269 args
.rval().setObject(env
->enclosingEnvironment());
8273 if (envObj
->is
<DebugEnvironmentProxy
>()) {
8274 DebugEnvironmentProxy
* envProxy
= &envObj
->as
<DebugEnvironmentProxy
>();
8275 args
.rval().setObject(envProxy
->enclosingEnvironment());
8279 args
.rval().setNull();
8283 static bool GetEnvironmentObjectType(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8284 CallArgs args
= CallArgsFromVp(argc
, vp
);
8285 if (!args
.requireAtLeast(cx
, "getEnvironmentObjectType", 1)) {
8289 if (!args
[0].isObject()) {
8290 args
.rval().setUndefined();
8294 JSObject
* envObj
= &args
[0].toObject();
8296 if (envObj
->is
<EnvironmentObject
>()) {
8297 EnvironmentObject
* env
= &envObj
->as
<EnvironmentObject
>();
8298 JSString
* str
= JS_NewStringCopyZ(cx
, env
->typeString());
8299 args
.rval().setString(str
);
8302 if (envObj
->is
<DebugEnvironmentProxy
>()) {
8303 DebugEnvironmentProxy
* envProxy
= &envObj
->as
<DebugEnvironmentProxy
>();
8304 EnvironmentObject
* env
= &envProxy
->environment();
8305 char buf
[256] = {'\0'};
8306 SprintfLiteral(buf
, "[DebugProxy] %s", env
->typeString());
8307 JSString
* str
= JS_NewStringCopyZ(cx
, buf
);
8308 args
.rval().setString(str
);
8312 args
.rval().setUndefined();
8316 static bool AssertRealmFuseInvariants(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8317 CallArgs args
= CallArgsFromVp(argc
, vp
);
8318 // Note: This will crash if any invariant isn't held, so it's sufficient to
8319 // simply return true always.
8320 cx
->realm()->realmFuses
.assertInvariants(cx
);
8321 args
.rval().setUndefined();
8325 static bool GetFuseState(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8326 CallArgs args
= CallArgsFromVp(argc
, vp
);
8328 cx
->realm()->realmFuses
.assertInvariants(cx
);
8330 RootedObject
returnObj(cx
, JS_NewPlainObject(cx
));
8335 RootedObject
fuseObj(cx
);
8336 RootedString
intactStr(cx
, NewStringCopyZ
<CanGC
>(cx
, "intact"));
8341 RootedValue
intactValue(cx
);
8343 #define FUSE(Name, LowerName) \
8344 fuseObj = JS_NewPlainObject(cx); \
8348 intactValue.setBoolean(cx->realm()->realmFuses.LowerName.intact()); \
8349 if (!JS_DefineProperty(cx, fuseObj, "intact", intactValue, \
8350 JSPROP_ENUMERATE)) { \
8353 if (!JS_DefineProperty(cx, returnObj, #Name, fuseObj, JSPROP_ENUMERATE)) { \
8357 FOR_EACH_REALM_FUSE(FUSE
)
8360 // Register hasSeenUndefinedFuse
8361 fuseObj
= JS_NewPlainObject(cx
);
8365 intactValue
.setBoolean(
8366 cx
->runtime()->hasSeenObjectEmulateUndefinedFuse
.ref().intact());
8367 if (!JS_DefineProperty(cx
, fuseObj
, "intact", intactValue
,
8368 JSPROP_ENUMERATE
)) {
8372 if (!JS_DefineProperty(cx
, returnObj
, "hasSeenObjectEmulateUndefinedFuse",
8373 fuseObj
, JSPROP_ENUMERATE
)) {
8377 args
.rval().setObject(*returnObj
);
8381 static bool PopAllFusesInRealm(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8382 CallArgs args
= CallArgsFromVp(argc
, vp
);
8384 MOZ_ASSERT(cx
->realm());
8386 RealmFuses
& realmFuses
= cx
->realm()->realmFuses
;
8388 #define FUSE(Name, LowerName) realmFuses.LowerName.popFuse(cx, realmFuses);
8389 FOR_EACH_REALM_FUSE(FUSE
)
8392 args
.rval().setUndefined();
8396 static bool GetErrorNotes(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8397 CallArgs args
= CallArgsFromVp(argc
, vp
);
8398 if (!args
.requireAtLeast(cx
, "getErrorNotes", 1)) {
8402 if (!args
[0].isObject() || !args
[0].toObject().is
<ErrorObject
>()) {
8403 args
.rval().setNull();
8407 JSErrorReport
* report
= args
[0].toObject().as
<ErrorObject
>().getErrorReport();
8409 args
.rval().setNull();
8413 RootedObject
notesArray(cx
, CreateErrorNotesArray(cx
, report
));
8418 args
.rval().setObject(*notesArray
);
8422 static bool IsConstructor(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8423 CallArgs args
= CallArgsFromVp(argc
, vp
);
8424 if (args
.length() < 1) {
8425 args
.rval().setBoolean(false);
8427 args
.rval().setBoolean(IsConstructor(args
[0]));
8432 static bool SetTimeResolution(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8433 CallArgs args
= CallArgsFromVp(argc
, vp
);
8434 RootedObject
callee(cx
, &args
.callee());
8436 if (!args
.requireAtLeast(cx
, "setTimeResolution", 2)) {
8440 if (!args
[0].isInt32()) {
8441 ReportUsageErrorASCII(cx
, callee
, "First argument must be an Int32.");
8444 int32_t resolution
= args
[0].toInt32();
8446 if (!args
[1].isBoolean()) {
8447 ReportUsageErrorASCII(cx
, callee
, "Second argument must be a Boolean");
8450 bool jitter
= args
[1].toBoolean();
8452 JS::SetTimeResolutionUsec(resolution
, jitter
);
8454 args
.rval().setUndefined();
8458 static bool ScriptedCallerGlobal(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8459 CallArgs args
= CallArgsFromVp(argc
, vp
);
8461 RootedObject
obj(cx
, JS::GetScriptedCallerGlobal(cx
));
8463 args
.rval().setNull();
8467 obj
= ToWindowProxyIfWindow(obj
);
8469 if (!cx
->compartment()->wrap(cx
, &obj
)) {
8473 args
.rval().setObject(*obj
);
8477 static bool ObjectGlobal(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8478 CallArgs args
= CallArgsFromVp(argc
, vp
);
8479 RootedObject
callee(cx
, &args
.callee());
8481 if (!args
.get(0).isObject()) {
8482 ReportUsageErrorASCII(cx
, callee
, "Argument must be an object");
8486 RootedObject
obj(cx
, &args
[0].toObject());
8487 if (IsCrossCompartmentWrapper(obj
)) {
8488 args
.rval().setNull();
8492 obj
= ToWindowProxyIfWindow(&obj
->nonCCWGlobal());
8494 args
.rval().setObject(*obj
);
8498 static bool IsSameCompartment(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8499 CallArgs args
= CallArgsFromVp(argc
, vp
);
8500 RootedObject
callee(cx
, &args
.callee());
8502 if (!args
.get(0).isObject() || !args
.get(1).isObject()) {
8503 ReportUsageErrorASCII(cx
, callee
, "Both arguments must be objects");
8507 RootedObject
obj1(cx
, UncheckedUnwrap(&args
[0].toObject()));
8508 RootedObject
obj2(cx
, UncheckedUnwrap(&args
[1].toObject()));
8510 args
.rval().setBoolean(obj1
->compartment() == obj2
->compartment());
8514 static bool FirstGlobalInCompartment(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8515 CallArgs args
= CallArgsFromVp(argc
, vp
);
8516 RootedObject
callee(cx
, &args
.callee());
8518 if (!args
.get(0).isObject()) {
8519 ReportUsageErrorASCII(cx
, callee
, "Argument must be an object");
8523 RootedObject
obj(cx
, UncheckedUnwrap(&args
[0].toObject()));
8524 obj
= ToWindowProxyIfWindow(GetFirstGlobalInCompartment(obj
->compartment()));
8526 if (!cx
->compartment()->wrap(cx
, &obj
)) {
8530 args
.rval().setObject(*obj
);
8534 static bool AssertCorrectRealm(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8535 CallArgs args
= CallArgsFromVp(argc
, vp
);
8536 MOZ_RELEASE_ASSERT(cx
->realm() == args
.callee().as
<JSFunction
>().realm());
8537 args
.rval().setUndefined();
8541 static bool GlobalLexicals(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8542 CallArgs args
= CallArgsFromVp(argc
, vp
);
8544 Rooted
<GlobalLexicalEnvironmentObject
*> globalLexical(
8545 cx
, &cx
->global()->lexicalEnvironment());
8547 RootedIdVector
props(cx
);
8548 if (!GetPropertyKeys(cx
, globalLexical
, JSITER_HIDDEN
, &props
)) {
8552 RootedObject
res(cx
, JS_NewPlainObject(cx
));
8557 RootedValue
val(cx
);
8558 for (size_t i
= 0; i
< props
.length(); i
++) {
8559 HandleId id
= props
[i
];
8560 if (!JS_GetPropertyById(cx
, globalLexical
, id
, &val
)) {
8563 if (val
.isMagic(JS_UNINITIALIZED_LEXICAL
)) {
8566 if (!JS_DefinePropertyById(cx
, res
, id
, val
, JSPROP_ENUMERATE
)) {
8571 args
.rval().setObject(*res
);
8575 static bool EncodeAsUtf8InBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8576 CallArgs args
= CallArgsFromVp(argc
, vp
);
8577 if (!args
.requireAtLeast(cx
, "encodeAsUtf8InBuffer", 2)) {
8581 RootedObject
callee(cx
, &args
.callee());
8583 if (!args
[0].isString()) {
8584 ReportUsageErrorASCII(cx
, callee
, "First argument must be a String");
8588 // Create the amounts array early so that the raw pointer into Uint8Array
8589 // data has as short a lifetime as possible
8590 Rooted
<ArrayObject
*> array(cx
, NewDenseFullyAllocatedArray(cx
, 2));
8594 array
->ensureDenseInitializedLength(0, 2);
8596 JSObject
* obj
= args
[1].isObject() ? &args
[1].toObject() : nullptr;
8597 Rooted
<JS::Uint8Array
> view(cx
, JS::Uint8Array::unwrap(obj
));
8599 ReportUsageErrorASCII(cx
, callee
, "Second argument must be a Uint8Array");
8603 mozilla::Span
<uint8_t> span
;
8604 bool isSharedMemory
= false;
8606 // The hazard analysis does not track the data pointer, so it can neither
8607 // tell that `data` is dead if ReportUsageErrorASCII is called, nor that
8608 // its live range ends at the call to AsWritableChars(). Construct a
8609 // temporary scope to hide from the analysis. This should really be replaced
8610 // with a safer mechanism.
8611 JS::AutoCheckCannotGC
nogc(cx
);
8612 if (!view
.isDetached()) {
8613 span
= view
.get().getData(&isSharedMemory
, nogc
);
8617 if (isSharedMemory
|| // exclude views of SharedArrayBuffers
8618 !span
.data()) { // exclude views of detached ArrayBuffers
8619 ReportUsageErrorASCII(
8621 "Second argument must be an unshared, non-detached Uint8Array");
8625 Maybe
<std::tuple
<size_t, size_t>> amounts
=
8626 JS_EncodeStringToUTF8BufferPartial(cx
, args
[0].toString(),
8627 AsWritableChars(span
));
8629 ReportOutOfMemory(cx
);
8633 auto [unitsRead
, bytesWritten
] = *amounts
;
8635 array
->initDenseElement(0, Int32Value(AssertedCast
<int32_t>(unitsRead
)));
8636 array
->initDenseElement(1, Int32Value(AssertedCast
<int32_t>(bytesWritten
)));
8638 args
.rval().setObject(*array
);
8642 JSScript
* js::TestingFunctionArgumentToScript(
8643 JSContext
* cx
, HandleValue v
, JSFunction
** funp
/* = nullptr */) {
8645 // To convert a string to a script, compile it. Parse it as an ES6 Program.
8646 Rooted
<JSString
*> str(cx
, v
.toString());
8647 AutoStableStringChars
linearChars(cx
);
8648 if (!linearChars
.initTwoByte(cx
, str
)) {
8651 SourceText
<char16_t
> source
;
8652 if (!source
.initMaybeBorrowed(cx
, linearChars
)) {
8656 CompileOptions
options(cx
);
8657 return JS::Compile(cx
, options
, source
);
8660 RootedFunction
fun(cx
, JS_ValueToFunction(cx
, v
));
8665 if (!fun
->isInterpreted()) {
8666 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
8667 JSMSG_TESTING_SCRIPTS_ONLY
);
8671 JSScript
* script
= JSFunction::getOrCreateScript(cx
, fun
);
8683 static bool BaselineCompile(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8684 CallArgs args
= CallArgsFromVp(argc
, vp
);
8685 RootedObject
callee(cx
, &args
.callee());
8687 RootedScript
script(cx
);
8688 if (args
.length() == 0) {
8689 NonBuiltinScriptFrameIter
iter(cx
);
8691 ReportUsageErrorASCII(cx
, callee
,
8692 "no script argument and no script caller");
8695 script
= iter
.script();
8697 script
= TestingFunctionArgumentToScript(cx
, args
[0]);
8703 bool forceDebug
= false;
8704 if (args
.length() > 1) {
8705 if (args
.length() > 2) {
8706 ReportUsageErrorASCII(cx
, callee
, "too many arguments");
8709 if (!args
[1].isBoolean() && !args
[1].isUndefined()) {
8710 ReportUsageErrorASCII(
8711 cx
, callee
, "forceDebugInstrumentation argument should be boolean");
8714 forceDebug
= ToBoolean(args
[1]);
8717 const char* returnedStr
= nullptr;
8719 // In order to check for differential behaviour, baselineCompile should have
8720 // the same output whether --no-baseline is used or not.
8721 if (js::SupportDifferentialTesting()) {
8722 returnedStr
= "skipped (differential testing)";
8726 AutoRealm
ar(cx
, script
);
8727 if (script
->hasBaselineScript()) {
8728 if (forceDebug
&& !script
->baselineScript()->hasDebugInstrumentation()) {
8729 // There isn't an easy way to do this for a script that might be on
8730 // stack right now. See
8731 // js::jit::RecompileOnStackBaselineScriptsForDebugMode.
8732 ReportUsageErrorASCII(
8733 cx
, callee
, "unsupported case: recompiling script for debug mode");
8737 args
.rval().setUndefined();
8741 if (!jit::IsBaselineJitEnabled(cx
)) {
8742 returnedStr
= "baseline disabled";
8745 if (!script
->canBaselineCompile()) {
8746 returnedStr
= "can't compile";
8749 if (!cx
->zone()->ensureJitZoneExists(cx
)) {
8753 jit::MethodStatus status
= jit::BaselineCompile(cx
, script
, forceDebug
);
8755 case jit::Method_Error
:
8757 case jit::Method_CantCompile
:
8758 returnedStr
= "can't compile";
8760 case jit::Method_Skipped
:
8761 returnedStr
= "skipped";
8763 case jit::Method_Compiled
:
8764 args
.rval().setUndefined();
8769 return ReturnStringCopy(cx
, args
, returnedStr
);
8775 static bool ClearKeptObjects(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8776 CallArgs args
= CallArgsFromVp(argc
, vp
);
8777 JS::ClearKeptObjects(cx
);
8778 args
.rval().setUndefined();
8782 static bool NumberToDouble(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8783 CallArgs args
= CallArgsFromVp(argc
, vp
);
8784 if (!args
.requireAtLeast(cx
, "numberToDouble", 1)) {
8788 if (!args
[0].isNumber()) {
8789 RootedObject
callee(cx
, &args
.callee());
8790 ReportUsageErrorASCII(cx
, callee
, "argument must be a number");
8794 args
.rval().setDouble(args
[0].toNumber());
8798 static bool GetICUOptions(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8799 CallArgs args
= CallArgsFromVp(argc
, vp
);
8801 RootedObject
info(cx
, JS_NewPlainObject(cx
));
8806 #ifdef JS_HAS_INTL_API
8807 RootedString
str(cx
);
8809 str
= NewStringCopy
<CanGC
>(cx
, mozilla::intl::ICU4CLibrary::GetVersion());
8810 if (!str
|| !JS_DefineProperty(cx
, info
, "version", str
, JSPROP_ENUMERATE
)) {
8814 str
= NewStringCopy
<CanGC
>(cx
, mozilla::intl::String::GetUnicodeVersion());
8815 if (!str
|| !JS_DefineProperty(cx
, info
, "unicode", str
, JSPROP_ENUMERATE
)) {
8819 str
= NewStringCopyZ
<CanGC
>(cx
, mozilla::intl::Locale::GetDefaultLocale());
8820 if (!str
|| !JS_DefineProperty(cx
, info
, "locale", str
, JSPROP_ENUMERATE
)) {
8824 auto tzdataVersion
= mozilla::intl::TimeZone::GetTZDataVersion();
8825 if (tzdataVersion
.isErr()) {
8826 intl::ReportInternalError(cx
, tzdataVersion
.unwrapErr());
8830 str
= NewStringCopy
<CanGC
>(cx
, tzdataVersion
.unwrap());
8831 if (!str
|| !JS_DefineProperty(cx
, info
, "tzdata", str
, JSPROP_ENUMERATE
)) {
8835 intl::FormatBuffer
<char16_t
, intl::INITIAL_CHAR_BUFFER_SIZE
> buf(cx
);
8837 if (auto ok
= DateTimeInfo::timeZoneId(DateTimeInfo::ForceUTC::No
, buf
);
8839 intl::ReportInternalError(cx
, ok
.unwrapErr());
8843 str
= buf
.toString(cx
);
8844 if (!str
|| !JS_DefineProperty(cx
, info
, "timezone", str
, JSPROP_ENUMERATE
)) {
8848 if (auto ok
= mozilla::intl::TimeZone::GetHostTimeZone(buf
); ok
.isErr()) {
8849 intl::ReportInternalError(cx
, ok
.unwrapErr());
8853 str
= buf
.toString(cx
);
8855 !JS_DefineProperty(cx
, info
, "host-timezone", str
, JSPROP_ENUMERATE
)) {
8860 args
.rval().setObject(*info
);
8864 static bool GetAvailableLocalesOf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8865 CallArgs args
= CallArgsFromVp(argc
, vp
);
8866 RootedObject
callee(cx
, &args
.callee());
8868 if (!args
.requireAtLeast(cx
, "getAvailableLocalesOf", 1)) {
8872 HandleValue arg
= args
[0];
8873 if (!arg
.isString()) {
8874 ReportUsageErrorASCII(cx
, callee
, "First argument must be a string");
8878 ArrayObject
* result
;
8879 #ifdef JS_HAS_INTL_API
8880 using SupportedLocaleKind
= js::intl::SharedIntlData::SupportedLocaleKind
;
8882 SupportedLocaleKind kind
;
8884 JSLinearString
* typeStr
= arg
.toString()->ensureLinear(cx
);
8889 if (StringEqualsLiteral(typeStr
, "Collator")) {
8890 kind
= SupportedLocaleKind::Collator
;
8891 } else if (StringEqualsLiteral(typeStr
, "DateTimeFormat")) {
8892 kind
= SupportedLocaleKind::DateTimeFormat
;
8893 } else if (StringEqualsLiteral(typeStr
, "DisplayNames")) {
8894 kind
= SupportedLocaleKind::DisplayNames
;
8895 } else if (StringEqualsLiteral(typeStr
, "ListFormat")) {
8896 kind
= SupportedLocaleKind::ListFormat
;
8897 } else if (StringEqualsLiteral(typeStr
, "NumberFormat")) {
8898 kind
= SupportedLocaleKind::NumberFormat
;
8899 } else if (StringEqualsLiteral(typeStr
, "PluralRules")) {
8900 kind
= SupportedLocaleKind::PluralRules
;
8901 } else if (StringEqualsLiteral(typeStr
, "RelativeTimeFormat")) {
8902 kind
= SupportedLocaleKind::RelativeTimeFormat
;
8903 } else if (StringEqualsLiteral(typeStr
, "Segmenter")) {
8904 kind
= SupportedLocaleKind::Segmenter
;
8906 ReportUsageErrorASCII(cx
, callee
, "Unsupported Intl constructor name");
8911 intl::SharedIntlData
& sharedIntlData
= cx
->runtime()->sharedIntlData
.ref();
8912 result
= sharedIntlData
.availableLocalesOf(cx
, kind
);
8914 result
= NewDenseEmptyArray(cx
);
8920 args
.rval().setObject(*result
);
8924 static bool IsSmallFunction(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8925 CallArgs args
= CallArgsFromVp(argc
, vp
);
8926 RootedObject
callee(cx
, &args
.callee());
8928 if (!args
.requireAtLeast(cx
, "IsSmallFunction", 1)) {
8932 HandleValue arg
= args
[0];
8933 if (!arg
.isObject() || !arg
.toObject().is
<JSFunction
>()) {
8934 ReportUsageErrorASCII(cx
, callee
, "First argument must be a function");
8938 RootedFunction
fun(cx
, &args
[0].toObject().as
<JSFunction
>());
8939 if (!fun
->isInterpreted()) {
8940 ReportUsageErrorASCII(cx
, callee
,
8941 "First argument must be an interpreted function");
8945 JSScript
* script
= JSFunction::getOrCreateScript(cx
, fun
);
8950 args
.rval().setBoolean(jit::JitOptions
.isSmallFunction(script
));
8954 static bool PCCountProfiling_Start(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8955 CallArgs args
= CallArgsFromVp(argc
, vp
);
8957 JS::StartPCCountProfiling(cx
);
8959 args
.rval().setUndefined();
8963 static bool PCCountProfiling_Stop(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8964 CallArgs args
= CallArgsFromVp(argc
, vp
);
8966 JS::StopPCCountProfiling(cx
);
8968 args
.rval().setUndefined();
8972 static bool PCCountProfiling_Purge(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8973 CallArgs args
= CallArgsFromVp(argc
, vp
);
8975 JS::PurgePCCounts(cx
);
8977 args
.rval().setUndefined();
8981 static bool PCCountProfiling_ScriptCount(JSContext
* cx
, unsigned argc
,
8983 CallArgs args
= CallArgsFromVp(argc
, vp
);
8985 size_t length
= JS::GetPCCountScriptCount(cx
);
8987 args
.rval().setNumber(double(length
));
8991 static bool PCCountProfiling_ScriptSummary(JSContext
* cx
, unsigned argc
,
8993 CallArgs args
= CallArgsFromVp(argc
, vp
);
8994 if (!args
.requireAtLeast(cx
, "summary", 1)) {
8999 if (!JS::ToUint32(cx
, args
[0], &index
)) {
9003 JSString
* str
= JS::GetPCCountScriptSummary(cx
, index
);
9008 args
.rval().setString(str
);
9012 static bool PCCountProfiling_ScriptContents(JSContext
* cx
, unsigned argc
,
9014 CallArgs args
= CallArgsFromVp(argc
, vp
);
9015 if (!args
.requireAtLeast(cx
, "contents", 1)) {
9020 if (!JS::ToUint32(cx
, args
[0], &index
)) {
9024 JSString
* str
= JS::GetPCCountScriptContents(cx
, index
);
9029 args
.rval().setString(str
);
9033 static bool NukeCCW(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9034 CallArgs args
= CallArgsFromVp(argc
, vp
);
9036 if (args
.length() != 1 || !args
[0].isObject() ||
9037 !IsCrossCompartmentWrapper(&args
[0].toObject())) {
9038 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_INVALID_ARGS
,
9043 NukeCrossCompartmentWrapper(cx
, &args
[0].toObject());
9044 args
.rval().setUndefined();
9048 static bool FdLibM_Pow(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9049 CallArgs args
= CallArgsFromVp(argc
, vp
);
9052 if (!JS::ToNumber(cx
, args
.get(0), &x
)) {
9057 if (!JS::ToNumber(cx
, args
.get(1), &y
)) {
9061 // Because C99 and ECMA specify different behavior for pow(), we need to wrap
9062 // the fdlibm call to make it ECMA compliant.
9063 if (!std::isfinite(y
) && (x
== 1.0 || x
== -1.0)) {
9064 args
.rval().setNaN();
9066 args
.rval().setDouble(fdlibm_pow(x
, y
));
9072 static const JSFunctionSpecWithHelp TestingFunctions
[] = {
9073 JS_FN_HELP("gc", ::GC
, 0, 0,
9074 "gc([obj] | 'zone' [, ('shrinking' | 'last-ditch') ])",
9075 " Run the garbage collector.\n"
9076 " The first parameter describes which zones to collect: if an object is\n"
9077 " given, GC only its zone. If 'zone' is given, GC any zones that were\n"
9078 " scheduled via schedulegc.\n"
9079 " The second parameter is optional and may be 'shrinking' to perform a\n"
9080 " shrinking GC or 'last-ditch' for a shrinking, last-ditch GC."),
9082 JS_FN_HELP("minorgc", ::MinorGC
, 0, 0,
9083 "minorgc([aboutToOverflow])",
9084 " Run a minor collector on the Nursery. When aboutToOverflow is true, marks\n"
9085 " the store buffer as about-to-overflow before collecting."),
9087 JS_FN_HELP("maybegc", ::MaybeGC
, 0, 0,
9089 " Hint to the engine that now is an ok time to run the garbage collector.\n"),
9091 JS_FN_HELP("gcparam", GCParameter
, 2, 0,
9092 "gcparam(name [, value])",
9093 " Wrapper for JS_[GS]etGCParameter. The name is one of:" GC_PARAMETER_ARGS_LIST
),
9095 JS_FN_HELP("hasDisassembler", HasDisassembler
, 0, 0,
9096 "hasDisassembler()",
9097 " Return true if a disassembler is present (for disnative and wasmDis)."),
9099 JS_FN_HELP("disnative", DisassembleNative
, 2, 0,
9100 "disnative(fun,[path])",
9101 " Disassemble a function into its native code. Optionally write the native code bytes to a file on disk.\n"),
9103 JS_FN_HELP("relazifyFunctions", RelazifyFunctions
, 0, 0,
9104 "relazifyFunctions(...)",
9105 " Perform a GC and allow relazification of functions. Accepts the same\n"
9106 " arguments as gc()."),
9108 JS_FN_HELP("getBuildConfiguration", GetBuildConfiguration
, 1, 0,
9109 "getBuildConfiguration([option])",
9110 " Query the options SpiderMonkey was built with, or return an object\n"
9111 " with the options if no argument is given."),
9113 JS_FN_HELP("getRealmConfiguration", GetRealmConfiguration
, 1, 0,
9114 "getRealmConfiguration([option])",
9115 " Query the runtime options SpiderMonkey is running with, or return an\n."
9116 " object with the options if no argument is given."),
9118 JS_FN_HELP("isLcovEnabled", ::IsLCovEnabled
, 0, 0,
9120 " Return true if JS LCov support is enabled."),
9122 JS_FN_HELP("trialInline", TrialInline
, 0, 0,
9124 " Perform trial-inlining for the caller's frame if it's a BaselineFrame."),
9126 JS_FN_HELP("hasChild", HasChild
, 0, 0,
9127 "hasChild(parent, child)",
9128 " Return true if |child| is a child of |parent|, as determined by a call to\n"
9131 JS_FN_HELP("setSavedStacksRNGState", SetSavedStacksRNGState
, 1, 0,
9132 "setSavedStacksRNGState(seed)",
9133 " Set this compartment's SavedStacks' RNG state.\n"),
9135 JS_FN_HELP("getSavedFrameCount", GetSavedFrameCount
, 0, 0,
9136 "getSavedFrameCount()",
9137 " Return the number of SavedFrame instances stored in this compartment's\n"
9138 " SavedStacks cache."),
9140 JS_FN_HELP("clearSavedFrames", ClearSavedFrames
, 0, 0,
9141 "clearSavedFrames()",
9142 " Empty the current compartment's cache of SavedFrame objects, so that\n"
9143 " subsequent stack captures allocate fresh objects to represent frames.\n"
9144 " Clear the current stack's LiveSavedFrameCaches."),
9146 JS_FN_HELP("saveStack", SaveStack
, 0, 0,
9147 "saveStack([maxDepth [, compartment]])",
9148 " Capture a stack. If 'maxDepth' is given, capture at most 'maxDepth' number\n"
9149 " of frames. If 'compartment' is given, allocate the js::SavedFrame instances\n"
9150 " with the given object's compartment."),
9152 JS_FN_HELP("captureFirstSubsumedFrame", CaptureFirstSubsumedFrame
, 1, 0,
9153 "saveStack(object [, shouldIgnoreSelfHosted = true]])",
9154 " Capture a stack back to the first frame whose principals are subsumed by the\n"
9155 " object's compartment's principals. If 'shouldIgnoreSelfHosted' is given,\n"
9156 " control whether self-hosted frames are considered when checking principals."),
9158 JS_FN_HELP("callFunctionFromNativeFrame", CallFunctionFromNativeFrame
, 1, 0,
9159 "callFunctionFromNativeFrame(function)",
9160 " Call 'function' with a (C++-)native frame on stack.\n"
9161 " Required for testing that SaveStack properly handles native frames."),
9163 JS_FN_HELP("callFunctionWithAsyncStack", CallFunctionWithAsyncStack
, 0, 0,
9164 "callFunctionWithAsyncStack(function, stack, asyncCause)",
9165 " Call 'function', using the provided stack as the async stack responsible\n"
9166 " for the call, and propagate its return value or the exception it throws.\n"
9167 " The function is called with no arguments, and 'this' is 'undefined'. The\n"
9168 " specified |asyncCause| is attached to the provided stack frame."),
9170 JS_FN_HELP("enableTrackAllocations", EnableTrackAllocations
, 0, 0,
9171 "enableTrackAllocations()",
9172 " Start capturing the JS stack at every allocation. Note that this sets an\n"
9173 " object metadata callback that will override any other object metadata\n"
9174 " callback that may be set."),
9176 JS_FN_HELP("disableTrackAllocations", DisableTrackAllocations
, 0, 0,
9177 "disableTrackAllocations()",
9178 " Stop capturing the JS stack at every allocation."),
9180 JS_FN_HELP("setTestFilenameValidationCallback", SetTestFilenameValidationCallback
, 0, 0,
9181 "setTestFilenameValidationCallback()",
9182 " Set the filename validation callback to a callback that accepts only\n"
9183 " filenames starting with 'safe' or (only in system realms) 'system'."),
9185 JS_FN_HELP("newObjectWithAddPropertyHook", NewObjectWithAddPropertyHook
, 0, 0,
9186 "newObjectWithAddPropertyHook()",
9187 " Returns a new object with an addProperty JSClass hook. This hook\n"
9188 " increments the value of the _propertiesAdded data property on the object\n"
9189 " when a new property is added."),
9191 JS_FN_HELP("newObjectWithCallHook", NewObjectWithCallHook
, 0, 0,
9192 "newObjectWithCallHook()",
9193 " Returns a new object with call/construct JSClass hooks. These hooks return\n"
9194 " a new object that contains the Values supplied by the caller."),
9196 JS_FN_HELP("newObjectWithManyReservedSlots", NewObjectWithManyReservedSlots
, 0, 0,
9197 "newObjectWithManyReservedSlots()",
9198 " Returns a new object with many reserved slots. The slots are initialized to int32\n"
9199 " values. checkObjectWithManyReservedSlots can be used to check the slots still\n"
9200 " hold these values."),
9202 JS_FN_HELP("checkObjectWithManyReservedSlots", CheckObjectWithManyReservedSlots
, 1, 0,
9203 "checkObjectWithManyReservedSlots(obj)",
9204 " Checks the reserved slots set by newObjectWithManyReservedSlots still hold the expected\n"
9207 JS_FN_HELP("getWatchtowerLog", GetWatchtowerLog
, 0, 0,
9208 "getWatchtowerLog()",
9209 " Returns the Watchtower log recording object changes for objects for which\n"
9210 " addWatchtowerTarget was called. The internal log is cleared. The return\n"
9211 " value is an array of plain objects with the following properties:\n"
9212 " - kind: a string describing the kind of mutation, for example \"add-prop\"\n"
9213 " - object: the object being mutated\n"
9214 " - extra: an extra value, for example the name of the property being added"),
9216 JS_FN_HELP("addWatchtowerTarget", AddWatchtowerTarget
, 1, 0,
9217 "addWatchtowerTarget(object)",
9218 " Invoke the watchtower callback for changes to this object."),
9220 JS_FN_HELP("newString", NewString
, 2, 0,
9221 "newString(str[, options])",
9222 " Copies str's chars and returns a new string. Valid options:\n"
9224 " - tenured: allocate directly into the tenured heap.\n"
9226 " - twoByte: create a \"two byte\" string, not a latin1 string, regardless of the\n"
9227 " input string's characters. Latin1 will be used by default if possible\n"
9228 " (again regardless of the input string.)\n"
9230 " - external: create an external string. External strings are always twoByte and\n"
9233 " - maybeExternal: create an external string, unless the data fits within an\n"
9234 " inline string. Inline strings may be nursery-allocated."),
9236 JS_FN_HELP("ensureLinearString", EnsureLinearString
, 1, 0,
9237 "ensureLinearString(str)",
9238 " Ensures str is a linear (non-rope) string and returns it."),
9240 JS_FN_HELP("representativeStringArray", RepresentativeStringArray
, 0, 0,
9241 "representativeStringArray()",
9242 " Returns an array of strings that represent the various internal string\n"
9243 " types and character encodings."),
9245 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
9247 JS_FN_HELP("oomThreadTypes", OOMThreadTypes
, 0, 0,
9249 " Get the number of thread types that can be used as an argument for\n"
9250 " oomAfterAllocations() and oomAtAllocation()."),
9252 JS_FN_HELP("oomAfterAllocations", OOMAfterAllocations
, 2, 0,
9253 "oomAfterAllocations(count [,threadType])",
9254 " After 'count' js_malloc memory allocations, fail every following allocation\n"
9255 " (return nullptr). The optional thread type limits the effect to the\n"
9256 " specified type of helper thread."),
9258 JS_FN_HELP("oomAtAllocation", OOMAtAllocation
, 2, 0,
9259 "oomAtAllocation(count [,threadType])",
9260 " After 'count' js_malloc memory allocations, fail the next allocation\n"
9261 " (return nullptr). The optional thread type limits the effect to the\n"
9262 " specified type of helper thread."),
9264 JS_FN_HELP("resetOOMFailure", ResetOOMFailure
, 0, 0,
9265 "resetOOMFailure()",
9266 " Remove the allocation failure scheduled by either oomAfterAllocations() or\n"
9267 " oomAtAllocation() and return whether any allocation had been caused to fail."),
9269 JS_FN_HELP("oomTest", OOMTest
, 0, 0,
9270 "oomTest(function, [expectExceptionOnFailure = true | options])",
9271 " Test that the passed function behaves correctly under OOM conditions by\n"
9272 " repeatedly executing it and simulating allocation failure at successive\n"
9273 " allocations until the function completes without seeing a failure.\n"
9274 " By default this tests that an exception is raised if execution fails, but\n"
9275 " this can be disabled by passing false as the optional second parameter.\n"
9276 " This is also disabled when --fuzzing-safe is specified.\n"
9277 " Alternatively an object can be passed to set the following options:\n"
9278 " expectExceptionOnFailure: bool - as described above.\n"
9279 " keepFailing: bool - continue to fail after first simulated failure.\n"
9281 " WARNING: By design, oomTest assumes the test-function follows the same\n"
9282 " code path each time it is called, right up to the point where OOM occurs.\n"
9283 " If on iteration 70 it finishes and caches a unit of work that saves 65\n"
9284 " allocations the next time we run, then the subsequent 65 allocation\n"
9285 " points will go untested.\n"
9287 " Things in this category include lazy parsing and baseline compilation,\n"
9288 " so it is very easy to accidentally write an oomTest that only tests one\n"
9289 " or the other of those, and not the functionality you meant to test!\n"
9290 " To avoid lazy parsing, call the test function once first before passing\n"
9291 " it to oomTest. The jits can be disabled via the test harness.\n"),
9293 JS_FN_HELP("stackTest", StackTest
, 0, 0,
9294 "stackTest(function, [expectExceptionOnFailure = true])",
9295 " This function behaves exactly like oomTest with the difference that\n"
9296 " instead of simulating regular OOM conditions, it simulates the engine\n"
9297 " running out of stack space (failing recursion check).\n"
9299 " See the WARNING in help('oomTest').\n"),
9301 JS_FN_HELP("interruptTest", InterruptTest
, 0, 0,
9302 "interruptTest(function)",
9303 " This function simulates interrupts similar to how oomTest simulates OOM conditions."
9305 " See the WARNING in help('oomTest').\n"),
9307 #endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
9309 JS_FN_HELP("newRope", NewRope
, 3, 0,
9310 "newRope(left, right[, options])",
9311 " Creates a rope with the given left/right strings.\n"
9312 " Available options:\n"
9313 " nursery: bool - force the string to be created in/out of the nursery, if possible.\n"),
9315 JS_FN_HELP("isRope", IsRope
, 1, 0,
9317 " Returns true if the parameter is a rope"),
9319 JS_FN_HELP("settlePromiseNow", SettlePromiseNow
, 1, 0,
9320 "settlePromiseNow(promise)",
9321 " 'Settle' a 'promise' immediately. This just marks the promise as resolved\n"
9322 " with a value of `undefined` and causes the firing of any onPromiseSettled\n"
9323 " hooks set on Debugger instances that are observing the given promise's\n"
9324 " global as a debuggee."),
9325 JS_FN_HELP("getWaitForAllPromise", GetWaitForAllPromise
, 1, 0,
9326 "getWaitForAllPromise(densePromisesArray)",
9327 " Calls the 'GetWaitForAllPromise' JSAPI function and returns the result\n"
9329 JS_FN_HELP("resolvePromise", ResolvePromise
, 2, 0,
9330 "resolvePromise(promise, resolution)",
9331 " Resolve a Promise by calling the JSAPI function JS::ResolvePromise."),
9332 JS_FN_HELP("rejectPromise", RejectPromise
, 2, 0,
9333 "rejectPromise(promise, reason)",
9334 " Reject a Promise by calling the JSAPI function JS::RejectPromise."),
9336 JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver
, 0, 0,
9337 "makeFinalizeObserver()",
9338 " Get a special object whose finalization increases the counter returned\n"
9339 " by the finalizeCount function."),
9341 JS_FN_HELP("finalizeCount", FinalizeCount
, 0, 0,
9343 " Return the current value of the finalization counter that is incremented\n"
9344 " each time an object returned by the makeFinalizeObserver is finalized."),
9346 JS_FN_HELP("resetFinalizeCount", ResetFinalizeCount
, 0, 0,
9347 "resetFinalizeCount()",
9348 " Reset the value returned by finalizeCount()."),
9350 JS_FN_HELP("gcPreserveCode", GCPreserveCode
, 0, 0,
9352 " Preserve JIT code during garbage collections."),
9355 JS_FN_HELP("gczeal", GCZeal
, 2, 0,
9356 "gczeal([mode, [frequency]])",
9357 gc::ZealModeHelpText
),
9359 JS_FN_HELP("unsetgczeal", UnsetGCZeal
, 2, 0,
9360 "unsetgczeal(mode)",
9361 " Turn off a single zeal mode set with gczeal() and don't finish any ongoing\n"
9362 " collection that may be happening."),
9364 JS_FN_HELP("schedulegc", ScheduleGC
, 1, 0,
9365 "schedulegc([num])",
9366 " If num is given, schedule a GC after num allocations.\n"
9367 " Returns the number of allocations before the next trigger."),
9369 JS_FN_HELP("selectforgc", SelectForGC
, 0, 0,
9370 "selectforgc(obj1, obj2, ...)",
9371 " Schedule the given objects to be marked in the next GC slice."),
9373 JS_FN_HELP("verifyprebarriers", VerifyPreBarriers
, 0, 0,
9374 "verifyprebarriers()",
9375 " Start or end a run of the pre-write barrier verifier."),
9377 JS_FN_HELP("verifypostbarriers", VerifyPostBarriers
, 0, 0,
9378 "verifypostbarriers()",
9379 " Does nothing (the post-write barrier verifier has been remove)."),
9381 JS_FN_HELP("currentgc", CurrentGC
, 0, 0,
9383 " Report various information about the currently running incremental GC,\n"
9384 " if one is running."),
9386 JS_FN_HELP("deterministicgc", DeterministicGC
, 1, 0,
9387 "deterministicgc(true|false)",
9388 " If true, only allow determinstic GCs to run."),
9390 JS_FN_HELP("dumpGCArenaInfo", DumpGCArenaInfo
, 0, 0,
9391 "dumpGCArenaInfo()",
9392 " Prints information about the different GC things and how they are arranged\n"
9395 JS_FN_HELP("setMarkStackLimit", SetMarkStackLimit
, 1, 0,
9396 "markStackLimit(limit)",
9397 " Sets a limit on the number of words used for the mark stack. Used to test OOM"
9398 " handling during marking.\n"),
9402 JS_FN_HELP("gcstate", GCState
, 0, 0,
9404 " Report the global GC state, or the GC state for the zone containing |obj|."),
9406 JS_FN_HELP("schedulezone", ScheduleZoneForGC
, 1, 0,
9407 "schedulezone([obj | string])",
9408 " If obj is given, schedule a GC of obj's zone.\n"
9409 " If string is given, schedule a GC of the string's zone if possible."),
9411 JS_FN_HELP("startgc", StartGC
, 1, 0,
9412 "startgc([n [, 'shrinking']])",
9413 " Start an incremental GC and run a slice that processes about n objects.\n"
9414 " If 'shrinking' is passesd as the optional second argument, perform a\n"
9415 " shrinking GC rather than a normal GC. If no zones have been selected with\n"
9416 " schedulezone(), a full GC will be performed."),
9418 JS_FN_HELP("finishgc", FinishGC
, 0, 0,
9420 " Finish an in-progress incremental GC, if none is running then do nothing."),
9422 JS_FN_HELP("gcslice", GCSlice
, 1, 0,
9423 "gcslice([n [, options]])",
9424 " Start or continue an an incremental GC, running a slice that processes\n"
9425 " about n objects. Takes an optional options object, which may contain the\n"
9426 " following properties:\n"
9427 " dontStart: do not start a new incremental GC if one is not already\n"
9430 JS_FN_HELP("abortgc", AbortGC
, 1, 0,
9432 " Abort the current incremental GC."),
9434 JS_FN_HELP("setMallocMaxDirtyPageModifier", SetMallocMaxDirtyPageModifier
, 1, 0,
9435 "setMallocMaxDirtyPageModifier(value)",
9436 " Change the maximum size of jemalloc's page cache. The value should be between\n"
9437 " -5 and 16 (inclusive). See moz_set_max_dirty_page_modifier.\n"),
9439 JS_FN_HELP("fullcompartmentchecks", FullCompartmentChecks
, 1, 0,
9440 "fullcompartmentchecks(true|false)",
9441 " If true, check for compartment mismatches before every GC."),
9443 JS_FN_HELP("nondeterministicGetWeakMapKeys", NondeterministicGetWeakMapKeys
, 1, 0,
9444 "nondeterministicGetWeakMapKeys(weakmap)",
9445 " Return an array of the keys in the given WeakMap."),
9447 JS_FN_HELP("internalConst", InternalConst
, 1, 0,
9448 "internalConst(name)",
9449 " Query an internal constant for the engine. See InternalConst source for\n"
9450 " the list of constant names."),
9452 JS_FN_HELP("isProxy", IsProxy
, 1, 0,
9454 " If true, obj is a proxy of some sort"),
9456 JS_FN_HELP("dumpHeap", DumpHeap
, 1, 0,
9457 "dumpHeap([filename])",
9458 " Dump reachable and unreachable objects to the named file, or to stdout. Objects\n"
9459 " in the nursery are ignored, so if you wish to include them, consider calling\n"
9460 " minorgc() first."),
9462 JS_FN_HELP("terminate", Terminate
, 0, 0,
9464 " Terminate JavaScript execution, as if we had run out of\n"
9465 " memory or been terminated by the slow script dialog."),
9467 JS_FN_HELP("readGeckoProfilingStack", ReadGeckoProfilingStack
, 0, 0,
9468 "readGeckoProfilingStack()",
9469 " Reads the JIT/Wasm stack using ProfilingFrameIterator. Skips non-JIT/Wasm frames."),
9471 JS_FN_HELP("readGeckoInterpProfilingStack", ReadGeckoInterpProfilingStack
, 0, 0,
9472 "readGeckoInterpProfilingStack()",
9473 " Reads the C++ interpreter profiling stack. Skips JIT/Wasm frames."),
9475 JS_FN_HELP("enableOsiPointRegisterChecks", EnableOsiPointRegisterChecks
, 0, 0,
9476 "enableOsiPointRegisterChecks()",
9477 " Emit extra code to verify live regs at the start of a VM call are not\n"
9478 " modified before its OsiPoint."),
9480 JS_FN_HELP("displayName", DisplayName
, 1, 0,
9482 " Gets the display name for a function, which can possibly be a guessed or\n"
9483 " inferred name based on where the function was defined. This can be\n"
9484 " different from the 'name' property on the function."),
9486 JS_FN_HELP("isAsmJSCompilationAvailable", IsAsmJSCompilationAvailable
, 0, 0,
9487 "isAsmJSCompilationAvailable",
9488 " Returns whether asm.js compilation is currently available or whether it is disabled\n"
9489 " (e.g., by the debugger)."),
9491 JS_FN_HELP("getJitCompilerOptions", GetJitCompilerOptions
, 0, 0,
9492 "getJitCompilerOptions()",
9493 " Return an object describing some of the JIT compiler options.\n"),
9495 JS_FN_HELP("isAsmJSModule", IsAsmJSModule
, 1, 0,
9496 "isAsmJSModule(fn)",
9497 " Returns whether the given value is a function containing \"use asm\" that has been\n"
9498 " validated according to the asm.js spec."),
9500 JS_FN_HELP("isAsmJSFunction", IsAsmJSFunction
, 1, 0,
9501 "isAsmJSFunction(fn)",
9502 " Returns whether the given value is a nested function in an asm.js module that has been\n"
9503 " both compile- and link-time validated."),
9505 JS_FN_HELP("isAvxPresent", IsAvxPresent
, 0, 0,
9506 "isAvxPresent([minVersion])",
9507 " Returns whether AVX is present and enabled. If minVersion specified,\n"
9508 " use 1 - to check if AVX is enabled (default), 2 - if AVX2 is enabled."),
9510 JS_FN_HELP("wasmIsSupported", WasmIsSupported
, 0, 0,
9511 "wasmIsSupported()",
9512 " Returns a boolean indicating whether WebAssembly is supported on the current device."),
9514 JS_FN_HELP("wasmIsSupportedByHardware", WasmIsSupportedByHardware
, 0, 0,
9515 "wasmIsSupportedByHardware()",
9516 " Returns a boolean indicating whether WebAssembly is supported on the current hardware (regardless of whether we've enabled support)."),
9518 JS_FN_HELP("wasmDebuggingEnabled", WasmDebuggingEnabled
, 0, 0,
9519 "wasmDebuggingEnabled()",
9520 " Returns a boolean indicating whether WebAssembly debugging is supported on the current device;\n"
9521 " returns false also if WebAssembly is not supported"),
9523 JS_FN_HELP("wasmStreamingEnabled", WasmStreamingEnabled
, 0, 0,
9524 "wasmStreamingEnabled()",
9525 " Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
9527 JS_FN_HELP("wasmCachingEnabled", WasmCachingEnabled
, 0, 0,
9528 "wasmCachingEnabled()",
9529 " Returns a boolean indicating whether WebAssembly caching is supported by the runtime."),
9531 JS_FN_HELP("wasmHugeMemorySupported", WasmHugeMemorySupported
, 0, 0,
9532 "wasmHugeMemorySupported()",
9533 " Returns a boolean indicating whether WebAssembly supports using a large"
9534 " virtual memory reservation in order to elide bounds checks on this platform."),
9536 JS_FN_HELP("wasmMaxMemoryPages", WasmMaxMemoryPages
, 1, 0,
9537 "wasmMaxMemoryPages(indexType)",
9538 " Returns an int with the maximum number of pages that can be allocated to a memory."
9539 " This is an implementation artifact that does depend on the index type, the hardware,"
9540 " the operating system, the build configuration, and flags. The result is constant for"
9541 " a given combination of those; there is no guarantee that that size allocation will"
9542 " always succeed, only that it can succeed in principle. The indexType is a string,"
9543 " 'i32' or 'i64'."),
9545 #define WASM_FEATURE(NAME, ...) \
9546 JS_FN_HELP("wasm" #NAME "Enabled", Wasm##NAME##Enabled, 0, 0, \
9547 "wasm" #NAME "Enabled()", \
9548 " Returns a boolean indicating whether the WebAssembly " #NAME " proposal is enabled."),
9549 JS_FOR_WASM_FEATURES(WASM_FEATURE
)
9552 JS_FN_HELP("wasmThreadsEnabled", WasmThreadsEnabled
, 0, 0,
9553 "wasmThreadsEnabled()",
9554 " Returns a boolean indicating whether the WebAssembly threads proposal is\n"
9555 " supported on the current device."),
9557 JS_FN_HELP("wasmSimdEnabled", WasmSimdEnabled
, 0, 0,
9558 "wasmSimdEnabled()",
9559 " Returns a boolean indicating whether WebAssembly SIMD proposal is\n"
9560 " supported by the current device."),
9562 #if defined(ENABLE_WASM_SIMD) && defined(DEBUG)
9563 JS_FN_HELP("wasmSimdAnalysis", WasmSimdAnalysis
, 1, 0,
9564 "wasmSimdAnalysis(...)",
9565 " Unstable API for white-box testing.\n"),
9568 JS_FN_HELP("wasmGlobalFromArrayBuffer", WasmGlobalFromArrayBuffer
, 2, 0,
9569 "wasmGlobalFromArrayBuffer(type, arrayBuffer)",
9570 " Create a WebAssembly.Global object from a provided ArrayBuffer. The type\n"
9571 " must be POD (i32, i64, f32, f64, v128). The buffer must be the same\n"
9572 " size as the type in bytes.\n"),
9573 JS_FN_HELP("wasmGlobalExtractLane", WasmGlobalExtractLane
, 3, 0,
9574 "wasmGlobalExtractLane(global, laneInterp, laneIndex)",
9575 " Extract a lane from a WebAssembly.Global object that contains a v128 value\n"
9576 " and return it as a new WebAssembly.Global object of the appropriate type.\n"
9577 " The supported laneInterp values are i32x4, i64x2, f32x4, and\n"
9579 JS_FN_HELP("wasmGlobalsEqual", WasmGlobalsEqual
, 2, 0,
9580 "wasmGlobalsEqual(globalA, globalB)",
9581 " Compares two WebAssembly.Global objects for if their types and values are\n"
9582 " equal. Mutability is not compared. Floating point values are compared for\n"
9583 " bitwise equality, not IEEE 754 equality.\n"),
9584 JS_FN_HELP("wasmGlobalIsNaN", WasmGlobalIsNaN
, 2, 0,
9585 "wasmGlobalIsNaN(global, flavor)",
9586 " Compares a floating point WebAssembly.Global object for if its value is a\n"
9587 " specific NaN flavor. Valid flavors are `arithmetic_nan` and `canonical_nan`.\n"),
9588 JS_FN_HELP("wasmGlobalToString", WasmGlobalToString
, 1, 0,
9589 "wasmGlobalToString(global)",
9590 " Returns a debug representation of the contents of a WebAssembly.Global\n"
9592 JS_FN_HELP("wasmLosslessInvoke", WasmLosslessInvoke
, 1, 0,
9593 "wasmLosslessInvoke(wasmFunc, args...)",
9594 " Invokes the provided WebAssembly function using a modified conversion\n"
9595 " function that allows providing a param as a WebAssembly.Global and\n"
9596 " returning a result as a WebAssembly.Global.\n"),
9598 JS_FN_HELP("wasmCompilersPresent", WasmCompilersPresent
, 0, 0,
9599 "wasmCompilersPresent()",
9600 " Returns a string indicating the present wasm compilers: a comma-separated list\n"
9601 " of 'baseline', 'ion'. A compiler is present in the executable if it is compiled\n"
9602 " in and can generate code for the current architecture."),
9604 JS_FN_HELP("wasmCompileMode", WasmCompileMode
, 0, 0,
9605 "wasmCompileMode()",
9606 " Returns a string indicating the available wasm compilers: 'baseline', 'ion',\n"
9607 " 'baseline+ion', or 'none'. A compiler is available if it is present in the\n"
9608 " executable and not disabled by switches or runtime conditions. At most one\n"
9609 " baseline and one optimizing compiler can be available."),
9611 JS_FN_HELP("wasmBaselineDisabledByFeatures", WasmBaselineDisabledByFeatures
, 0, 0,
9612 "wasmBaselineDisabledByFeatures()",
9613 " If some feature is enabled at compile-time or run-time that prevents baseline\n"
9614 " from being used then this returns a truthy string describing the features that\n."
9615 " are disabling it. Otherwise it returns false."),
9617 JS_FN_HELP("wasmIonDisabledByFeatures", WasmIonDisabledByFeatures
, 0, 0,
9618 "wasmIonDisabledByFeatures()",
9619 " If some feature is enabled at compile-time or run-time that prevents Ion\n"
9620 " from being used then this returns a truthy string describing the features that\n."
9621 " are disabling it. Otherwise it returns false."),
9623 JS_FN_HELP("wasmExtractCode", WasmExtractCode
, 1, 0,
9624 "wasmExtractCode(module[, tier])",
9625 " Extracts generated machine code from WebAssembly.Module. The tier is a string,\n"
9626 " 'stable', 'best', 'baseline', or 'ion'; the default is 'stable'. If the request\n"
9627 " cannot be satisfied then null is returned. If the request is 'ion' then block\n"
9628 " until background compilation is complete."),
9630 JS_FN_HELP("wasmDis", WasmDisassemble
, 1, 0,
9631 "wasmDis(wasmObject[, options])\n",
9632 " Disassembles generated machine code from an exported WebAssembly function,\n"
9633 " or from all the functions defined in the module or instance, exported and not.\n"
9634 " The `options` is an object with the following optional keys:\n"
9635 " asString: boolean - if true, return a string rather than printing on stderr,\n"
9636 " the default is false.\n"
9637 " tier: string - one of 'stable', 'best', 'baseline', or 'ion'; the default is\n"
9639 " kinds: string - if set, and the wasmObject is a module or instance, a\n"
9640 " comma-separated list of the following keys, the default is `Function`:\n"
9641 " Function - functions defined in the module\n"
9642 " InterpEntry - C++-to-wasm stubs\n"
9643 " JitEntry - jitted-js-to-wasm stubs\n"
9644 " ImportInterpExit - wasm-to-C++ stubs\n"
9645 " ImportJitExit - wasm-to-jitted-JS stubs\n"
9646 " all - all kinds, including obscure ones\n"),
9648 JS_FN_HELP("wasmHasTier2CompilationCompleted", WasmHasTier2CompilationCompleted
, 1, 0,
9649 "wasmHasTier2CompilationCompleted(module)",
9650 " Returns a boolean indicating whether a given module has finished compiled code for tier2. \n"
9651 "This will return true early if compilation isn't two-tiered. "),
9653 JS_FN_HELP("wasmLoadedFromCache", WasmLoadedFromCache
, 1, 0,
9654 "wasmLoadedFromCache(module)",
9655 " Returns a boolean indicating whether a given module was deserialized directly from a\n"
9656 " cache (as opposed to compiled from bytecode)."),
9658 JS_FN_HELP("wasmBuiltinI8VecMul", WasmBuiltinI8VecMul
, 0, 0,
9659 "wasmBuiltinI8VecMul()",
9660 " Returns a module that implements an i8 vector pairwise multiplication intrinsic."),
9662 #ifdef ENABLE_WASM_GC
9663 JS_FN_HELP("wasmGcReadField", WasmGcReadField
, 2, 0,
9664 "wasmGcReadField(obj, index)",
9665 " Gets a field of a WebAssembly GC struct or array."),
9667 JS_FN_HELP("wasmGcArrayLength", WasmGcArrayLength
, 1, 0,
9668 "wasmGcArrayLength(arr)",
9669 " Gets the length of a WebAssembly GC array."),
9670 #endif // ENABLE_WASM_GC
9672 JS_FN_HELP("largeArrayBufferSupported", LargeArrayBufferSupported
, 0, 0,
9673 "largeArrayBufferSupported()",
9674 " Returns true if array buffers larger than 2GB can be allocated."),
9676 JS_FN_HELP("isLazyFunction", IsLazyFunction
, 1, 0,
9677 "isLazyFunction(fun)",
9678 " True if fun is a lazy JSFunction."),
9680 JS_FN_HELP("isRelazifiableFunction", IsRelazifiableFunction
, 1, 0,
9681 "isRelazifiableFunction(fun)",
9682 " True if fun is a JSFunction with a relazifiable JSScript."),
9684 JS_FN_HELP("hasSameBytecodeData", HasSameBytecodeData
, 2, 0,
9685 "hasSameBytecodeData(fun1, fun2)",
9686 " True if fun1 and fun2 share the same copy of bytecode data. This will\n"
9687 " delazify the function if necessary."),
9689 JS_FN_HELP("enableShellAllocationMetadataBuilder", EnableShellAllocationMetadataBuilder
, 0, 0,
9690 "enableShellAllocationMetadataBuilder()",
9691 " Use ShellAllocationMetadataBuilder to supply metadata for all newly created objects."),
9693 JS_FN_HELP("getAllocationMetadata", GetAllocationMetadata
, 1, 0,
9694 "getAllocationMetadata(obj)",
9695 " Get the metadata for an object."),
9697 JS_INLINABLE_FN_HELP("bailout", testingFunc_bailout
, 0, 0, TestBailout
,
9699 " Force a bailout out of ionmonkey (if running in ionmonkey)."),
9701 JS_FN_HELP("bailAfter", testingFunc_bailAfter
, 1, 0,
9702 "bailAfter(number)",
9703 " Start a counter to bail once after passing the given amount of possible bailout positions in\n"
9706 JS_FN_HELP("invalidate", testingFunc_invalidate
, 0, 0,
9708 " Force an immediate invalidation (if running in Warp)."),
9710 JS_FN_HELP("inJit", testingFunc_inJit
, 0, 0,
9712 " Returns true when called within (jit-)compiled code. When jit compilation is disabled this\n"
9713 " function returns an error string. This function returns false in all other cases.\n"
9714 " Depending on truthiness, you should continue to wait for compilation to happen or stop execution.\n"),
9716 JS_FN_HELP("inIon", testingFunc_inIon
, 0, 0,
9718 " Returns true when called within ion. When ion is disabled or when compilation is abnormally\n"
9719 " slow to start, this function returns an error string. Otherwise, this function returns false.\n"
9720 " This behaviour ensures that a falsy value means that we are not in ion, but expect a\n"
9721 " compilation to occur in the future. Conversely, a truthy value means that we are either in\n"
9722 " ion or that there is litle or no chance of ion ever compiling the current script."),
9724 JS_FN_HELP("assertJitStackInvariants", TestingFunc_assertJitStackInvariants
, 0, 0,
9725 "assertJitStackInvariants()",
9726 " Iterates the Jit stack and check that stack invariants hold."),
9728 JS_FN_HELP("setIonCheckGraphCoherency", SetIonCheckGraphCoherency
, 1, 0,
9729 "setIonCheckGraphCoherency(bool)",
9730 " Set whether Ion should perform graph consistency (DEBUG-only) assertions. These assertions\n"
9731 " are valuable and should be generally enabled, however they can be very expensive for large\n"
9732 " (wasm) programs."),
9734 JS_FN_HELP("serialize", testingFunc_serialize
, 1, 0,
9735 "serialize(data, [transferables, [policy]])",
9736 " Serialize 'data' using JS_WriteStructuredClone. Returns a structured\n"
9737 " clone buffer object. 'policy' may be an options hash. Valid keys:\n"
9738 " 'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
9739 " to specify whether SharedArrayBuffers may be serialized.\n"
9740 " 'scope' - SameProcess, DifferentProcess, or\n"
9741 " DifferentProcessForIndexedDB. Determines how some values will be\n"
9742 " serialized. Clone buffers may only be deserialized with a compatible\n"
9743 " scope. NOTE - For DifferentProcess/DifferentProcessForIndexedDB,\n"
9744 " must also set SharedArrayBuffer:'deny' if data contains any shared memory\n"
9747 JS_FN_HELP("deserialize", Deserialize
, 1, 0,
9748 "deserialize(clonebuffer[, opts])",
9749 " Deserialize data generated by serialize. 'opts' may be an options hash.\n"
9751 " 'SharedArrayBuffer' - either 'allow' or 'deny' (the default)\n"
9752 " to specify whether SharedArrayBuffers may be serialized.\n"
9753 " 'scope', which limits the clone buffers that are considered\n"
9754 " valid. Allowed values: ''SameProcess', 'DifferentProcess',\n"
9755 " and 'DifferentProcessForIndexedDB'. So for example, a\n"
9756 " DifferentProcessForIndexedDB clone buffer may be deserialized in any scope, but\n"
9757 " a SameProcess clone buffer cannot be deserialized in a\n"
9758 " DifferentProcess scope."),
9760 JS_FN_HELP("detachArrayBuffer", DetachArrayBuffer
, 1, 0,
9761 "detachArrayBuffer(buffer)",
9762 " Detach the given ArrayBuffer object from its memory, i.e. as if it\n"
9763 " had been transferred to a WebWorker."),
9765 JS_FN_HELP("makeSerializable", MakeSerializable
, 1, 0,
9766 "makeSerializable(numeric id, [behavior])",
9767 " Make a custom serializable, transferable object. It will have a single accessor\n"
9768 " obj.log that will give a history of all operations on all such objects in the\n"
9769 " current thread as an array [id, action, id, action, ...] where the id\n"
9770 " is the number passed into this function, and the action is one of:\n"
9771 " ? - the canTransfer() hook was called.\n"
9772 " w - the write() hook was called.\n"
9773 " W - the writeTransfer() hook was called.\n"
9774 " R - the readTransfer() hook was called.\n"
9775 " r - the read() hook was called.\n"
9776 " F - the freeTransfer() hook was called.\n"
9777 " The `behavior` parameter can be used to force a failure during processing:\n"
9778 " 1 - fail during readTransfer() hook\n"
9779 " 2 - fail during read() hook\n"
9780 " Set the log to null to clear it."),
9782 JS_FN_HELP("ensureNonInline", EnsureNonInline
, 1, 0,
9783 "ensureNonInline(view or buffer)",
9784 " Ensure that the memory for the given ArrayBuffer or ArrayBufferView\n"
9787 JS_FN_HELP("JSONStringify", JSONStringify
, 4, 0,
9788 "JSONStringify(value, behavior)",
9789 " Same as JSON.stringify(value), but allows setting behavior:\n"
9790 " Normal: the default\n"
9791 " FastOnly: throw if the fast path bails\n"
9792 " SlowOnly: skip the fast path entirely\n"
9793 " Compare: run both the fast and slow paths and compare the result. Crash if\n"
9794 " they do not match. If the fast path bails, no comparison is done."),
9796 JS_FN_HELP("helperThreadCount", HelperThreadCount
, 0, 0,
9797 "helperThreadCount()",
9798 " Returns the number of helper threads available for off-thread tasks."),
9800 JS_FN_HELP("createShapeSnapshot", CreateShapeSnapshot
, 1, 0,
9801 "createShapeSnapshot(obj)",
9802 " Returns an object containing a shape snapshot for use with\n"
9803 " checkShapeSnapshot.\n"),
9805 JS_FN_HELP("checkShapeSnapshot", CheckShapeSnapshot
, 2, 0,
9806 "checkShapeSnapshot(snapshot, [obj])",
9807 " Check shape invariants based on the given snapshot and optional object.\n"
9808 " If there's no object argument, the snapshot's object is used.\n"),
9810 JS_FN_HELP("enableShapeConsistencyChecks", EnableShapeConsistencyChecks
, 0, 0,
9811 "enableShapeConsistencyChecks()",
9812 " Enable some slow Shape assertions.\n"),
9814 JS_FN_HELP("reportOutOfMemory", ReportOutOfMemory
, 0, 0,
9815 "reportOutOfMemory()",
9816 " Report OOM, then clear the exception and return undefined. For crash testing."),
9818 JS_FN_HELP("throwOutOfMemory", ThrowOutOfMemory
, 0, 0,
9819 "throwOutOfMemory()",
9820 " Throw out of memory exception, for OOM handling testing."),
9822 JS_FN_HELP("reportLargeAllocationFailure", ReportLargeAllocationFailure
, 0, 0,
9823 "reportLargeAllocationFailure([bytes])",
9824 " Call the large allocation failure callback, as though a large malloc call failed,\n"
9825 " then return undefined. In Gecko, this sends a memory pressure notification, which\n"
9826 " can free up some memory."),
9828 JS_FN_HELP("findPath", FindPath
, 2, 0,
9829 "findPath(start, target)",
9830 " Return an array describing one of the shortest paths of GC heap edges from\n"
9831 " |start| to |target|, or |undefined| if |target| is unreachable from |start|.\n"
9832 " Each element of the array is either of the form:\n"
9833 " { node: <object or string>, edge: <string describing edge from node> }\n"
9834 " if the node is a JavaScript object or value; or of the form:\n"
9835 " { type: <string describing node>, edge: <string describing edge> }\n"
9836 " if the node is some internal thing that is not a proper JavaScript value\n"
9837 " (like a shape or a scope chain element). The destination of the i'th array\n"
9838 " element's edge is the node of the i+1'th array element; the destination of\n"
9839 " the last array element is implicitly |target|.\n"),
9842 JS_FN_HELP("wasmMetadataAnalysis", wasmMetadataAnalysis
, 1, 0,
9843 "wasmMetadataAnalysis(wasmObject)",
9844 " Prints an analysis of the size of metadata on this wasm object.\n"),
9847 #if defined(DEBUG) || defined(JS_JITSPEW)
9848 JS_FN_HELP("dumpObject", DumpObject
, 1, 0,
9850 " Dump an internal representation of an object."),
9853 JS_FN_HELP("sharedMemoryEnabled", SharedMemoryEnabled
, 0, 0,
9854 "sharedMemoryEnabled()",
9855 " Return true if SharedArrayBuffer and Atomics are enabled"),
9857 JS_FN_HELP("sharedArrayRawBufferRefcount", SharedArrayRawBufferRefcount
, 0, 0,
9858 "sharedArrayRawBufferRefcount(sab)",
9859 " Return the reference count of the SharedArrayRawBuffer object held by sab"),
9861 #ifdef NIGHTLY_BUILD
9862 JS_FN_HELP("objectAddress", ObjectAddress
, 1, 0,
9863 "objectAddress(obj)",
9864 " Return the current address of the object. For debugging only--this\n"
9865 " address may change during a moving GC."),
9867 JS_FN_HELP("sharedAddress", SharedAddress
, 1, 0,
9868 "sharedAddress(obj)",
9869 " Return the address of the shared storage of a SharedArrayBuffer."),
9872 JS_FN_HELP("hasInvalidatedTeleporting", HasInvalidatedTeleporting
, 1, 0,
9873 "hasInvalidatedTeleporting(obj)",
9874 " Return true if the shape teleporting optimization has been disabled for |obj|."),
9876 JS_FN_HELP("evalReturningScope", EvalReturningScope
, 1, 0,
9877 "evalReturningScope(scriptStr, [global])",
9878 " Evaluate the script in a new scope and return the scope.\n"
9879 " If |global| is present, clone the script to |global| before executing."),
9881 JS_FN_HELP("backtrace", DumpBacktrace
, 1, 0,
9883 " Dump out a brief backtrace."),
9885 JS_FN_HELP("getBacktrace", GetBacktrace
, 1, 0,
9886 "getBacktrace([options])",
9887 " Return the current stack as a string. Takes an optional options object,\n"
9888 " which may contain any or all of the boolean properties:\n"
9889 " options.args - show arguments to each function\n"
9890 " options.locals - show local variables in each frame\n"
9891 " options.thisprops - show the properties of the 'this' object of each frame\n"),
9893 JS_FN_HELP("byteSize", ByteSize
, 1, 0,
9895 " Return the size in bytes occupied by |value|, or |undefined| if value\n"
9896 " is not allocated in memory.\n"),
9898 JS_FN_HELP("byteSizeOfScript", ByteSizeOfScript
, 1, 0,
9899 "byteSizeOfScript(f)",
9900 " Return the size in bytes occupied by the function |f|'s JSScript.\n"),
9902 JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype
, 1, 0,
9903 "setImmutablePrototype(obj)",
9904 " Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n"
9905 " change it will fail. Return true if obj's [[Prototype]] was successfully made\n"
9906 " immutable (or if it already was immutable), false otherwise. Throws in case\n"
9907 " of internal error, or if the operation doesn't even make sense (for example,\n"
9908 " because the object is a revoked proxy)."),
9911 JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation
, 1, 0,
9912 "dumpStringRepresentation(str)",
9913 " Print a human-readable description of how the string |str| is represented.\n"),
9915 JS_FN_HELP("stringRepresentation", GetStringRepresentation
, 1, 0,
9916 "stringRepresentation(str)",
9917 " Return a human-readable description of how the string |str| is represented.\n"),
9921 JS_FN_HELP("allocationMarker", AllocationMarker
, 0, 0,
9922 "allocationMarker([options])",
9923 " Return a freshly allocated object whose [[Class]] name is\n"
9924 " \"AllocationMarker\". Such objects are allocated only by calls\n"
9925 " to this function, never implicitly by the system, making them\n"
9926 " suitable for use in allocation tooling tests. Takes an optional\n"
9927 " options object which may contain the following properties:\n"
9928 " * nursery: bool, whether to allocate the object in the nursery\n"),
9930 JS_FN_HELP("setGCCallback", SetGCCallback
, 1, 0,
9931 "setGCCallback({action:\"...\", options...})",
9932 " Set the GC callback. action may be:\n"
9933 " 'minorGC' - run a nursery collection\n"
9934 " 'majorGC' - run a major collection, nesting up to a given 'depth'\n"),
9937 JS_FN_HELP("enqueueMark", EnqueueMark
, 1, 0,
9938 "enqueueMark(obj|string)",
9939 " Add an object to the queue of objects to mark at the beginning every GC. (Note\n"
9940 " that the objects will actually be marked at the beginning of every slice, but\n"
9941 " after the first slice they will already be marked so nothing will happen.)\n"
9943 " Instead of an object, a few magic strings may be used:\n"
9944 " 'yield' - cause the current marking slice to end, as if the mark budget were\n"
9946 " 'enter-weak-marking-mode' - divide the list into two segments. The items after\n"
9947 " this string will not be marked until we enter weak marking mode. Note that weak\n"
9948 " marking mode may be entered zero or multiple times for one GC.\n"
9949 " 'abort-weak-marking-mode' - same as above, but then abort weak marking to fall back\n"
9950 " on the old iterative marking code path.\n"
9951 " 'drain' - fully drain the mark stack before continuing.\n"
9952 " 'set-color-black' - force everything following in the mark queue to be marked black.\n"
9953 " 'set-color-gray' - continue with the regular GC until gray marking is possible, then force\n"
9954 " everything following in the mark queue to be marked gray.\n"
9955 " 'unset-color' - stop forcing the mark color."),
9957 JS_FN_HELP("clearMarkQueue", ClearMarkQueue
, 0, 0,
9959 " Cancel the special marking of all objects enqueue with enqueueMark()."),
9961 JS_FN_HELP("getMarkQueue", GetMarkQueue
, 0, 0,
9963 " Return the current mark queue set up via enqueueMark calls. Note that all\n"
9964 " returned values will be wrapped into the current compartment, so this loses\n"
9968 JS_FN_HELP("nurseryStringsEnabled", NurseryStringsEnabled
, 0, 0,
9969 "nurseryStringsEnabled()",
9970 " Return whether strings are currently allocated in the nursery for the current\n"
9973 JS_FN_HELP("isNurseryAllocated", IsNurseryAllocated
, 1, 0,
9974 "isNurseryAllocated(thing)",
9975 " Return whether a GC thing is nursery allocated.\n"),
9977 JS_FN_HELP("numAllocSitesPretenured", NumAllocSitesPretenured
, 0, 0,
9978 "numAllocSitesPretenured()",
9979 " Return the number of allocation sites that were pretenured for the current\n"
9982 JS_FN_HELP("getLcovInfo", GetLcovInfo
, 1, 0,
9983 "getLcovInfo(global)",
9984 " Generate LCOV tracefile for the given compartment. If no global are provided then\n"
9985 " the current global is used as the default one.\n"),
9988 JS_FN_HELP("setRNGState", SetRNGState
, 2, 0,
9989 "setRNGState(seed0, seed1)",
9990 " Set this compartment's RNG state.\n"),
9994 JS_FN_HELP("aflloop", AflLoop
, 1, 0,
9996 " Call the __AFL_LOOP() runtime function (see AFL docs)\n"),
9999 JS_FN_HELP("monotonicNow", MonotonicNow
, 0, 0,
10001 " Return a timestamp reflecting the current elapsed system time.\n"
10002 " This is monotonically increasing.\n"),
10004 JS_FN_HELP("timeSinceCreation", TimeSinceCreation
, 0, 0,
10005 "TimeSinceCreation()",
10006 " Returns the time in milliseconds since process creation.\n"
10007 " This uses a clock compatible with the profiler.\n"),
10009 JS_FN_HELP("isConstructor", IsConstructor
, 1, 0,
10010 "isConstructor(value)",
10011 " Returns whether the value is considered IsConstructor.\n"),
10013 JS_FN_HELP("getTimeZone", GetTimeZone
, 0, 0,
10015 " Get the current time zone.\n"),
10017 JS_FN_HELP("getDefaultLocale", GetDefaultLocale
, 0, 0,
10018 "getDefaultLocale()",
10019 " Get the current default locale.\n"),
10021 JS_FN_HELP("getCoreCount", GetCoreCount
, 0, 0,
10023 " Get the number of CPU cores from the platform layer. Typically this\n"
10024 " means the number of hyperthreads on systems where that makes sense.\n"),
10026 JS_FN_HELP("setTimeResolution", SetTimeResolution
, 2, 0,
10027 "setTimeResolution(resolution, jitter)",
10028 " Enables time clamping and jittering. Specify a time resolution in\n"
10029 " microseconds and whether or not to jitter\n"),
10031 JS_FN_HELP("scriptedCallerGlobal", ScriptedCallerGlobal
, 0, 0,
10032 "scriptedCallerGlobal()",
10033 " Get the caller's global (or null). See JS::GetScriptedCallerGlobal.\n"),
10035 JS_FN_HELP("objectGlobal", ObjectGlobal
, 1, 0,
10036 "objectGlobal(obj)",
10037 " Returns the object's global object or null if the object is a wrapper.\n"),
10039 JS_FN_HELP("isSameCompartment", IsSameCompartment
, 2, 0,
10040 "isSameCompartment(obj1, obj2)",
10041 " Unwraps obj1 and obj2 and returns whether the unwrapped objects are\n"
10042 " same-compartment.\n"),
10044 JS_FN_HELP("firstGlobalInCompartment", FirstGlobalInCompartment
, 1, 0,
10045 "firstGlobalInCompartment(obj)",
10046 " Returns the first global in obj's compartment.\n"),
10048 JS_FN_HELP("assertCorrectRealm", AssertCorrectRealm
, 0, 0,
10049 "assertCorrectRealm()",
10050 " Asserts cx->realm matches callee->realm.\n"),
10052 JS_FN_HELP("globalLexicals", GlobalLexicals
, 0, 0,
10053 "globalLexicals()",
10054 " Returns an object containing a copy of all global lexical bindings.\n"
10055 " Example use: let x = 1; assertEq(globalLexicals().x, 1);\n"),
10057 JS_FN_HELP("baselineCompile", BaselineCompile
, 2, 0,
10058 "baselineCompile([fun/code], forceDebugInstrumentation=false)",
10059 " Baseline-compiles the given JS function or script.\n"
10060 " Without arguments, baseline-compiles the caller's script; but note\n"
10061 " that extra boilerplate is needed afterwards to cause the VM to start\n"
10062 " running the jitcode rather than staying in the interpreter:\n"
10063 " baselineCompile(); for (var i=0; i<1; i++) {} ...\n"
10064 " The interpreter will enter the new jitcode at the loop header unless\n"
10065 " baselineCompile returned a string or threw an error.\n"),
10067 JS_FN_HELP("encodeAsUtf8InBuffer", EncodeAsUtf8InBuffer
, 2, 0,
10068 "encodeAsUtf8InBuffer(str, uint8Array)",
10069 " Encode as many whole code points from the string str into the provided\n"
10070 " Uint8Array as will completely fit in it, converting lone surrogates to\n"
10071 " REPLACEMENT CHARACTER. Return an array [r, w] where |r| is the\n"
10072 " number of 16-bit units read and |w| is the number of bytes of UTF-8\n"
10075 JS_FN_HELP("clearKeptObjects", ClearKeptObjects
, 0, 0,
10076 "clearKeptObjects()",
10077 "Perform the ECMAScript ClearKeptObjects operation, clearing the list of\n"
10078 "observed WeakRef targets that are kept alive until the next synchronous\n"
10079 "sequence of ECMAScript execution completes. This is used for testing\n"
10082 JS_FN_HELP("numberToDouble", NumberToDouble
, 1, 0,
10083 "numberToDouble(number)",
10084 " Return the input number as double-typed number."),
10086 JS_FN_HELP("getICUOptions", GetICUOptions
, 0, 0,
10088 " Return an object describing the following ICU options.\n\n"
10089 " version: a string containing the ICU version number, e.g. '67.1'\n"
10090 " unicode: a string containing the Unicode version number, e.g. '13.0'\n"
10091 " locale: the ICU default locale, e.g. 'en_US'\n"
10092 " tzdata: a string containing the tzdata version number, e.g. '2020a'\n"
10093 " timezone: the ICU default time zone, e.g. 'America/Los_Angeles'\n"
10094 " host-timezone: the host time zone, e.g. 'America/Los_Angeles'"),
10096 JS_FN_HELP("getAvailableLocalesOf", GetAvailableLocalesOf
, 0, 0,
10097 "getAvailableLocalesOf(name)",
10098 " Return an array of all available locales for the given Intl constuctor."),
10100 JS_FN_HELP("isSmallFunction", IsSmallFunction
, 1, 0,
10101 "isSmallFunction(fun)",
10102 " Returns true if a scripted function is small enough to be inlinable."),
10104 JS_FN_HELP("compileToStencil", CompileToStencil
, 1, 0,
10105 "compileToStencil(string, [options])",
10106 " Parses the given string argument as js script, returns the stencil"
10109 JS_FN_HELP("evalStencil", EvalStencil
, 1, 0,
10110 "evalStencil(stencil, [options])",
10111 " Instantiates the given stencil, and evaluates the top-level script it"
10114 JS_FN_HELP("compileToStencilXDR", CompileToStencilXDR
, 1, 0,
10115 "compileToStencilXDR(string, [options])",
10116 " Parses the given string argument as js script, produces the stencil"
10117 " for it, XDR-encodes the stencil, and returns an object that contains the"
10120 JS_FN_HELP("evalStencilXDR", EvalStencilXDR
, 1, 0,
10121 "evalStencilXDR(stencilXDR, [options])",
10122 " Reads the given stencil XDR object, and evaluates the top-level script it"
10125 JS_FN_HELP("getExceptionInfo", GetExceptionInfo
, 1, 0,
10126 "getExceptionInfo(fun)",
10127 " Calls the given function and returns information about the exception it"
10128 " throws. Returns null if the function didn't throw an exception."),
10130 JS_FN_HELP("nukeCCW", NukeCCW
, 1, 0,
10131 "nukeCCW(wrapper)",
10132 " Nuke a CrossCompartmentWrapper, which turns it into a DeadProxyObject."),
10134 JS_FN_HELP("assertRealmFuseInvariants", AssertRealmFuseInvariants
, 0, 0,
10135 "assertRealmFuseInvariants()",
10136 " Runs the realm's fuse invariant checks -- these will crash on failure. "
10137 " Only available in fuzzing or debug builds, so usage should be guarded. "),
10139 JS_FN_HELP("popAllFusesInRealm", PopAllFusesInRealm
, 0, 0,
10140 "popAllFusesInRealm()",
10141 " Pops all the fuses in the current realm"),
10147 // clang-format off
10148 static const JSFunctionSpecWithHelp FuzzingUnsafeTestingFunctions
[] = {
10149 JS_FN_HELP("getErrorNotes", GetErrorNotes
, 1, 0,
10150 "getErrorNotes(error)",
10151 " Returns an array of error notes."),
10153 JS_FN_HELP("setTimeZone", SetTimeZone
, 1, 0,
10154 "setTimeZone(tzname)",
10155 " Set the 'TZ' environment variable to the given time zone and applies the new time zone.\n"
10156 " The time zone given is validated according to the current environment.\n"
10157 " An empty string or undefined resets the time zone to its default value."),
10159 JS_FN_HELP("setDefaultLocale", SetDefaultLocale
, 1, 0,
10160 "setDefaultLocale(locale)",
10161 " Set the runtime default locale to the given value.\n"
10162 " An empty string or undefined resets the runtime locale to its default value.\n"
10163 " NOTE: The input string is not fully validated, it must be a valid BCP-47 language tag."),
10165 JS_FN_HELP("isInStencilCache", IsInStencilCache
, 1, 0,
10166 "isInStencilCache(fun)",
10167 " True if fun is available in the stencil cache."),
10169 JS_FN_HELP("waitForStencilCache", WaitForStencilCache
, 1, 0,
10170 "waitForStencilCache(fun)",
10171 " Block main thread execution until the function is made available in the cache."),
10173 JS_FN_HELP("getInnerMostEnvironmentObject", GetInnerMostEnvironmentObject
, 0, 0,
10174 "getInnerMostEnvironmentObject()",
10175 " Return the inner-most environment object for current execution."),
10177 JS_FN_HELP("getEnclosingEnvironmentObject", GetEnclosingEnvironmentObject
, 1, 0,
10178 "getEnclosingEnvironmentObject(env)",
10179 " Return the enclosing environment object for given environment object."),
10181 JS_FN_HELP("getEnvironmentObjectType", GetEnvironmentObjectType
, 1, 0,
10182 "getEnvironmentObjectType(env)",
10183 " Return a string represents the type of given environment object."),
10185 JS_FN_HELP("shortestPaths", ShortestPaths
, 3, 0,
10186 "shortestPaths(targets, options)",
10187 " Return an array of arrays of shortest retaining paths. There is an array of\n"
10188 " shortest retaining paths for each object in |targets|. Each element in a path\n"
10189 " is of the form |{ predecessor, edge }|. |options| may contain:\n"
10191 " maxNumPaths: The maximum number of paths returned in each of those arrays\n"
10193 " start: The object to start all paths from. If not given, then\n"
10194 " the starting point will be the set of GC roots."),
10196 JS_FN_HELP("getFuseState", GetFuseState
, 0, 0,
10198 " Return an object describing the calling realm's fuse state, "
10199 "as well as the state of any runtime fuses."),
10205 // clang-format off
10206 static const JSFunctionSpecWithHelp PCCountProfilingTestingFunctions
[] = {
10207 JS_FN_HELP("start", PCCountProfiling_Start
, 0, 0,
10209 " Start PC count profiling."),
10211 JS_FN_HELP("stop", PCCountProfiling_Stop
, 0, 0,
10213 " Stop PC count profiling."),
10215 JS_FN_HELP("purge", PCCountProfiling_Purge
, 0, 0,
10217 " Purge the collected PC count profiling data."),
10219 JS_FN_HELP("count", PCCountProfiling_ScriptCount
, 0, 0,
10221 " Return the number of profiled scripts."),
10223 JS_FN_HELP("summary", PCCountProfiling_ScriptSummary
, 1, 0,
10225 " Return the PC count profiling summary for the given script index.\n"
10226 " The script index must be in the range [0, pc.count())."),
10228 JS_FN_HELP("contents", PCCountProfiling_ScriptContents
, 1, 0,
10230 " Return the complete profiling contents for the given script index.\n"
10231 " The script index must be in the range [0, pc.count())."),
10237 // clang-format off
10238 static const JSFunctionSpecWithHelp FdLibMTestingFunctions
[] = {
10239 JS_FN_HELP("pow", FdLibM_Pow
, 2, 0,
10241 " Return x ** y."),
10247 bool js::InitTestingFunctions() { return disasmBuf
.init(); }
10249 bool js::DefineTestingFunctions(JSContext
* cx
, HandleObject obj
,
10250 bool fuzzingSafe_
, bool disableOOMFunctions_
) {
10251 fuzzingSafe
= fuzzingSafe_
;
10252 if (EnvVarIsDefined("MOZ_FUZZING_SAFE")) {
10253 fuzzingSafe
= true;
10256 disableOOMFunctions
= disableOOMFunctions_
;
10258 if (!fuzzingSafe
) {
10259 if (!JS_DefineFunctionsWithHelp(cx
, obj
, FuzzingUnsafeTestingFunctions
)) {
10263 RootedObject
pccount(cx
, JS_NewPlainObject(cx
));
10268 if (!JS_DefineProperty(cx
, obj
, "pccount", pccount
, 0)) {
10272 if (!JS_DefineFunctionsWithHelp(cx
, pccount
,
10273 PCCountProfilingTestingFunctions
)) {
10278 RootedObject
fdlibm(cx
, JS_NewPlainObject(cx
));
10283 if (!JS_DefineProperty(cx
, obj
, "fdlibm", fdlibm
, 0)) {
10287 if (!JS_DefineFunctionsWithHelp(cx
, fdlibm
, FdLibMTestingFunctions
)) {
10291 return JS_DefineFunctionsWithHelp(cx
, obj
, TestingFunctions
);
10294 #ifdef FUZZING_JS_FUZZILLI
10295 uint32_t js::FuzzilliHashDouble(double value
) {
10296 // We shouldn't GC here as this is called directly from IC code.
10297 AutoUnsafeCallWithABI unsafe
;
10298 uint64_t v
= mozilla::BitwiseCast
<uint64_t>(value
);
10299 return static_cast<uint32_t>(v
) + static_cast<uint32_t>(v
>> 32);
10302 uint32_t js::FuzzilliHashBigInt(BigInt
* bigInt
) {
10303 // We shouldn't GC here as this is called directly from IC code.
10304 AutoUnsafeCallWithABI unsafe
;
10305 return bigInt
->hash();
10308 void js::FuzzilliHashObject(JSContext
* cx
, JSObject
* obj
) {
10309 // called from IC and baseline/interpreter
10311 FuzzilliHashObjectInl(cx
, obj
, &hash
);
10313 cx
->executionHashInputs
+= 1;
10314 cx
->executionHash
= mozilla::RotateLeft(cx
->executionHash
+ hash
, 1);
10317 void js::FuzzilliHashObjectInl(JSContext
* cx
, JSObject
* obj
, uint32_t* out
) {
10319 if (!js::SupportDifferentialTesting()) {
10326 JSAutoStructuredCloneBuffer
JSCloner(
10327 JS::StructuredCloneScope::DifferentProcess
, nullptr, nullptr);
10328 if (JSCloner
.write(cx
, v
)) {
10329 JSStructuredCloneData
& data
= JSCloner
.data();
10330 data
.ForEachDataChunk([&](const char* aData
, size_t aSize
) {
10331 uint32_t h
= mozilla::HashBytes(aData
, aSize
);
10337 } else if (JS_IsExceptionPending(cx
)) {
10338 JS_ClearPendingException(cx
);