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/. */
9 #include "mozilla/AlreadyAddRefed.h" // mozilla::already_AddRefed
10 #include "mozilla/ArrayUtils.h"
11 #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_ASSERT_IF, MOZ_RELEASE_ASSERT, MOZ_CRASH
12 #include "mozilla/Atomics.h"
13 #include "mozilla/Attributes.h"
14 #include "mozilla/DebugOnly.h"
15 #include "mozilla/EnumSet.h"
16 #include "mozilla/IntegerPrintfMacros.h"
17 #include "mozilla/mozalloc.h"
18 #include "mozilla/PodOperations.h"
19 #include "mozilla/RandomNum.h"
20 #include "mozilla/RefPtr.h"
21 #include "mozilla/ScopeExit.h"
22 #include "mozilla/Sprintf.h"
23 #include "mozilla/TimeStamp.h"
24 #include "mozilla/UniquePtrExtensions.h" // UniqueFreePtr
25 #include "mozilla/Utf8.h"
26 #include "mozilla/Variant.h"
37 # include <io.h> /* for isatty() */
41 # include MALLOC_H /* for malloc_usable_size, malloc_size, _msize */
52 #include <sys/types.h>
56 # include <sys/mman.h>
57 # include <sys/wait.h>
59 # include <sys/stat.h>
63 # include <sys/prctl.h>
67 #include "jsfriendapi.h"
69 #ifndef JS_WITHOUT_NSPR
74 #include "builtin/Array.h"
75 #include "builtin/MapObject.h"
76 #include "builtin/ModuleObject.h"
77 #include "builtin/RegExp.h"
78 #include "builtin/TestingFunctions.h"
79 #include "builtin/TestingUtility.h" // js::ParseCompileOptions, js::ParseDebugMetadata, js::CreateScriptPrivate
80 #include "debugger/DebugAPI.h"
81 #include "frontend/BytecodeCompiler.h" // frontend::{CompileGlobalScriptToExtensibleStencil, CompileModule, ParseModuleToExtensibleStencil}
82 #include "frontend/CompilationStencil.h"
83 #ifdef JS_ENABLE_SMOOSH
84 # include "frontend/Frontend2.h"
86 #include "frontend/FrontendContext.h" // AutoReportFrontendContext
87 #include "frontend/ModuleSharedContext.h"
88 #include "frontend/Parser.h"
89 #include "frontend/ScopeBindingCache.h" // js::frontend::ScopeBindingCache
91 #include "gc/PublicIterators.h"
93 # include "irregexp/RegExpAPI.h"
96 #ifdef JS_SIMULATOR_ARM
97 # include "jit/arm/Simulator-arm.h"
99 #ifdef JS_SIMULATOR_MIPS32
100 # include "jit/mips32/Simulator-mips32.h"
102 #ifdef JS_SIMULATOR_MIPS64
103 # include "jit/mips64/Simulator-mips64.h"
105 #ifdef JS_SIMULATOR_LOONG64
106 # include "jit/loong64/Simulator-loong64.h"
108 #ifdef JS_SIMULATOR_RISCV64
109 # include "jit/riscv64/Simulator-riscv64.h"
111 #include "jit/CacheIRHealth.h"
112 #include "jit/InlinableNatives.h"
114 #include "jit/JitcodeMap.h"
115 #include "jit/JitZone.h"
116 #include "jit/shared/CodeGenerator-shared.h"
117 #include "js/Array.h" // JS::NewArrayObject
118 #include "js/ArrayBuffer.h" // JS::{CreateMappedArrayBufferContents,NewMappedArrayBufferWithContents,IsArrayBufferObject,GetArrayBufferLengthAndData}
119 #include "js/BuildId.h" // JS::BuildIdCharVector, JS::SetProcessBuildIdOp
120 #include "js/CallAndConstruct.h" // JS::Call, JS::IsCallable, JS_CallFunction, JS_CallFunctionValue
121 #include "js/CharacterEncoding.h" // JS::StringIsASCII
122 #include "js/CompilationAndEvaluation.h"
123 #include "js/CompileOptions.h" // JS::ReadOnlyCompileOptions, JS::CompileOptions, JS::OwningCompileOptions, JS::DecodeOptions, JS::InstantiateOptions
124 #include "js/ContextOptions.h" // JS::ContextOptions{,Ref}
125 #include "js/Debug.h"
126 #include "js/Equality.h" // JS::SameValue
127 #include "js/ErrorReport.h" // JS::PrintError
128 #include "js/Exception.h" // JS::StealPendingExceptionStack
129 #include "js/experimental/CodeCoverage.h" // js::EnableCodeCoverage
130 #include "js/experimental/CompileScript.h" // JS::NewFrontendContext, JS::DestroyFrontendContext, JS::HadFrontendErrors, JS::ConvertFrontendErrorsToRuntimeErrors, JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil, JS::CompilationStorage
131 #include "js/experimental/CTypes.h" // JS::InitCTypesClass
132 #include "js/experimental/Intl.h" // JS::AddMoz{DateTimeFormat,DisplayNames}Constructor
133 #include "js/experimental/JitInfo.h" // JSJit{Getter,Setter,Method}CallArgs, JSJitGetterInfo, JSJit{Getter,Setter}Op, JSJitInfo
134 #include "js/experimental/JSStencil.h" // JS::Stencil, JS::DecodeStencil
135 #include "js/experimental/SourceHook.h" // js::{Set,Forget,}SourceHook
136 #include "js/experimental/TypedData.h" // JS_NewUint8Array
137 #include "js/friend/DumpFunctions.h" // JS::FormatStackDump
138 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
139 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit
140 #include "js/friend/WindowProxy.h" // js::IsWindowProxy, js::SetWindowProxyClass, js::ToWindowProxyIfWindow, js::ToWindowIfWindowProxy
141 #include "js/GCAPI.h" // JS::AutoCheckCannotGC
142 #include "js/GCVector.h"
143 #include "js/GlobalObject.h"
144 #include "js/Initialization.h"
145 #include "js/Interrupt.h"
147 #include "js/MemoryCallbacks.h"
148 #include "js/MemoryFunctions.h"
149 #include "js/Modules.h" // JS::GetModulePrivate, JS::SetModule{DynamicImport,Metadata,Resolve}Hook, JS::SetModulePrivate
150 #include "js/Object.h" // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, JS::SetReservedSlot
151 #include "js/Principals.h"
152 #include "js/Printer.h" // QuoteString
153 #include "js/Printf.h"
154 #include "js/PropertyAndElement.h" // JS_DefineElement, JS_DefineFunction, JS_DefineFunctions, JS_DefineProperties, JS_DefineProperty, JS_GetElement, JS_GetProperty, JS_GetPropertyById, JS_HasProperty, JS_SetElement, JS_SetProperty, JS_SetPropertyById
155 #include "js/PropertySpec.h"
156 #include "js/Realm.h"
157 #include "js/RegExp.h" // JS::ObjectIsRegExp
158 #include "js/ScriptPrivate.h"
159 #include "js/SourceText.h" // JS::SourceText
160 #include "js/StableStringChars.h"
161 #include "js/Stack.h"
162 #include "js/StreamConsumer.h"
163 #include "js/StructuredClone.h"
164 #include "js/Transcoding.h" // JS::TranscodeBuffer, JS::TranscodeRange, JS::IsTranscodeFailureResult
165 #include "js/Warnings.h" // JS::SetWarningReporter
166 #include "js/WasmModule.h" // JS::WasmModule
167 #include "js/Wrapper.h"
168 #include "proxy/DeadObjectProxy.h" // js::IsDeadProxyObject
169 #include "shell/jsoptparse.h"
170 #include "shell/jsshell.h"
171 #include "shell/OSObject.h"
172 #include "shell/ShellModuleObjectWrapper.h"
173 #include "shell/WasmTesting.h"
174 #include "threading/ConditionVariable.h"
175 #include "threading/ExclusiveData.h"
176 #include "threading/LockGuard.h"
177 #include "threading/Thread.h"
178 #include "util/CompleteFile.h" // js::FileContents, js::ReadCompleteFile
179 #include "util/DifferentialTesting.h"
180 #include "util/StringBuffer.h"
181 #include "util/Text.h"
182 #include "util/WindowsWrapper.h"
183 #include "vm/ArgumentsObject.h"
184 #include "vm/Compression.h"
185 #include "vm/ErrorObject.h"
186 #include "vm/ErrorReporting.h"
187 #include "vm/HelperThreads.h"
188 #include "vm/JSAtomUtils.h" // AtomizeUTF8Chars, AtomizeString, ToAtom
189 #include "vm/JSContext.h"
190 #include "vm/JSFunction.h"
191 #include "vm/JSObject.h"
192 #include "vm/JSScript.h"
193 #include "vm/ModuleBuilder.h" // js::ModuleBuilder
194 #include "vm/Modules.h"
195 #include "vm/Monitor.h"
196 #include "vm/MutexIDs.h"
197 #include "vm/PromiseObject.h" // js::PromiseObject
198 #include "vm/Shape.h"
199 #include "vm/SharedArrayObject.h"
200 #include "vm/StencilObject.h" // js::StencilObject
202 #include "vm/ToSource.h" // js::ValueToSource
203 #include "vm/TypedArrayObject.h"
204 #include "vm/WrapperObject.h"
205 #include "wasm/WasmFeatures.h"
206 #include "wasm/WasmJS.h"
208 #include "vm/Compartment-inl.h"
209 #include "vm/ErrorObject-inl.h"
210 #include "vm/Interpreter-inl.h"
211 #include "vm/JSObject-inl.h"
212 #include "vm/Realm-inl.h"
213 #include "vm/Stack-inl.h"
216 using namespace js::cli
;
217 using namespace js::shell
;
219 using JS::AutoStableStringChars
;
220 using JS::CompileOptions
;
222 using js::shell::RCFile
;
224 using mozilla::ArrayEqual
;
225 using mozilla::AsVariant
;
226 using mozilla::Atomic
;
227 using mozilla::MakeScopeExit
;
228 using mozilla::Maybe
;
229 using mozilla::Nothing
;
230 using mozilla::NumberEqualsInt32
;
231 using mozilla::TimeDuration
;
232 using mozilla::TimeStamp
;
233 using mozilla::Utf8Unit
;
234 using mozilla::Variant
;
236 bool InitOptionParser(OptionParser
& op
);
237 bool SetGlobalOptionsPreJSInit(const OptionParser
& op
);
238 bool SetGlobalOptionsPostJSInit(const OptionParser
& op
);
239 bool SetContextOptions(JSContext
* cx
, const OptionParser
& op
);
240 bool SetContextWasmOptions(JSContext
* cx
, const OptionParser
& op
);
241 bool SetContextJITOptions(JSContext
* cx
, const OptionParser
& op
);
242 bool SetContextGCOptions(JSContext
* cx
, const OptionParser
& op
);
243 bool InitModuleLoader(JSContext
* cx
, const OptionParser
& op
);
245 #ifdef FUZZING_JS_FUZZILLI
246 # define REPRL_CRFD 100
247 # define REPRL_CWFD 101
248 # define REPRL_DRFD 102
249 # define REPRL_DWFD 103
251 # define SHM_SIZE 0x100000
252 # define MAX_EDGES ((SHM_SIZE - 4) * 8)
256 unsigned char edges
[];
259 struct shmem_data
* __shmem
;
261 uint32_t *__edges_start
, *__edges_stop
;
262 void __sanitizer_cov_reset_edgeguards() {
264 for (uint32_t* x
= __edges_start
; x
< __edges_stop
&& N
< MAX_EDGES
; x
++)
268 extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t* start
,
270 // Avoid duplicate initialization
271 if (start
== stop
|| *start
) return;
273 if (__edges_start
!= NULL
|| __edges_stop
!= NULL
) {
275 "Coverage instrumentation is only supported for a single module\n");
279 __edges_start
= start
;
282 // Map the shared memory region
283 const char* shm_key
= getenv("SHM_ID");
285 puts("[COV] no shared memory bitmap available, skipping");
286 __shmem
= (struct shmem_data
*)malloc(SHM_SIZE
);
288 int fd
= shm_open(shm_key
, O_RDWR
, S_IREAD
| S_IWRITE
);
290 fprintf(stderr
, "Failed to open shared memory region: %s\n",
295 __shmem
= (struct shmem_data
*)mmap(0, SHM_SIZE
, PROT_READ
| PROT_WRITE
,
297 if (__shmem
== MAP_FAILED
) {
298 fprintf(stderr
, "Failed to mmap shared memory region\n");
303 __sanitizer_cov_reset_edgeguards();
305 __shmem
->num_edges
= stop
- start
;
306 printf("[COV] edge counters initialized. Shared memory: %s with %u edges\n",
307 shm_key
, __shmem
->num_edges
);
310 extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t* guard
) {
311 // There's a small race condition here: if this function executes in two
312 // threads for the same edge at the same time, the first thread might disable
313 // the edge (by setting the guard to zero) before the second thread fetches
314 // the guard value (and thus the index). However, our instrumentation ignores
315 // the first edge (see libcoverage.c) and so the race is unproblematic.
316 uint32_t index
= *guard
;
317 // If this function is called before coverage instrumentation is properly
318 // initialized we want to return early.
320 __shmem
->edges
[index
/ 8] |= 1 << (index
% 8);
323 #endif /* FUZZING_JS_FUZZILLI */
325 enum JSShellExitCode
{
326 EXITCODE_RUNTIME_ERROR
= 3,
327 EXITCODE_FILE_NOT_FOUND
= 4,
328 EXITCODE_OUT_OF_MEMORY
= 5,
333 * Limit the timeout to 30 minutes to prevent an overflow on platfoms
334 * that represent the time internally in microseconds using 32-bit int.
336 static const double MAX_TIMEOUT_SECONDS
= 1800.0;
338 // Not necessarily in sync with the browser
339 #ifdef ENABLE_SHARED_MEMORY
340 # define SHARED_MEMORY_DEFAULT 1
342 # define SHARED_MEMORY_DEFAULT 0
345 // Fuzzing support for JS runtime fuzzing
346 #ifdef FUZZING_INTERFACES
347 # include "shell/jsrtfuzzing/jsrtfuzzing.h"
348 static bool fuzzDoDebug
= !!getenv("MOZ_FUZZ_DEBUG");
349 static bool fuzzHaveModule
= !!getenv("FUZZER");
350 #endif // FUZZING_INTERFACES
352 // Code to support GCOV code coverage measurements on standalone shell
353 #ifdef MOZ_CODE_COVERAGE
354 # if defined(__GNUC__) && !defined(__clang__)
355 extern "C" void __gcov_dump();
356 extern "C" void __gcov_reset();
358 void counters_dump(int) { __gcov_dump(); }
360 void counters_reset(int) { __gcov_reset(); }
362 void counters_dump(int) { /* Do nothing */
365 void counters_reset(int) { /* Do nothing */
369 static void InstallCoverageSignalHandlers() {
371 fprintf(stderr
, "[CodeCoverage] Setting handlers for process %d.\n",
374 struct sigaction dump_sa
;
375 dump_sa
.sa_handler
= counters_dump
;
376 dump_sa
.sa_flags
= SA_RESTART
;
377 sigemptyset(&dump_sa
.sa_mask
);
378 mozilla::DebugOnly
<int> r1
= sigaction(SIGUSR1
, &dump_sa
, nullptr);
379 MOZ_ASSERT(r1
== 0, "Failed to install GCOV SIGUSR1 handler");
381 struct sigaction reset_sa
;
382 reset_sa
.sa_handler
= counters_reset
;
383 reset_sa
.sa_flags
= SA_RESTART
;
384 sigemptyset(&reset_sa
.sa_mask
);
385 mozilla::DebugOnly
<int> r2
= sigaction(SIGUSR2
, &reset_sa
, nullptr);
386 MOZ_ASSERT(r2
== 0, "Failed to install GCOV SIGUSR2 handler");
391 // An off-thread parse or decode job.
392 class js::shell::OffThreadJob
{
393 static constexpr size_t kCompileStackQuota
= 128 * sizeof(size_t) * 1024;
394 static constexpr size_t kThreadStackQuota
=
395 kCompileStackQuota
+ 128 * sizeof(size_t) * 1024;
398 RUNNING
, // Working; no stencil.
399 DONE
, // Finished; have stencil.
400 CANCELLED
// Cancelled due to error.
410 OffThreadJob(ShellContext
* sc
, Kind kind
, JS::SourceText
<char16_t
>&& srcBuf
);
411 OffThreadJob(ShellContext
* sc
, Kind kind
, JS::TranscodeBuffer
&& xdrBuf
);
415 bool init(JSContext
* cx
, const JS::ReadOnlyCompileOptions
& options
);
418 static void OffThreadMain(OffThreadJob
* self
);
422 void waitUntilDone();
424 already_AddRefed
<JS::Stencil
> stealStencil(JSContext
* cx
);
433 JS::FrontendContext
* fc_
= nullptr;
434 JS::OwningCompileOptions options_
;
436 UniquePtr
<Thread
> thread_
;
438 JS::SourceText
<char16_t
> srcBuf_
;
439 JS::TranscodeBuffer xdrBuf_
;
441 RefPtr
<JS::Stencil
> stencil_
;
443 JS::TranscodeResult transcodeResult_
= JS::TranscodeResult::Ok
;
446 template <typename T
>
447 static OffThreadJob
* NewOffThreadJob(JSContext
* cx
, OffThreadJob::Kind kind
,
448 JS::ReadOnlyCompileOptions
& options
,
450 ShellContext
* sc
= GetShellContext(cx
);
452 // Off-thread compilation/decode is used by main-thread, in order to improve
453 // the responsiveness. It's not used by worker in browser, and there's not
454 // much reason to support worker here.
455 JS_ReportErrorASCII(cx
, "Off-thread job is not supported in worker");
459 UniquePtr
<OffThreadJob
> job(
460 cx
->new_
<OffThreadJob
>(sc
, kind
, std::move(source
)));
465 if (!job
->init(cx
, options
)) {
469 if (!sc
->offThreadJobs
.append(job
.get())) {
471 JS_ReportErrorASCII(cx
, "OOM adding off-thread job");
475 return job
.release();
478 static OffThreadJob
* GetSingleOffThreadJob(JSContext
* cx
) {
479 ShellContext
* sc
= GetShellContext(cx
);
480 const auto& jobs
= sc
->offThreadJobs
;
482 JS_ReportErrorASCII(cx
, "No off-thread jobs are pending");
486 if (jobs
.length() > 1) {
488 cx
, "Multiple off-thread jobs are pending: must specify job ID");
495 static OffThreadJob
* LookupOffThreadJobByID(JSContext
* cx
, int32_t id
) {
497 JS_ReportErrorASCII(cx
, "Bad off-thread job ID");
501 ShellContext
* sc
= GetShellContext(cx
);
502 const auto& jobs
= sc
->offThreadJobs
;
504 JS_ReportErrorASCII(cx
, "No off-thread jobs are pending");
508 OffThreadJob
* job
= nullptr;
509 for (auto someJob
: jobs
) {
510 if (someJob
->id
== id
) {
517 JS_ReportErrorASCII(cx
, "Off-thread job not found");
524 static OffThreadJob
* LookupOffThreadJobForArgs(JSContext
* cx
,
525 const CallArgs
& args
,
527 // If the optional ID argument isn't present, get the single pending job.
528 if (args
.length() <= arg
) {
529 return GetSingleOffThreadJob(cx
);
532 // Lookup the job using the specified ID.
534 RootedValue
value(cx
, args
[arg
]);
535 if (!ToInt32(cx
, value
, &id
)) {
539 return LookupOffThreadJobByID(cx
, id
);
542 static void DeleteOffThreadJob(JSContext
* cx
, OffThreadJob
* job
) {
543 ShellContext
* sc
= GetShellContext(cx
);
544 for (size_t i
= 0; i
< sc
->offThreadJobs
.length(); i
++) {
545 if (sc
->offThreadJobs
[i
] == job
) {
546 sc
->offThreadJobs
.erase(&sc
->offThreadJobs
[i
]);
552 MOZ_CRASH("Off-thread job not found");
555 static void CancelOffThreadJobsForRuntime(JSContext
* cx
) {
556 ShellContext
* sc
= GetShellContext(cx
);
557 while (!sc
->offThreadJobs
.empty()) {
558 OffThreadJob
* job
= sc
->offThreadJobs
.popCopy();
559 job
->waitUntilDone();
564 mozilla::Atomic
<int32_t> gOffThreadJobSerial(1);
566 OffThreadJob::OffThreadJob(ShellContext
* sc
, Kind kind
,
567 JS::SourceText
<char16_t
>&& srcBuf
)
568 : id(gOffThreadJobSerial
++),
571 options_(JS::OwningCompileOptions::ForFrontendContext()),
572 srcBuf_(std::move(srcBuf
)) {
573 MOZ_RELEASE_ASSERT(id
> 0, "Off-thread job IDs exhausted");
576 OffThreadJob::OffThreadJob(ShellContext
* sc
, Kind kind
,
577 JS::TranscodeBuffer
&& xdrBuf
)
578 : id(gOffThreadJobSerial
++),
581 options_(JS::OwningCompileOptions::ForFrontendContext()),
582 xdrBuf_(std::move(xdrBuf
)) {
583 MOZ_RELEASE_ASSERT(id
> 0, "Off-thread job IDs exhausted");
586 OffThreadJob::~OffThreadJob() {
588 JS::DestroyFrontendContext(fc_
);
590 MOZ_ASSERT(state_
!= RUNNING
);
593 bool OffThreadJob::init(JSContext
* cx
,
594 const JS::ReadOnlyCompileOptions
& options
) {
595 fc_
= JS::NewFrontendContext();
597 ReportOutOfMemory(cx
);
602 if (!options_
.copy(cx
, options
)) {
610 bool OffThreadJob::dispatch() {
612 js::MakeUnique
<Thread
>(Thread::Options().setStackSize(kThreadStackQuota
));
618 if (!thread_
->init(OffThreadJob::OffThreadMain
, this)) {
627 /* static */ void OffThreadJob::OffThreadMain(OffThreadJob
* self
) {
631 void OffThreadJob::run() {
632 MOZ_ASSERT(state_
== RUNNING
);
633 MOZ_ASSERT(!stencil_
);
635 JS::SetNativeStackQuota(fc_
, kCompileStackQuota
);
638 case Kind::CompileScript
: {
639 JS::CompilationStorage compileStorage
;
640 stencil_
= JS::CompileGlobalScriptToStencil(fc_
, options_
, srcBuf_
,
644 case Kind::CompileModule
: {
645 JS::CompilationStorage compileStorage
;
646 stencil_
= JS::CompileModuleScriptToStencil(fc_
, options_
, srcBuf_
,
651 JS::DecodeOptions
decodeOptions(options_
);
652 JS::TranscodeRange
range(xdrBuf_
.begin(), xdrBuf_
.length());
653 transcodeResult_
= JS::DecodeStencil(fc_
, decodeOptions
, range
,
654 getter_AddRefs(stencil_
));
662 void OffThreadJob::cancel() {
663 MOZ_ASSERT(state_
== RUNNING
);
664 MOZ_ASSERT(!stencil_
);
665 MOZ_ASSERT(!thread_
, "cannot cancel after starting a thread");
670 void OffThreadJob::waitUntilDone() {
671 MOZ_ASSERT(state_
!= CANCELLED
);
675 already_AddRefed
<JS::Stencil
> OffThreadJob::stealStencil(JSContext
* cx
) {
676 JS::FrontendContext
* fc
= fc_
;
678 auto destroyFrontendContext
=
679 mozilla::MakeScopeExit([&]() { JS::DestroyFrontendContext(fc
); });
683 if (JS::HadFrontendErrors(fc
)) {
684 (void)JS::ConvertFrontendErrorsToRuntimeErrors(cx
, fc
, options_
);
688 if (!stencil_
&& JS::IsTranscodeFailureResult(transcodeResult_
)) {
689 JS_ReportErrorASCII(cx
, "failed to decode cache");
694 if (!JS::ConvertFrontendErrorsToRuntimeErrors(cx
, fc
, options_
)) {
698 return stencil_
.forget();
701 struct ShellCompartmentPrivate
{
702 GCPtr
<ArrayObject
*> blackRoot
;
703 GCPtr
<ArrayObject
*> grayRoot
;
706 struct MOZ_STACK_CLASS EnvironmentPreparer
707 : public js::ScriptEnvironmentPreparer
{
708 explicit EnvironmentPreparer(JSContext
* cx
) {
709 js::SetScriptEnvironmentPreparer(cx
, this);
711 void invoke(JS::HandleObject global
, Closure
& closure
) override
;
714 const char* shell::selfHostedXDRPath
= nullptr;
715 bool shell::encodeSelfHostedCode
= false;
716 bool shell::enableCodeCoverage
= false;
717 bool shell::enableDisassemblyDumps
= false;
718 bool shell::offthreadCompilation
= false;
719 JS::DelazificationOption
shell::defaultDelazificationMode
=
720 JS::DelazificationOption::OnDemandOnly
;
721 bool shell::enableAsmJS
= false;
722 bool shell::enableWasm
= false;
723 bool shell::enableSharedMemory
= SHARED_MEMORY_DEFAULT
;
724 bool shell::enableWasmBaseline
= false;
725 bool shell::enableWasmOptimizing
= false;
727 #define WASM_FEATURE(NAME, _, STAGE, ...) \
728 bool shell::enableWasm##NAME = STAGE != WasmFeatureStage::Experimental;
729 JS_FOR_WASM_FEATURES(WASM_FEATURE
);
732 bool shell::enableWasmVerbose
= false;
733 bool shell::enableTestWasmAwaitTier2
= false;
734 bool shell::enableSourcePragmas
= true;
735 bool shell::enableAsyncStacks
= false;
736 bool shell::enableAsyncStackCaptureDebuggeeOnly
= false;
737 bool shell::enableWeakRefs
= false;
738 bool shell::enableToSource
= false;
739 bool shell::enablePropertyErrorMessageFix
= false;
740 bool shell::enableIteratorHelpers
= false;
741 bool shell::enableShadowRealms
= false;
742 // Pref for String.prototype.{is,to}WellFormed() methods.
743 bool shell::enableWellFormedUnicodeStrings
= true;
744 bool shell::enableArrayGrouping
= false;
746 // Pref for new Set.prototype methods.
747 bool shell::enableNewSetMethods
= false;
748 // Pref for ArrayBuffer.prototype.transfer{,ToFixedLength}() methods.
749 bool shell::enableSymbolsAsWeakMapKeys
= false;
750 // Pref for resizable ArrayBuffers and growable SharedArrayBuffers.
751 bool shell::enableArrayBufferResizable
= false;
753 bool shell::enableArrayBufferTransfer
= true;
754 bool shell::enableImportAttributes
= false;
755 bool shell::enableImportAttributesAssertSyntax
= false;
756 bool shell::enableDestructuringFuse
= true;
758 uint32_t shell::gZealBits
= 0;
759 uint32_t shell::gZealFrequency
= 0;
761 bool shell::printTiming
= false;
762 RCFile
* shell::gErrFile
= nullptr;
763 RCFile
* shell::gOutFile
= nullptr;
764 bool shell::reportWarnings
= true;
765 bool shell::compileOnly
= false;
766 bool shell::disableOOMFunctions
= false;
767 bool shell::defaultToSameCompartment
= true;
769 bool shell::useFdlibmForSinCosTan
= false;
772 bool shell::dumpEntrainedVariables
= false;
773 bool shell::OOM_printAllocationCount
= false;
776 UniqueChars
shell::processWideModuleLoadPath
;
778 static bool SetTimeoutValue(JSContext
* cx
, double t
);
780 static void KillWatchdog(JSContext
* cx
);
782 static bool ScheduleWatchdog(JSContext
* cx
, double t
);
784 static void CancelExecution(JSContext
* cx
);
786 enum class ShellGlobalKind
{
791 static JSObject
* NewGlobalObject(JSContext
* cx
, JS::RealmOptions
& options
,
792 JSPrincipals
* principals
, ShellGlobalKind kind
,
793 bool immutablePrototype
);
796 * A toy WindowProxy class for the shell. This is intended for testing code
797 * where global |this| is a WindowProxy. All requests are forwarded to the
798 * underlying global and no navigation is supported.
800 const JSClass ShellWindowProxyClass
=
801 PROXY_CLASS_DEF("ShellWindowProxy", JSCLASS_HAS_RESERVED_SLOTS(1));
803 JSObject
* NewShellWindowProxy(JSContext
* cx
, JS::HandleObject global
) {
804 MOZ_ASSERT(global
->is
<GlobalObject
>());
806 js::WrapperOptions options
;
807 options
.setClass(&ShellWindowProxyClass
);
809 JSAutoRealm
ar(cx
, global
);
811 js::Wrapper::New(cx
, global
, &js::Wrapper::singleton
, options
);
812 MOZ_ASSERT_IF(obj
, js::IsWindowProxy(obj
));
817 * A toy principals type for the shell.
819 * In the shell, a principal is simply a 32-bit mask: P subsumes Q if the
820 * set bits in P are a superset of those in Q. Thus, the principal 0 is
821 * subsumed by everything, and the principal ~0 subsumes everything.
823 * As a special case, a null pointer as a principal is treated like 0xffff.
825 * The 'newGlobal' function takes an option indicating which principal the
826 * new global should have; 'evaluate' does for the new code.
828 class ShellPrincipals final
: public JSPrincipals
{
831 static uint32_t getBits(JSPrincipals
* p
) {
835 return static_cast<ShellPrincipals
*>(p
)->bits
;
839 explicit ShellPrincipals(uint32_t bits
, int32_t refcount
= 0) : bits(bits
) {
840 this->refcount
= refcount
;
843 bool write(JSContext
* cx
, JSStructuredCloneWriter
* writer
) override
{
844 // The shell doesn't have a read principals hook, so it doesn't really
845 // matter what we write here, but we have to write something so the
847 return JS_WriteUint32Pair(writer
, bits
, 0);
850 bool isSystemOrAddonPrincipal() override
{ return true; }
852 static void destroy(JSPrincipals
* principals
) {
853 MOZ_ASSERT(principals
!= &fullyTrusted
);
854 MOZ_ASSERT(principals
->refcount
== 0);
855 js_delete(static_cast<const ShellPrincipals
*>(principals
));
858 static bool subsumes(JSPrincipals
* first
, JSPrincipals
* second
) {
859 uint32_t firstBits
= getBits(first
);
860 uint32_t secondBits
= getBits(second
);
861 return (firstBits
| secondBits
) == firstBits
;
864 static JSSecurityCallbacks securityCallbacks
;
866 // Fully-trusted principals singleton.
867 static ShellPrincipals fullyTrusted
;
870 JSSecurityCallbacks
ShellPrincipals::securityCallbacks
= {
871 nullptr, // contentSecurityPolicyAllows
874 // The fully-trusted principal subsumes all other principals.
875 ShellPrincipals
ShellPrincipals::fullyTrusted(-1, 1);
879 extern MOZ_EXPORT
char* readline(const char* prompt
);
880 extern MOZ_EXPORT
void add_history(char* line
);
884 ShellContext::ShellContext(JSContext
* cx
, IsWorkerEnum isWorker_
)
887 lastWarningEnabled(false),
888 trackUnhandledRejections(true),
889 timeoutInterval(-1.0),
890 startTime(PRMJ_Now()),
891 serviceInterrupt(false),
892 haveInterruptFunc(false),
893 interruptFunc(cx
, NullValue()),
894 lastWarning(cx
, NullValue()),
895 promiseRejectionTrackerCallback(cx
, NullValue()),
896 unhandledRejectedPromises(cx
),
897 watchdogLock(mutexid::ShellContextWatchdog
),
903 offThreadMonitor(mutexid::ShellOffThreadState
),
904 finalizationRegistryCleanupCallbacks(cx
) {}
906 ShellContext
* js::shell::GetShellContext(JSContext
* cx
) {
907 ShellContext
* sc
= static_cast<ShellContext
*>(JS_GetContextPrivate(cx
));
912 static void TraceRootArrays(JSTracer
* trc
, gc::MarkColor color
) {
913 JSRuntime
* rt
= trc
->runtime();
914 for (ZonesIter
zone(rt
, SkipAtoms
); !zone
.done(); zone
.next()) {
915 for (CompartmentsInZoneIter
comp(zone
); !comp
.done(); comp
.next()) {
916 auto priv
= static_cast<ShellCompartmentPrivate
*>(
917 JS_GetCompartmentPrivate(comp
.get()));
922 GCPtr
<ArrayObject
*>& array
=
923 (color
== gc::MarkColor::Black
) ? priv
->blackRoot
: priv
->grayRoot
;
924 TraceNullableEdge(trc
, &array
, "shell root array");
927 // Trace the array elements as part of root marking.
928 for (uint32_t i
= 0; i
< array
->getDenseInitializedLength(); i
++) {
929 Value
& value
= const_cast<Value
&>(array
->getDenseElement(i
));
930 TraceManuallyBarrieredEdge(trc
, &value
, "shell root array element");
937 static void TraceBlackRoots(JSTracer
* trc
, void* data
) {
938 TraceRootArrays(trc
, gc::MarkColor::Black
);
941 static bool TraceGrayRoots(JSTracer
* trc
, SliceBudget
& budget
, void* data
) {
942 TraceRootArrays(trc
, gc::MarkColor::Gray
);
946 static inline JSString
* NewStringCopyUTF8(JSContext
* cx
, const char* chars
) {
947 return JS_NewStringCopyUTF8N(cx
, JS::UTF8Chars(chars
, strlen(chars
)));
950 static mozilla::UniqueFreePtr
<char[]> GetLine(FILE* file
, const char* prompt
) {
953 * Use readline only if file is stdin, because there's no way to specify
954 * another handle. Are other filehandles interactive?
957 mozilla::UniqueFreePtr
<char[]> linep(readline(prompt
));
959 * We set it to zero to avoid complaining about inappropriate ioctl
960 * for device in the case of EOF. Looks like errno == 251 if line is
961 * finished with EOF and errno == 25 (EINVAL on Mac) if there is
962 * nothing left to read.
964 if (errno
== 251 || errno
== 25 || errno
== EINVAL
) {
970 if (linep
[0] != '\0') {
971 add_history(linep
.get());
978 if (*prompt
!= '\0' && gOutFile
->isOpen()) {
979 fprintf(gOutFile
->fp
, "%s", prompt
);
980 fflush(gOutFile
->fp
);
984 mozilla::UniqueFreePtr
<char[]> buffer(static_cast<char*>(malloc(size
)));
989 char* current
= buffer
.get();
992 if (fgets(current
, size
- len
, file
)) {
995 if (errno
!= EINTR
) {
1000 len
+= strlen(current
);
1001 char* t
= buffer
.get() + len
- 1;
1003 /* Line was read. We remove '\n' and exit. */
1008 if (len
+ 1 == size
) {
1010 char* raw
= buffer
.release();
1011 char* tmp
= static_cast<char*>(realloc(raw
, size
));
1018 current
= buffer
.get() + len
;
1023 static bool ShellInterruptCallback(JSContext
* cx
) {
1024 ShellContext
* sc
= GetShellContext(cx
);
1025 if (!sc
->serviceInterrupt
) {
1029 // Reset serviceInterrupt. CancelExecution or InterruptIf will set it to
1030 // true to distinguish watchdog or user triggered interrupts.
1031 // Do this first to prevent other interrupts that may occur while the
1032 // user-supplied callback is executing from re-entering the handler.
1033 sc
->serviceInterrupt
= false;
1036 if (sc
->haveInterruptFunc
) {
1037 bool wasAlreadyThrowing
= cx
->isExceptionPending();
1038 JS::AutoSaveExceptionState
savedExc(cx
);
1039 JSAutoRealm
ar(cx
, &sc
->interruptFunc
.toObject());
1040 RootedValue
rval(cx
);
1042 // Report any exceptions thrown by the JS interrupt callback, but do
1043 // *not* keep it on the cx. The interrupt handler is invoked at points
1044 // that are not expected to throw catchable exceptions, like at
1047 // If the interrupted JS code was already throwing, any exceptions
1048 // thrown by the interrupt handler are silently swallowed.
1050 Maybe
<AutoReportException
> are
;
1051 if (!wasAlreadyThrowing
) {
1054 result
= JS_CallFunctionValue(cx
, nullptr, sc
->interruptFunc
,
1055 JS::HandleValueArray::empty(), &rval
);
1059 if (rval
.isBoolean()) {
1060 result
= rval
.toBoolean();
1068 if (!result
&& sc
->exitCode
== 0) {
1069 static const char msg
[] = "Script terminated by interrupt handler.\n";
1072 sc
->exitCode
= EXITCODE_TIMEOUT
;
1078 static void GCSliceCallback(JSContext
* cx
, JS::GCProgress progress
,
1079 const JS::GCDescription
& desc
) {
1080 if (progress
== JS::GC_CYCLE_END
) {
1081 #if defined(MOZ_MEMORY)
1082 // We call this here to match the browser's DOMGCSliceCallback.
1083 jemalloc_free_dirty_pages();
1089 * Some UTF-8 files, notably those written using Notepad, have a Unicode
1090 * Byte-Order-Mark (BOM) as their first character. This is useless (byte-order
1091 * is meaningless for UTF-8) but causes a syntax error unless we skip it.
1093 static void SkipUTF8BOM(FILE* file
) {
1094 int ch1
= fgetc(file
);
1095 int ch2
= fgetc(file
);
1096 int ch3
= fgetc(file
);
1099 if (ch1
== 0xEF && ch2
== 0xBB && ch3
== 0xBF) {
1115 void EnvironmentPreparer::invoke(HandleObject global
, Closure
& closure
) {
1116 MOZ_ASSERT(JS_IsGlobalObject(global
));
1118 JSContext
* cx
= TlsContext
.get();
1119 MOZ_ASSERT(!JS_IsExceptionPending(cx
));
1121 AutoRealm
ar(cx
, global
);
1122 AutoReportException
are(cx
);
1128 static bool RegisterScriptPathWithModuleLoader(JSContext
* cx
,
1129 HandleScript script
,
1130 const char* filename
) {
1131 // Set the private value associated with a script to a object containing the
1132 // script's filename so that the module loader can use it to resolve
1133 // relative imports.
1135 RootedString
path(cx
, NewStringCopyUTF8(cx
, filename
));
1140 MOZ_ASSERT(JS::GetScriptPrivate(script
).isUndefined());
1141 RootedObject
infoObject(cx
, js::CreateScriptPrivate(cx
, path
));
1146 JS::SetScriptPrivate(script
, ObjectValue(*infoObject
));
1150 enum class CompileUtf8
{
1155 [[nodiscard
]] static bool RunFile(JSContext
* cx
, const char* filename
,
1156 FILE* file
, CompileUtf8 compileMethod
,
1157 bool compileOnly
, bool fullParse
) {
1160 int64_t t1
= PRMJ_Now();
1161 RootedScript
script(cx
);
1164 CompileOptions
options(cx
);
1165 options
.setIntroductionType("js shell file")
1166 .setFileAndLine(filename
, 1)
1168 .setNoScriptRval(true);
1171 options
.setForceFullParse();
1173 options
.setEagerDelazificationStrategy(defaultDelazificationMode
);
1176 if (compileMethod
== CompileUtf8::DontInflate
) {
1177 script
= JS::CompileUtf8File(cx
, options
, file
);
1179 fprintf(stderr
, "(compiling '%s' after inflating to UTF-16)\n", filename
);
1181 FileContents
buffer(cx
);
1182 if (!ReadCompleteFile(cx
, file
, buffer
)) {
1186 size_t length
= buffer
.length();
1187 auto chars
= UniqueTwoByteChars(
1188 UTF8CharsToNewTwoByteCharsZ(
1190 JS::UTF8Chars(reinterpret_cast<const char*>(buffer
.begin()),
1192 &length
, js::MallocArena
)
1198 JS::SourceText
<char16_t
> source
;
1199 if (!source
.init(cx
, std::move(chars
), length
)) {
1203 script
= JS::Compile(cx
, options
, source
);
1211 if (!RegisterScriptPathWithModuleLoader(cx
, script
, filename
)) {
1216 if (dumpEntrainedVariables
) {
1217 AnalyzeEntrainedVariables(cx
, script
);
1221 if (!JS_ExecuteScript(cx
, script
)) {
1224 int64_t t2
= PRMJ_Now() - t1
;
1226 printf("runtime = %.3f ms\n", double(t2
) / PRMJ_USEC_PER_MSEC
);
1232 [[nodiscard
]] static bool RunModule(JSContext
* cx
, const char* filename
,
1234 ShellContext
* sc
= GetShellContext(cx
);
1236 RootedString
path(cx
, NewStringCopyUTF8(cx
, filename
));
1241 path
= ResolvePath(cx
, path
, RootRelative
);
1246 return sc
->moduleLoader
->loadRootModule(cx
, path
);
1249 static void ShellCleanupFinalizationRegistryCallback(JSFunction
* doCleanup
,
1250 JSObject
* incumbentGlobal
,
1252 // In the browser this queues a task. Shell jobs correspond to microtasks so
1253 // we arrange for cleanup to happen after all jobs/microtasks have run. The
1254 // incumbent global is ignored in the shell.
1256 auto sc
= static_cast<ShellContext
*>(data
);
1257 AutoEnterOOMUnsafeRegion oomUnsafe
;
1258 if (!sc
->finalizationRegistryCleanupCallbacks
.append(doCleanup
)) {
1259 oomUnsafe
.crash("ShellCleanupFinalizationRegistryCallback");
1263 // Run any FinalizationRegistry cleanup tasks and return whether any ran.
1264 static bool MaybeRunFinalizationRegistryCleanupTasks(JSContext
* cx
) {
1265 ShellContext
* sc
= GetShellContext(cx
);
1266 MOZ_ASSERT(!sc
->quitting
);
1268 Rooted
<ShellContext::FunctionVector
> callbacks(cx
);
1269 std::swap(callbacks
.get(), sc
->finalizationRegistryCleanupCallbacks
.get());
1271 bool ranTasks
= false;
1273 RootedFunction
callback(cx
);
1274 for (JSFunction
* f
: callbacks
) {
1277 JS::ExposeObjectToActiveJS(callback
);
1278 AutoRealm
ar(cx
, callback
);
1281 AutoReportException
are(cx
);
1282 RootedValue
unused(cx
);
1283 (void)JS_CallFunction(cx
, nullptr, callback
, HandleValueArray::empty(),
1297 static bool EnqueueJob(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1298 CallArgs args
= CallArgsFromVp(argc
, vp
);
1300 if (!IsFunctionObject(args
.get(0))) {
1301 JS_ReportErrorASCII(cx
, "EnqueueJob's first argument must be a function");
1305 args
.rval().setUndefined();
1307 RootedObject
job(cx
, &args
[0].toObject());
1308 return js::EnqueueJob(cx
, job
);
1311 static void RunShellJobs(JSContext
* cx
) {
1312 ShellContext
* sc
= GetShellContext(cx
);
1324 // Run tasks (only finalization registry clean tasks are possible).
1325 bool ranTasks
= MaybeRunFinalizationRegistryCleanupTasks(cx
);
1332 static bool DrainJobQueue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1333 CallArgs args
= CallArgsFromVp(argc
, vp
);
1335 if (GetShellContext(cx
)->quitting
) {
1336 JS_ReportErrorASCII(
1337 cx
, "Mustn't drain the job queue when the shell is quitting");
1343 if (GetShellContext(cx
)->quitting
) {
1347 args
.rval().setUndefined();
1351 static bool GlobalOfFirstJobInQueue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1352 CallArgs args
= CallArgsFromVp(argc
, vp
);
1354 RootedObject
job(cx
, cx
->internalJobQueue
->maybeFront());
1356 JS_ReportErrorASCII(cx
, "Job queue is empty");
1360 RootedObject
global(cx
, &job
->nonCCWGlobal());
1361 if (!cx
->compartment()->wrap(cx
, &global
)) {
1365 args
.rval().setObject(*global
);
1369 static bool TrackUnhandledRejections(JSContext
* cx
, JS::HandleObject promise
,
1370 JS::PromiseRejectionHandlingState state
) {
1371 ShellContext
* sc
= GetShellContext(cx
);
1372 if (!sc
->trackUnhandledRejections
) {
1376 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
1377 if (cx
->runningOOMTest
) {
1378 // When OOM happens, we cannot reliably track the set of unhandled
1379 // promise rejections. Throw error only when simulated OOM is used
1380 // *and* promises are used in the test.
1381 JS_ReportErrorASCII(
1383 "Can't track unhandled rejections while running simulated OOM "
1384 "test. Call ignoreUnhandledRejections before using oomTest etc.");
1389 if (!sc
->unhandledRejectedPromises
) {
1390 sc
->unhandledRejectedPromises
= SetObject::create(cx
);
1391 if (!sc
->unhandledRejectedPromises
) {
1396 RootedValue
promiseVal(cx
, ObjectValue(*promise
));
1398 AutoRealm
ar(cx
, sc
->unhandledRejectedPromises
);
1399 if (!cx
->compartment()->wrap(cx
, &promiseVal
)) {
1404 case JS::PromiseRejectionHandlingState::Unhandled
:
1405 if (!SetObject::add(cx
, sc
->unhandledRejectedPromises
, promiseVal
)) {
1409 case JS::PromiseRejectionHandlingState::Handled
:
1410 bool deleted
= false;
1411 if (!SetObject::delete_(cx
, sc
->unhandledRejectedPromises
, promiseVal
,
1415 // We can't MOZ_ASSERT(deleted) here, because it's possible we failed to
1416 // add the promise in the first place, due to OOM.
1423 static void ForwardingPromiseRejectionTrackerCallback(
1424 JSContext
* cx
, bool mutedErrors
, JS::HandleObject promise
,
1425 JS::PromiseRejectionHandlingState state
, void* data
) {
1426 AutoReportException
are(cx
);
1428 if (!TrackUnhandledRejections(cx
, promise
, state
)) {
1432 RootedValue
callback(cx
,
1433 GetShellContext(cx
)->promiseRejectionTrackerCallback
);
1434 if (callback
.isNull()) {
1438 AutoRealm
ar(cx
, &callback
.toObject());
1440 FixedInvokeArgs
<2> args(cx
);
1441 args
[0].setObject(*promise
);
1442 args
[1].setInt32(static_cast<int32_t>(state
));
1444 if (!JS_WrapValue(cx
, args
[0])) {
1448 RootedValue
rval(cx
);
1449 (void)Call(cx
, callback
, UndefinedHandleValue
, args
, &rval
);
1452 static bool SetPromiseRejectionTrackerCallback(JSContext
* cx
, unsigned argc
,
1454 CallArgs args
= CallArgsFromVp(argc
, vp
);
1456 if (!IsFunctionObject(args
.get(0))) {
1457 JS_ReportErrorASCII(
1459 "setPromiseRejectionTrackerCallback expects a function as its sole "
1464 GetShellContext(cx
)->promiseRejectionTrackerCallback
= args
[0];
1466 args
.rval().setUndefined();
1471 static const char* telemetryNames
[static_cast<int>(JSMetric::Count
)] = {
1472 #define LIT(NAME, _) #NAME,
1473 FOR_EACH_JS_METRIC(LIT
)
1478 // Telemetry can be executed from multiple threads, and the callback is
1479 // responsible to avoid contention on the recorded telemetry data.
1480 static Mutex
* telemetryLock
= nullptr;
1481 class MOZ_RAII AutoLockTelemetry
: public LockGuard
<Mutex
> {
1482 using Base
= LockGuard
<Mutex
>;
1485 AutoLockTelemetry() : Base(*telemetryLock
) { MOZ_ASSERT(telemetryLock
); }
1488 using TelemetryData
= uint32_t;
1489 using TelemetryVec
= Vector
<TelemetryData
, 0, SystemAllocPolicy
>;
1490 static mozilla::Array
<TelemetryVec
, size_t(JSMetric::Count
)> telemetryResults
;
1491 static void AccumulateTelemetryDataCallback(JSMetric id
, uint32_t sample
) {
1492 AutoLockTelemetry alt
;
1493 // We ignore OOMs while writting teleemtry data.
1494 if (telemetryResults
[static_cast<int>(id
)].append(sample
)) {
1499 static void WriteTelemetryDataToDisk(const char* dir
) {
1500 const int pathLen
= 260;
1501 char fileName
[pathLen
];
1503 auto initOutput
= [&](const char* name
) -> bool {
1504 if (SprintfLiteral(fileName
, "%s%s.csv", dir
, name
) >= pathLen
) {
1507 FILE* file
= fopen(fileName
, "a");
1515 for (size_t id
= 0; id
< size_t(JSMetric::Count
); id
++) {
1516 auto clear
= MakeScopeExit([&] { telemetryResults
[id
].clearAndFree(); });
1517 if (!initOutput(telemetryNames
[id
])) {
1520 for (uint32_t data
: telemetryResults
[id
]) {
1521 output
.printf("%u\n", data
);
1527 #undef MAP_TELEMETRY
1529 static bool BoundToAsyncStack(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1530 CallArgs args
= CallArgsFromVp(argc
, vp
);
1532 RootedValue
function(cx
, GetFunctionNativeReserved(&args
.callee(), 0));
1533 RootedObject
options(
1534 cx
, &GetFunctionNativeReserved(&args
.callee(), 1).toObject());
1536 Rooted
<SavedFrame
*> stack(cx
, nullptr);
1541 if (!JS_GetProperty(cx
, options
, "stack", &v
)) {
1544 if (!v
.isObject() || !v
.toObject().is
<SavedFrame
>()) {
1545 JS_ReportErrorASCII(cx
,
1546 "The 'stack' property must be a SavedFrame object.");
1549 stack
= &v
.toObject().as
<SavedFrame
>();
1551 if (!JS_GetProperty(cx
, options
, "cause", &v
)) {
1554 RootedString
causeString(cx
, ToString(cx
, v
));
1556 MOZ_ASSERT(cx
->isExceptionPending());
1560 UniqueChars cause
= JS_EncodeStringToUTF8(cx
, causeString
);
1562 MOZ_ASSERT(cx
->isExceptionPending());
1566 if (!JS_GetProperty(cx
, options
, "explicit", &v
)) {
1569 isExplicit
= v
.isUndefined() ? true : ToBoolean(v
);
1572 (isExplicit
? JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT
1573 : JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::IMPLICIT
);
1575 JS::AutoSetAsyncStackForNewCalls
asasfnckthxbye(cx
, stack
, cause
.get(), kind
);
1576 return Call(cx
, UndefinedHandleValue
, function
, JS::HandleValueArray::empty(),
1580 static bool BindToAsyncStack(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1581 CallArgs args
= CallArgsFromVp(argc
, vp
);
1583 if (args
.length() != 2) {
1584 JS_ReportErrorASCII(cx
, "bindToAsyncStack takes exactly two arguments.");
1588 if (!args
[0].isObject() || !IsCallable(args
[0])) {
1589 JS_ReportErrorASCII(
1590 cx
, "bindToAsyncStack's first argument should be a function.");
1594 if (!args
[1].isObject()) {
1595 JS_ReportErrorASCII(
1596 cx
, "bindToAsyncStack's second argument should be an object.");
1600 RootedFunction
bound(cx
, NewFunctionWithReserved(cx
, BoundToAsyncStack
, 0, 0,
1601 "bindToAsyncStack thunk"));
1605 SetFunctionNativeReserved(bound
, 0, args
[0]);
1606 SetFunctionNativeReserved(bound
, 1, args
[1]);
1608 args
.rval().setObject(*bound
);
1612 #ifdef JS_HAS_INTL_API
1613 static bool AddIntlExtras(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1614 CallArgs args
= CallArgsFromVp(argc
, vp
);
1615 if (!args
.get(0).isObject()) {
1616 JS_ReportErrorASCII(cx
, "addIntlExtras must be passed an object");
1619 JS::RootedObject
intl(cx
, &args
[0].toObject());
1621 static const JSFunctionSpec funcs
[] = {
1622 JS_SELF_HOSTED_FN("getCalendarInfo", "Intl_getCalendarInfo", 1, 0),
1625 if (!JS_DefineFunctions(cx
, intl
, funcs
)) {
1629 if (!JS::AddMozDateTimeFormatConstructor(cx
, intl
)) {
1633 if (!JS::AddMozDisplayNamesConstructor(cx
, intl
)) {
1637 args
.rval().setUndefined();
1640 #endif // JS_HAS_INTL_API
1642 [[nodiscard
]] static bool EvalUtf8AndPrint(JSContext
* cx
, const char* bytes
,
1643 size_t length
, int lineno
,
1646 JS::CompileOptions
options(cx
);
1647 options
.setIntroductionType("js shell interactive")
1649 .setFileAndLine("typein", lineno
)
1650 .setEagerDelazificationStrategy(defaultDelazificationMode
);
1652 JS::SourceText
<Utf8Unit
> srcBuf
;
1653 if (!srcBuf
.init(cx
, bytes
, length
, JS::SourceOwnership::Borrowed
)) {
1657 RootedScript
script(cx
, JS::Compile(cx
, options
, srcBuf
));
1664 RootedValue
result(cx
);
1665 if (!JS_ExecuteScript(cx
, script
, &result
)) {
1669 if (!result
.isUndefined() && gOutFile
->isOpen()) {
1671 RootedString
str(cx
, JS_ValueToSource(cx
, result
));
1676 UniqueChars utf8chars
= JS_EncodeStringToUTF8(cx
, str
);
1680 fprintf(gOutFile
->fp
, "%s\n", utf8chars
.get());
1685 [[nodiscard
]] static bool ReadEvalPrintLoop(JSContext
* cx
, FILE* in
,
1687 ShellContext
* sc
= GetShellContext(cx
);
1689 bool hitEOF
= false;
1693 * Accumulate lines until we get a 'compilable unit' - one that either
1694 * generates an error (before running out of source) or that compiles
1695 * cleanly. This should be whenever we get a complete statement that
1696 * coincides with the end of a line.
1698 int startline
= lineno
;
1699 typedef Vector
<char, 32> CharBuffer
;
1700 RootedObject
globalLexical(cx
, &cx
->global()->lexicalEnvironment());
1701 CharBuffer
buffer(cx
);
1703 ScheduleWatchdog(cx
, -1);
1704 sc
->serviceInterrupt
= false;
1707 mozilla::UniqueFreePtr
<char[]> line
=
1708 GetLine(in
, startline
== lineno
? "js> " : "");
1711 if (UniqueChars error
= SystemErrorMessage(cx
, errno
)) {
1712 JS_ReportErrorUTF8(cx
, "%s", error
.get());
1720 if (!buffer
.append(line
.get(), strlen(line
.get())) ||
1721 !buffer
.append('\n')) {
1726 if (!ScheduleWatchdog(cx
, sc
->timeoutInterval
)) {
1730 } while (!JS_Utf8BufferIsCompilableUnit(cx
, cx
->global(), buffer
.begin(),
1733 if (hitEOF
&& buffer
.empty()) {
1738 // Report exceptions but keep going.
1739 AutoReportException
are(cx
);
1740 (void)EvalUtf8AndPrint(cx
, buffer
.begin(), buffer
.length(), startline
,
1744 // If a let or const fail to initialize they will remain in an unusable
1745 // without further intervention. This call cleans up the global scope,
1746 // setting uninitialized lexicals to undefined so that they may still
1747 // be used. This behavior is _only_ acceptable in the context of the repl.
1748 if (JS::ForceLexicalInitialization(cx
, globalLexical
) &&
1749 gErrFile
->isOpen()) {
1751 "Warning: According to the standard, after the above exception,\n"
1752 "Warning: the global bindings should be permanently uninitialized.\n"
1753 "Warning: We have non-standard-ly initialized them to `undefined`"
1754 "for you.\nWarning: This nicety only happens in the JS shell.\n",
1759 } while (!hitEOF
&& !sc
->quitting
);
1761 if (gOutFile
->isOpen()) {
1762 fprintf(gOutFile
->fp
, "\n");
1769 PreludeScript
, // UTF-8 script, fully-parsed, to avoid conflicting
1771 FileScript
, // UTF-8, directly parsed as such
1772 FileScriptUtf16
, // FileScript, but inflate to UTF-16 before parsing
1776 [[nodiscard
]] static bool Process(JSContext
* cx
, const char* filename
,
1777 bool forceTTY
, FileKind kind
) {
1779 if (forceTTY
|| !filename
|| strcmp(filename
, "-") == 0) {
1782 file
= OpenFile(cx
, filename
, "rb");
1787 AutoCloseFile
autoClose(file
);
1789 bool fullParse
= false;
1790 if (!forceTTY
&& !isatty(fileno(file
))) {
1791 // It's not interactive - just execute it.
1795 if (!RunFile(cx
, filename
, file
, CompileUtf8::DontInflate
, compileOnly
,
1801 if (!RunFile(cx
, filename
, file
, CompileUtf8::DontInflate
, compileOnly
,
1806 case FileScriptUtf16
:
1807 if (!RunFile(cx
, filename
, file
, CompileUtf8::InflateToUtf16
,
1808 compileOnly
, fullParse
)) {
1813 if (!RunModule(cx
, filename
, compileOnly
)) {
1818 MOZ_CRASH("Impossible FileKind!");
1821 // It's an interactive filehandle; drop into read-eval-print loop.
1822 MOZ_ASSERT(kind
== FileScript
);
1823 if (!ReadEvalPrintLoop(cx
, file
, compileOnly
)) {
1827 #ifdef FUZZING_JS_FUZZILLI
1828 fprintf(stderr
, "executionHash is 0x%x with %d inputs\n", cx
->executionHash
,
1829 cx
->executionHashInputs
);
1835 # define GET_FD_FROM_FILE(a) int(_get_osfhandle(fileno(a)))
1837 # define GET_FD_FROM_FILE(a) fileno(a)
1840 static void freeExternalCallback(void* contents
, void* userData
) {
1841 MOZ_ASSERT(!userData
);
1845 static bool CreateExternalArrayBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1846 CallArgs args
= CallArgsFromVp(argc
, vp
);
1847 if (args
.length() != 1) {
1848 JS_ReportErrorNumberASCII(
1849 cx
, my_GetErrorMessage
, nullptr,
1850 args
.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS
: JSSMSG_TOO_MANY_ARGS
,
1851 "createExternalArrayBuffer");
1856 if (!ToInt32(cx
, args
[0], &bytes
)) {
1861 JS_ReportErrorASCII(cx
, "Size must be non-negative");
1865 void* buffer
= js_calloc(bytes
);
1867 JS_ReportOutOfMemory(cx
);
1871 UniquePtr
<void, JS::BufferContentsDeleter
> ptr
{buffer
,
1872 {&freeExternalCallback
}};
1873 RootedObject
arrayBuffer(
1874 cx
, JS::NewExternalArrayBuffer(cx
, bytes
, std::move(ptr
)));
1879 args
.rval().setObject(*arrayBuffer
);
1883 static bool CreateMappedArrayBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
1884 CallArgs args
= CallArgsFromVp(argc
, vp
);
1886 if (args
.length() < 1 || args
.length() > 3) {
1887 JS_ReportErrorNumberASCII(
1888 cx
, my_GetErrorMessage
, nullptr,
1889 args
.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS
: JSSMSG_TOO_MANY_ARGS
,
1890 "createMappedArrayBuffer");
1894 RootedString
rawFilenameStr(cx
, JS::ToString(cx
, args
[0]));
1895 if (!rawFilenameStr
) {
1898 // It's a little bizarre to resolve relative to the script, but for testing
1899 // I need a file at a known location, and the only good way I know of to do
1900 // that right now is to include it in the repo alongside the test script.
1901 // Bug 944164 would introduce an alternative.
1902 Rooted
<JSString
*> filenameStr(
1903 cx
, ResolvePath(cx
, rawFilenameStr
, ScriptRelative
));
1907 UniqueChars filename
= JS_EncodeStringToUTF8(cx
, filenameStr
);
1912 uint32_t offset
= 0;
1913 if (args
.length() >= 2) {
1914 if (!JS::ToUint32(cx
, args
[1], &offset
)) {
1919 bool sizeGiven
= false;
1921 if (args
.length() >= 3) {
1922 if (!JS::ToUint32(cx
, args
[2], &size
)) {
1927 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1928 JSMSG_BAD_ARRAY_LENGTH
);
1933 FILE* file
= OpenFile(cx
, filename
.get(), "rb");
1937 AutoCloseFile
autoClose(file
);
1940 if (fstat(fileno(file
), &st
) < 0) {
1941 JS_ReportErrorASCII(cx
, "Unable to stat file");
1945 if ((st
.st_mode
& S_IFMT
) != S_IFREG
) {
1946 JS_ReportErrorASCII(cx
, "Path is not a regular file");
1951 if (off_t(offset
) >= st
.st_size
) {
1952 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
1953 JSMSG_OFFSET_LARGER_THAN_FILESIZE
);
1956 size
= st
.st_size
- offset
;
1960 JS::CreateMappedArrayBufferContents(GET_FD_FROM_FILE(file
), offset
, size
);
1962 JS_ReportErrorASCII(cx
,
1963 "failed to allocate mapped array buffer contents "
1964 "(possibly due to bad alignment)");
1968 RootedObject
obj(cx
,
1969 JS::NewMappedArrayBufferWithContents(cx
, size
, contents
));
1974 args
.rval().setObject(*obj
);
1978 #undef GET_FD_FROM_FILE
1980 class UserBufferObject
: public NativeObject
{
1981 static const uint32_t BUFFER_SLOT
= 0;
1982 static const uint32_t BYTE_LENGTH_SLOT
= 1;
1983 static const uint32_t RESERVED_SLOTS
= 2;
1985 static constexpr auto BufferMemoryUse
= MemoryUse::Embedding1
;
1987 static void finalize(JS::GCContext
* gcx
, JSObject
* obj
);
1990 static const JSClassOps classOps_
;
1991 static const JSClass class_
;
1993 [[nodiscard
]] static UserBufferObject
* create(JSContext
* cx
,
1996 void* buffer() const {
1997 auto& buffer
= getReservedSlot(BUFFER_SLOT
);
1998 if (buffer
.isUndefined()) {
2001 return buffer
.toPrivate();
2004 size_t byteLength() const {
2005 return size_t(getReservedSlot(BYTE_LENGTH_SLOT
).toPrivate());
2009 const JSClassOps
UserBufferObject::classOps_
= {
2010 nullptr, // addProperty
2011 nullptr, // delProperty
2012 nullptr, // enumerate
2013 nullptr, // newEnumerate
2015 nullptr, // mayResolve
2016 UserBufferObject::finalize
, // finalize
2018 nullptr, // construct
2022 const JSClass
UserBufferObject::class_
= {
2024 JSCLASS_HAS_RESERVED_SLOTS(UserBufferObject::RESERVED_SLOTS
) |
2025 JSCLASS_BACKGROUND_FINALIZE
,
2026 &UserBufferObject::classOps_
,
2029 UserBufferObject
* UserBufferObject::create(JSContext
* cx
, size_t byteLength
) {
2030 void* buffer
= js_calloc(byteLength
);
2032 JS_ReportOutOfMemory(cx
);
2035 UniquePtr
<void, JS::FreePolicy
> ptr(buffer
);
2037 auto* userBuffer
= NewObjectWithGivenProto
<UserBufferObject
>(cx
, nullptr);
2042 InitReservedSlot(userBuffer
, BUFFER_SLOT
, ptr
.release(), byteLength
,
2044 userBuffer
->initReservedSlot(BYTE_LENGTH_SLOT
, PrivateValue(byteLength
));
2049 void UserBufferObject::finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
2050 auto* userBuffer
= &obj
->as
<UserBufferObject
>();
2051 if (auto* buffer
= userBuffer
->buffer()) {
2052 gcx
->free_(userBuffer
, buffer
, userBuffer
->byteLength(), BufferMemoryUse
);
2056 static bool CreateUserArrayBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2057 CallArgs args
= CallArgsFromVp(argc
, vp
);
2058 if (args
.length() != 1) {
2059 JS_ReportErrorNumberASCII(
2060 cx
, my_GetErrorMessage
, nullptr,
2061 args
.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS
: JSSMSG_TOO_MANY_ARGS
,
2062 "createUserArrayBuffer");
2067 if (!ToInt32(cx
, args
[0], &bytes
)) {
2071 JS_ReportErrorASCII(cx
, "Size must be non-negative");
2075 Rooted
<UserBufferObject
*> userBuffer(cx
, UserBufferObject::create(cx
, bytes
));
2080 Rooted
<JSObject
*> arrayBuffer(
2081 cx
, JS::NewArrayBufferWithUserOwnedContents(cx
, userBuffer
->byteLength(),
2082 userBuffer
->buffer()));
2087 // Create a strong reference from |arrayBuffer| to |userBuffer|. This ensures
2088 // |userBuffer| can't outlive |arrayBuffer|. That way we don't have to worry
2089 // about detaching the ArrayBuffer object when |userBuffer| gets finalized.
2090 // The reference is made through a private name, because we don't want to
2091 // expose |userBuffer| to user-code.
2093 auto* privateName
= NewPrivateName(cx
, cx
->names().empty_
.toHandle());
2098 Rooted
<PropertyKey
> id(cx
, PropertyKey::Symbol(privateName
));
2099 Rooted
<JS::Value
> userBufferVal(cx
, ObjectValue(*userBuffer
));
2100 if (!js::DefineDataProperty(cx
, arrayBuffer
, id
, userBufferVal
, 0)) {
2104 args
.rval().setObject(*arrayBuffer
);
2108 static bool AddPromiseReactions(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2109 CallArgs args
= CallArgsFromVp(argc
, vp
);
2111 if (args
.length() != 3) {
2112 JS_ReportErrorNumberASCII(
2113 cx
, my_GetErrorMessage
, nullptr,
2114 args
.length() < 3 ? JSSMSG_NOT_ENOUGH_ARGS
: JSSMSG_TOO_MANY_ARGS
,
2115 "addPromiseReactions");
2119 RootedObject
promise(cx
);
2120 if (args
[0].isObject()) {
2121 promise
= &args
[0].toObject();
2124 if (!promise
|| !JS::IsPromiseObject(promise
)) {
2125 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2126 JSSMSG_INVALID_ARGS
, "addPromiseReactions");
2130 RootedObject
onResolve(cx
);
2131 if (args
[1].isObject()) {
2132 onResolve
= &args
[1].toObject();
2135 RootedObject
onReject(cx
);
2136 if (args
[2].isObject()) {
2137 onReject
= &args
[2].toObject();
2140 if (!onResolve
|| !onResolve
->is
<JSFunction
>() || !onReject
||
2141 !onReject
->is
<JSFunction
>()) {
2142 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2143 JSSMSG_INVALID_ARGS
, "addPromiseReactions");
2147 return JS::AddPromiseReactions(cx
, promise
, onResolve
, onReject
);
2150 static bool IgnoreUnhandledRejections(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2151 CallArgs args
= CallArgsFromVp(argc
, vp
);
2153 ShellContext
* sc
= GetShellContext(cx
);
2154 sc
->trackUnhandledRejections
= false;
2156 args
.rval().setUndefined();
2160 static bool Options(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2161 CallArgs args
= CallArgsFromVp(argc
, vp
);
2163 JS::ContextOptions oldContextOptions
= JS::ContextOptionsRef(cx
);
2164 for (unsigned i
= 0; i
< args
.length(); i
++) {
2165 RootedString
str(cx
, JS::ToString(cx
, args
[i
]));
2170 Rooted
<JSLinearString
*> opt(cx
, str
->ensureLinear(cx
));
2175 if (StringEqualsLiteral(opt
, "throw_on_asmjs_validation_failure")) {
2176 JS::ContextOptionsRef(cx
).toggleThrowOnAsmJSValidationFailure();
2178 UniqueChars optChars
= QuoteString(cx
, opt
, '"');
2183 JS_ReportErrorASCII(cx
,
2184 "unknown option name %s."
2185 " The valid name is "
2186 "throw_on_asmjs_validation_failure.",
2192 UniqueChars names
= DuplicateString("");
2194 if (names
&& oldContextOptions
.throwOnAsmJSValidationFailure()) {
2195 names
= JS_sprintf_append(std::move(names
), "%s%s", found
? "," : "",
2196 "throw_on_asmjs_validation_failure");
2200 JS_ReportOutOfMemory(cx
);
2204 JSString
* str
= JS_NewStringCopyZ(cx
, names
.get());
2208 args
.rval().setString(str
);
2212 static bool LoadScript(JSContext
* cx
, unsigned argc
, Value
* vp
,
2213 bool scriptRelative
) {
2214 CallArgs args
= CallArgsFromVp(argc
, vp
);
2216 RootedString
str(cx
);
2217 for (unsigned i
= 0; i
< args
.length(); i
++) {
2218 str
= JS::ToString(cx
, args
[i
]);
2220 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2221 JSSMSG_INVALID_ARGS
, "load");
2225 str
= ResolvePath(cx
, str
, scriptRelative
? ScriptRelative
: RootRelative
);
2227 JS_ReportErrorASCII(cx
, "unable to resolve path");
2231 UniqueChars filename
= JS_EncodeStringToUTF8(cx
, str
);
2238 CompileOptions
opts(cx
);
2239 opts
.setIntroductionType("js shell load")
2241 .setNoScriptRval(true)
2242 .setEagerDelazificationStrategy(defaultDelazificationMode
);
2244 RootedValue
unused(cx
);
2246 ? JS::CompileUtf8Path(cx
, opts
, filename
.get()) != nullptr
2247 : JS::EvaluateUtf8Path(cx
, opts
, filename
.get(), &unused
))) {
2252 args
.rval().setUndefined();
2256 static bool Load(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2257 return LoadScript(cx
, argc
, vp
, false);
2260 static bool LoadScriptRelativeToScript(JSContext
* cx
, unsigned argc
,
2262 return LoadScript(cx
, argc
, vp
, true);
2265 static void my_LargeAllocFailCallback() {
2266 JSContext
* cx
= TlsContext
.get();
2271 MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
2273 JS::PrepareForFullGC(cx
);
2274 cx
->runtime()->gc
.gc(JS::GCOptions::Shrink
,
2275 JS::GCReason::SHARED_MEMORY_LIMIT
);
2278 static const uint32_t CacheEntry_SOURCE
= 0;
2279 static const uint32_t CacheEntry_BYTECODE
= 1;
2280 static const uint32_t CacheEntry_OPTIONS
= 2;
2282 // Some compile options can't be combined differently between save and load.
2284 // CacheEntries store a CacheOption set, and on load an exception is thrown
2285 // if the entries are incompatible.
2287 enum CacheOptions
: uint32_t {
2296 struct CacheOptionSet
: public mozilla::EnumSet
<CacheOptions
> {
2297 using mozilla::EnumSet
<CacheOptions
>::EnumSet
;
2299 explicit CacheOptionSet(const CompileOptions
& options
) : EnumSet() {
2300 initFromOptions(options
);
2303 void initFromOptions(const CompileOptions
& options
) {
2304 if (options
.noScriptRval
) {
2305 *this += CacheOptions::NoScriptRval
;
2307 if (options
.isRunOnce
) {
2308 *this += CacheOptions::IsRunOnce
;
2310 if (options
.sourceIsLazy
) {
2311 *this += CacheOptions::SourceIsLazy
;
2313 if (options
.forceFullParse()) {
2314 *this += CacheOptions::ForceFullParse
;
2316 if (options
.nonSyntacticScope
) {
2317 *this += CacheOptions::NonSyntactic
;
2322 static bool CacheOptionsCompatible(const CacheOptionSet
& a
,
2323 const CacheOptionSet
& b
) {
2324 // If the options are identical, they are trivially compatible.
2328 static const JSClass CacheEntry_class
= {"CacheEntryObject",
2329 JSCLASS_HAS_RESERVED_SLOTS(3)};
2331 static bool CacheEntry(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
2332 CallArgs args
= CallArgsFromVp(argc
, vp
);
2334 if (args
.length() != 1 || !args
[0].isString()) {
2335 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2336 JSSMSG_INVALID_ARGS
, "CacheEntry");
2340 RootedObject
obj(cx
, JS_NewObject(cx
, &CacheEntry_class
));
2345 JS::SetReservedSlot(obj
, CacheEntry_SOURCE
, args
[0]);
2346 JS::SetReservedSlot(obj
, CacheEntry_BYTECODE
, UndefinedValue());
2348 // Fill in empty option set.
2349 CacheOptionSet defaultOptions
;
2350 JS::SetReservedSlot(obj
, CacheEntry_OPTIONS
,
2351 Int32Value(defaultOptions
.serialize()));
2353 args
.rval().setObject(*obj
);
2357 static bool CacheEntry_isCacheEntry(JSObject
* cache
) {
2358 return cache
->hasClass(&CacheEntry_class
);
2361 static JSString
* CacheEntry_getSource(JSContext
* cx
, HandleObject cache
) {
2362 MOZ_ASSERT(CacheEntry_isCacheEntry(cache
));
2363 Value v
= JS::GetReservedSlot(cache
, CacheEntry_SOURCE
);
2364 if (!v
.isString()) {
2365 JS_ReportErrorASCII(
2366 cx
, "CacheEntry_getSource: Unexpected type of source reserved slot.");
2370 return v
.toString();
2373 static bool CacheEntry_compatible(JSContext
* cx
, HandleObject cache
,
2374 const CacheOptionSet
& currentOptionSet
) {
2375 CacheOptionSet cacheEntryOptions
;
2376 MOZ_ASSERT(CacheEntry_isCacheEntry(cache
));
2377 Value v
= JS::GetReservedSlot(cache
, CacheEntry_OPTIONS
);
2378 cacheEntryOptions
.deserialize(v
.toInt32());
2379 if (!CacheOptionsCompatible(cacheEntryOptions
, currentOptionSet
)) {
2380 JS_ReportErrorASCII(cx
,
2381 "CacheEntry_compatible: Incompatible cache contents");
2387 static uint8_t* CacheEntry_getBytecode(JSContext
* cx
, HandleObject cache
,
2389 MOZ_ASSERT(CacheEntry_isCacheEntry(cache
));
2390 Value v
= JS::GetReservedSlot(cache
, CacheEntry_BYTECODE
);
2391 if (!v
.isObject() || !v
.toObject().is
<ArrayBufferObject
>()) {
2392 JS_ReportErrorASCII(
2394 "CacheEntry_getBytecode: Unexpected type of bytecode reserved slot.");
2398 ArrayBufferObject
* arrayBuffer
= &v
.toObject().as
<ArrayBufferObject
>();
2399 *length
= arrayBuffer
->byteLength();
2400 return arrayBuffer
->dataPointer();
2403 static bool CacheEntry_setBytecode(JSContext
* cx
, HandleObject cache
,
2404 const CacheOptionSet
& cacheOptions
,
2405 uint8_t* buffer
, uint32_t length
) {
2406 MOZ_ASSERT(CacheEntry_isCacheEntry(cache
));
2408 using BufferContents
= ArrayBufferObject::BufferContents
;
2410 BufferContents contents
= BufferContents::createMallocedUnknownArena(buffer
);
2411 Rooted
<ArrayBufferObject
*> arrayBuffer(
2412 cx
, ArrayBufferObject::createForContents(cx
, length
, contents
));
2417 JS::SetReservedSlot(cache
, CacheEntry_BYTECODE
, ObjectValue(*arrayBuffer
));
2418 JS::SetReservedSlot(cache
, CacheEntry_OPTIONS
,
2419 Int32Value(cacheOptions
.serialize()));
2423 static bool ConvertTranscodeResultToJSException(JSContext
* cx
,
2424 JS::TranscodeResult rv
) {
2426 case JS::TranscodeResult::Ok
:
2431 case JS::TranscodeResult::Failure
:
2432 MOZ_ASSERT(!cx
->isExceptionPending());
2433 JS_ReportErrorASCII(cx
, "generic warning");
2435 case JS::TranscodeResult::Failure_BadBuildId
:
2436 MOZ_ASSERT(!cx
->isExceptionPending());
2437 JS_ReportErrorASCII(cx
, "the build-id does not match");
2439 case JS::TranscodeResult::Failure_AsmJSNotSupported
:
2440 MOZ_ASSERT(!cx
->isExceptionPending());
2441 JS_ReportErrorASCII(cx
, "Asm.js is not supported by XDR");
2443 case JS::TranscodeResult::Failure_BadDecode
:
2444 MOZ_ASSERT(!cx
->isExceptionPending());
2445 JS_ReportErrorASCII(cx
, "XDR data corruption");
2448 case JS::TranscodeResult::Throw
:
2449 MOZ_ASSERT(cx
->isExceptionPending());
2454 static bool Evaluate(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2455 CallArgs args
= CallArgsFromVp(argc
, vp
);
2457 if (args
.length() < 1 || args
.length() > 2) {
2458 JS_ReportErrorNumberASCII(
2459 cx
, my_GetErrorMessage
, nullptr,
2460 args
.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS
: JSSMSG_TOO_MANY_ARGS
,
2465 RootedString
code(cx
, nullptr);
2466 RootedObject
cacheEntry(cx
, nullptr);
2467 if (args
[0].isString()) {
2468 code
= args
[0].toString();
2469 } else if (args
[0].isObject() &&
2470 CacheEntry_isCacheEntry(&args
[0].toObject())) {
2471 cacheEntry
= &args
[0].toObject();
2472 code
= CacheEntry_getSource(cx
, cacheEntry
);
2478 if (!code
|| (args
.length() == 2 && args
[1].isPrimitive())) {
2479 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2480 JSSMSG_INVALID_ARGS
, "evaluate");
2484 RootedObject
opts(cx
);
2485 if (args
.length() == 2) {
2486 if (!args
[1].isObject()) {
2487 JS_ReportErrorASCII(cx
, "evaluate: The 2nd argument must be an object");
2491 opts
= &args
[1].toObject();
2494 RootedObject
global(cx
, JS::CurrentGlobalOrNull(cx
));
2497 // Check "global" property before everything to use the given global's
2498 // option as the default value.
2499 Maybe
<CompileOptions
> maybeOptions
;
2502 if (!JS_GetProperty(cx
, opts
, "global", &v
)) {
2505 if (!v
.isUndefined()) {
2507 global
= js::CheckedUnwrapDynamic(&v
.toObject(), cx
,
2508 /* stopAtWindowProxy = */ false);
2513 if (!global
|| !(JS::GetClass(global
)->flags
& JSCLASS_IS_GLOBAL
)) {
2514 JS_ReportErrorNumberASCII(
2515 cx
, GetErrorMessage
, nullptr, JSMSG_UNEXPECTED_TYPE
,
2516 "\"global\" passed to evaluate()", "not a global object");
2520 JSAutoRealm
ar(cx
, global
);
2521 maybeOptions
.emplace(cx
);
2524 if (!maybeOptions
) {
2525 // If "global" property is not given, use the current global's option as
2526 // the default value.
2527 maybeOptions
.emplace(cx
);
2530 CompileOptions
& options
= maybeOptions
.ref();
2531 UniqueChars fileNameBytes
;
2532 RootedString
displayURL(cx
);
2533 RootedString
sourceMapURL(cx
);
2534 bool catchTermination
= false;
2535 bool loadBytecode
= false;
2536 bool saveIncrementalBytecode
= false;
2537 bool execute
= true;
2538 bool assertEqBytecode
= false;
2539 JS::RootedObjectVector
envChain(cx
);
2540 RootedObject
callerGlobal(cx
, cx
->global());
2542 options
.setIntroductionType("js shell evaluate")
2543 .setFileAndLine("@evaluate", 1)
2544 .setDeferDebugMetadata();
2546 RootedValue
privateValue(cx
);
2547 RootedString
elementAttributeName(cx
);
2550 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
2553 if (!ParseDebugMetadata(cx
, opts
, &privateValue
, &elementAttributeName
)) {
2556 if (!ParseSourceOptions(cx
, opts
, &displayURL
, &sourceMapURL
)) {
2561 if (!JS_GetProperty(cx
, opts
, "catchTermination", &v
)) {
2564 if (!v
.isUndefined()) {
2565 catchTermination
= ToBoolean(v
);
2568 if (!JS_GetProperty(cx
, opts
, "loadBytecode", &v
)) {
2571 if (!v
.isUndefined()) {
2572 loadBytecode
= ToBoolean(v
);
2575 if (!JS_GetProperty(cx
, opts
, "saveIncrementalBytecode", &v
)) {
2578 if (!v
.isUndefined()) {
2579 saveIncrementalBytecode
= ToBoolean(v
);
2582 if (!JS_GetProperty(cx
, opts
, "execute", &v
)) {
2585 if (!v
.isUndefined()) {
2586 execute
= ToBoolean(v
);
2589 if (!JS_GetProperty(cx
, opts
, "assertEqBytecode", &v
)) {
2592 if (!v
.isUndefined()) {
2593 assertEqBytecode
= ToBoolean(v
);
2596 if (!JS_GetProperty(cx
, opts
, "envChainObject", &v
)) {
2599 if (!v
.isUndefined()) {
2600 if (!v
.isObject()) {
2601 JS_ReportErrorNumberASCII(
2602 cx
, GetErrorMessage
, nullptr, JSMSG_UNEXPECTED_TYPE
,
2603 "\"envChainObject\" passed to evaluate()", "not an object");
2607 JSObject
* obj
= &v
.toObject();
2608 if (obj
->isUnqualifiedVarObj()) {
2609 JS_ReportErrorASCII(
2611 "\"envChainObject\" passed to evaluate() should not be an "
2612 "unqualified variables object");
2616 if (!envChain
.append(obj
)) {
2621 // We cannot load or save the bytecode if we have no object where the
2622 // bytecode cache is stored.
2623 if (loadBytecode
|| saveIncrementalBytecode
) {
2625 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2626 JSSMSG_INVALID_ARGS
, "evaluate");
2632 if (envChain
.length() != 0) {
2633 // Wrap the envChainObject list into target realm.
2634 JSAutoRealm
ar(cx
, global
);
2635 for (size_t i
= 0; i
< envChain
.length(); ++i
) {
2636 if (!JS_WrapObject(cx
, envChain
[i
])) {
2641 options
.setNonSyntacticScope(true);
2644 // The `loadBuffer` we use below outlives the Stencil we generate so we can
2645 // use its contents directly in the Stencil.
2646 options
.borrowBuffer
= true;
2648 // We need to track the options used to generate bytecode for a CacheEntry to
2649 // avoid mismatches. This is primarily a concern when fuzzing the jsshell.
2650 CacheOptionSet cacheOptions
;
2651 cacheOptions
.initFromOptions(options
);
2653 JS::TranscodeBuffer loadBuffer
;
2654 JS::TranscodeBuffer saveBuffer
;
2657 size_t loadLength
= 0;
2658 uint8_t* loadData
= nullptr;
2660 if (!CacheEntry_compatible(cx
, cacheEntry
, cacheOptions
)) {
2664 loadData
= CacheEntry_getBytecode(cx
, cacheEntry
, &loadLength
);
2668 if (!loadBuffer
.append(loadData
, loadLength
)) {
2669 JS_ReportOutOfMemory(cx
);
2675 JSAutoRealm
ar(cx
, global
);
2676 RefPtr
<JS::Stencil
> stencil
;
2679 JS::TranscodeRange
range(loadBuffer
.begin(), loadBuffer
.length());
2680 JS::DecodeOptions
decodeOptions(options
);
2682 JS::TranscodeResult rv
=
2683 JS::DecodeStencil(cx
, decodeOptions
, range
, getter_AddRefs(stencil
));
2684 if (JS::IsTranscodeFailureResult(rv
)) {
2685 JS_ReportErrorASCII(cx
, "failed to decode cache");
2689 if (!ConvertTranscodeResultToJSException(cx
, rv
)) {
2693 AutoStableStringChars
linearChars(cx
);
2694 if (!linearChars
.initTwoByte(cx
, code
)) {
2698 JS::SourceText
<char16_t
> srcBuf
;
2699 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
2703 stencil
= JS::CompileGlobalScriptToStencil(cx
, options
, srcBuf
);
2709 if (!js::ValidateLazinessOfStencilAndGlobal(cx
, *stencil
)) {
2713 JS::InstantiateOptions
instantiateOptions(options
);
2714 RootedScript
script(
2715 cx
, JS::InstantiateGlobalStencil(cx
, instantiateOptions
, stencil
));
2720 AutoReportFrontendContext
fc(cx
);
2721 if (!SetSourceOptions(cx
, &fc
, script
->scriptSource(), displayURL
,
2726 if (!JS::UpdateDebugMetadata(cx
, script
, instantiateOptions
, privateValue
,
2727 elementAttributeName
, nullptr, nullptr)) {
2731 if (saveIncrementalBytecode
) {
2732 if (!JS::StartIncrementalEncoding(cx
, std::move(stencil
))) {
2738 if (!(envChain
.empty()
2739 ? JS_ExecuteScript(cx
, script
, args
.rval())
2740 : JS_ExecuteScript(cx
, envChain
, script
, args
.rval()))) {
2741 if (catchTermination
&& !JS_IsExceptionPending(cx
)) {
2742 JSAutoRealm
ar1(cx
, callerGlobal
);
2743 JSString
* str
= JS_NewStringCopyZ(cx
, "terminated");
2747 args
.rval().setString(str
);
2754 // Serialize the encoded bytecode, recorded before the execution, into a
2755 // buffer which can be deserialized linearly.
2756 if (saveIncrementalBytecode
) {
2757 if (!FinishIncrementalEncoding(cx
, script
, saveBuffer
)) {
2763 if (saveIncrementalBytecode
) {
2764 // If we are both loading and saving, we assert that we are going to
2765 // replace the current bytecode by the same stream of bytes.
2766 if (loadBytecode
&& assertEqBytecode
) {
2767 if (saveBuffer
.length() != loadBuffer
.length()) {
2768 char loadLengthStr
[16];
2769 SprintfLiteral(loadLengthStr
, "%zu", loadBuffer
.length());
2770 char saveLengthStr
[16];
2771 SprintfLiteral(saveLengthStr
, "%zu", saveBuffer
.length());
2773 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2774 JSSMSG_CACHE_EQ_SIZE_FAILED
, loadLengthStr
,
2779 if (!ArrayEqual(loadBuffer
.begin(), saveBuffer
.begin(),
2780 loadBuffer
.length())) {
2781 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2782 JSSMSG_CACHE_EQ_CONTENT_FAILED
);
2787 size_t saveLength
= saveBuffer
.length();
2788 if (saveLength
>= INT32_MAX
) {
2789 JS_ReportErrorASCII(cx
, "Cannot save large cache entry content");
2792 uint8_t* saveData
= saveBuffer
.extractOrCopyRawBuffer();
2793 if (!CacheEntry_setBytecode(cx
, cacheEntry
, cacheOptions
, saveData
,
2800 return JS_WrapValue(cx
, args
.rval());
2803 JSString
* js::shell::FileAsString(JSContext
* cx
, JS::HandleString pathnameStr
) {
2804 UniqueChars pathname
= JS_EncodeStringToUTF8(cx
, pathnameStr
);
2809 FILE* file
= OpenFile(cx
, pathname
.get(), "rb");
2814 AutoCloseFile
autoClose(file
);
2817 if (fstat(fileno(file
), &st
) != 0) {
2818 JS_ReportErrorUTF8(cx
, "can't stat %s", pathname
.get());
2822 if ((st
.st_mode
& S_IFMT
) != S_IFREG
) {
2823 JS_ReportErrorUTF8(cx
, "can't read non-regular file %s", pathname
.get());
2828 if (!FileSize(cx
, pathname
.get(), file
, &len
)) {
2832 UniqueChars
buf(js_pod_malloc
<char>(len
+ 1));
2834 JS_ReportErrorUTF8(cx
, "out of memory reading %s", pathname
.get());
2838 if (!ReadFile(cx
, pathname
.get(), file
, buf
.get(), len
)) {
2842 UniqueTwoByteChars
ucbuf(
2843 JS::LossyUTF8CharsToNewTwoByteCharsZ(cx
, JS::UTF8Chars(buf
.get(), len
),
2844 &len
, js::MallocArena
)
2847 JS_ReportErrorUTF8(cx
, "Invalid UTF-8 in file '%s'", pathname
.get());
2851 return JS_NewUCStringCopyN(cx
, ucbuf
.get(), len
);
2855 * Function to run scripts and return compilation + execution time. Semantics
2856 * are closely modelled after the equivalent function in WebKit, as this is used
2857 * to produce benchmark timings by SunSpider.
2859 static bool Run(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2860 CallArgs args
= CallArgsFromVp(argc
, vp
);
2861 if (args
.length() != 1) {
2862 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
2863 JSSMSG_INVALID_ARGS
, "run");
2867 RootedString
str(cx
, JS::ToString(cx
, args
[0]));
2871 args
[0].setString(str
);
2873 str
= FileAsString(cx
, str
);
2878 AutoStableStringChars
linearChars(cx
);
2879 if (!linearChars
.initTwoByte(cx
, str
)) {
2883 JS::SourceText
<char16_t
> srcBuf
;
2884 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
2888 RootedScript
script(cx
);
2889 int64_t startClock
= PRMJ_Now();
2891 UniqueChars filename
= JS_EncodeStringToUTF8(cx
, str
);
2896 JS::CompileOptions
options(cx
);
2897 options
.setIntroductionType("js shell run")
2898 .setFileAndLine(filename
.get(), 1)
2900 .setNoScriptRval(true)
2901 .setEagerDelazificationStrategy(defaultDelazificationMode
);
2903 script
= JS::Compile(cx
, options
, srcBuf
);
2909 if (!JS_ExecuteScript(cx
, script
)) {
2913 int64_t endClock
= PRMJ_Now();
2915 args
.rval().setDouble((endClock
- startClock
) / double(PRMJ_USEC_PER_MSEC
));
2919 static int js_fgets(char* buf
, int size
, FILE* file
) {
2928 // Use the fastest available getc.
2930 #if defined(HAVE_GETC_UNLOCKED)
2932 #elif defined(HAVE__GETC_NOLOCK)
2940 for (i
= 0; i
< n
&& (c
= fast_getc(file
)) != EOF
; i
++) {
2942 if (c
== '\n') { // any \n ends a line
2943 i
++; // keep the \n; we know there is room for \0
2946 if (crflag
) { // \r not followed by \n ends line at the \r
2948 break; // and overwrite c in buf with \0
2950 crflag
= (c
== '\r');
2958 * function readline()
2959 * Provides a hook for scripts to read a line from stdin.
2961 static bool ReadLine(JSContext
* cx
, unsigned argc
, Value
* vp
) {
2962 CallArgs args
= CallArgsFromVp(argc
, vp
);
2964 static constexpr size_t BUFSIZE
= 256;
2966 size_t buflength
= 0;
2967 size_t bufsize
= BUFSIZE
;
2968 char* buf
= (char*)JS_malloc(cx
, bufsize
);
2973 bool sawNewline
= false;
2975 while ((gotlength
= js_fgets(buf
+ buflength
, bufsize
- buflength
, from
)) >
2977 buflength
+= gotlength
;
2980 if (buf
[buflength
- 1] == '\n') {
2981 buf
[buflength
- 1] = '\0';
2984 } else if (buflength
< bufsize
- 1) {
2988 /* Else, grow our buffer for another pass. */
2991 if (bufsize
> buflength
) {
2992 tmp
= static_cast<char*>(JS_realloc(cx
, buf
, bufsize
/ 2, bufsize
));
2994 JS_ReportOutOfMemory(cx
);
3006 /* Treat the empty string specially. */
3007 if (buflength
== 0) {
3008 args
.rval().set(feof(from
) ? NullValue() : JS_GetEmptyStringValue(cx
));
3013 /* Shrink the buffer to the real size. */
3014 char* tmp
= static_cast<char*>(JS_realloc(cx
, buf
, bufsize
, buflength
));
3023 * Turn buf into a JSString. Note that buflength includes the trailing null
3027 JS_NewStringCopyN(cx
, buf
, sawNewline
? buflength
- 1 : buflength
);
3033 args
.rval().setString(str
);
3038 * function readlineBuf()
3039 * Provides a hook for scripts to emulate readline() using a string object.
3041 static bool ReadLineBuf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3042 CallArgs args
= CallArgsFromVp(argc
, vp
);
3043 ShellContext
* sc
= GetShellContext(cx
);
3045 if (!args
.length()) {
3046 if (!sc
->readLineBuf
) {
3047 JS_ReportErrorASCII(cx
,
3048 "No source buffer set. You must initially "
3049 "call readlineBuf with an argument.");
3053 char* currentBuf
= sc
->readLineBuf
.get() + sc
->readLineBufPos
;
3054 size_t buflen
= strlen(currentBuf
);
3057 args
.rval().setNull();
3062 while (len
< buflen
) {
3063 if (currentBuf
[len
] == '\n') {
3069 JSString
* str
= JS_NewStringCopyUTF8N(cx
, JS::UTF8Chars(currentBuf
, len
));
3074 if (currentBuf
[len
] == '\0') {
3075 sc
->readLineBufPos
+= len
;
3077 sc
->readLineBufPos
+= len
+ 1;
3080 args
.rval().setString(str
);
3084 if (args
.length() == 1) {
3085 sc
->readLineBuf
= nullptr;
3086 sc
->readLineBufPos
= 0;
3088 RootedString
str(cx
, JS::ToString(cx
, args
[0]));
3092 sc
->readLineBuf
= JS_EncodeStringToUTF8(cx
, str
);
3093 if (!sc
->readLineBuf
) {
3097 args
.rval().setUndefined();
3101 JS_ReportErrorASCII(cx
, "Must specify at most one argument");
3105 static bool PutStr(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3106 CallArgs args
= CallArgsFromVp(argc
, vp
);
3108 if (args
.length() != 0) {
3109 if (!gOutFile
->isOpen()) {
3110 JS_ReportErrorASCII(cx
, "output file is closed");
3114 RootedString
str(cx
, JS::ToString(cx
, args
[0]));
3118 UniqueChars bytes
= JS_EncodeStringToUTF8(cx
, str
);
3122 fputs(bytes
.get(), gOutFile
->fp
);
3123 fflush(gOutFile
->fp
);
3126 args
.rval().setUndefined();
3130 static bool Now(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3131 CallArgs args
= CallArgsFromVp(argc
, vp
);
3132 double now
= PRMJ_Now() / double(PRMJ_USEC_PER_MSEC
);
3133 args
.rval().setDouble(now
);
3137 static bool CpuNow(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3138 CallArgs args
= CallArgsFromVp(argc
, vp
);
3139 double now
= double(std::clock()) / double(CLOCKS_PER_SEC
);
3140 args
.rval().setDouble(now
);
3144 static bool PrintInternal(JSContext
* cx
, const CallArgs
& args
, RCFile
* file
) {
3145 if (!file
->isOpen()) {
3146 JS_ReportErrorASCII(cx
, "output file is closed");
3150 for (unsigned i
= 0; i
< args
.length(); i
++) {
3151 RootedString
str(cx
, JS::ToString(cx
, args
[i
]));
3155 UniqueChars bytes
= JS_EncodeStringToUTF8(cx
, str
);
3159 fprintf(file
->fp
, "%s%s", i
? " " : "", bytes
.get());
3162 fputc('\n', file
->fp
);
3165 args
.rval().setUndefined();
3169 static bool Print(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3170 CallArgs args
= CallArgsFromVp(argc
, vp
);
3171 #ifdef FUZZING_INTERFACES
3172 if (fuzzHaveModule
&& !fuzzDoDebug
) {
3173 // When fuzzing and not debugging, suppress any print() output,
3174 // as it slows down fuzzing and makes libFuzzer's output hard
3176 args
.rval().setUndefined();
3179 #endif // FUZZING_INTERFACES
3180 return PrintInternal(cx
, args
, gOutFile
);
3183 static bool PrintErr(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3184 CallArgs args
= CallArgsFromVp(argc
, vp
);
3185 return PrintInternal(cx
, args
, gErrFile
);
3188 static bool Help(JSContext
* cx
, unsigned argc
, Value
* vp
);
3190 static bool Quit(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3191 ShellContext
* sc
= GetShellContext(cx
);
3193 // Print a message to stderr in differential testing to help jsfunfuzz
3194 // find uncatchable-exception bugs.
3195 if (js::SupportDifferentialTesting()) {
3196 fprintf(stderr
, "quit called\n");
3199 CallArgs args
= CallArgsFromVp(argc
, vp
);
3201 if (!ToInt32(cx
, args
.get(0), &code
)) {
3205 // The fuzzers check the shell's exit code and assume a value >= 128 means
3206 // the process crashed (for instance, SIGSEGV will result in code 139). On
3207 // POSIX platforms, the exit code is 8-bit and negative values can also
3208 // result in an exit code >= 128. We restrict the value to range [0, 127] to
3209 // avoid false positives.
3210 if (code
< 0 || code
>= 128) {
3211 JS_ReportErrorASCII(cx
, "quit exit code should be in range 0-127");
3215 js::StopDrainingJobQueue(cx
);
3216 sc
->exitCode
= code
;
3217 sc
->quitting
= true;
3221 static bool StartTimingMutator(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3222 CallArgs args
= CallArgsFromVp(argc
, vp
);
3223 if (args
.length() > 0) {
3224 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
3225 JSSMSG_TOO_MANY_ARGS
, "startTimingMutator");
3229 if (!cx
->runtime()->gc
.stats().startTimingMutator()) {
3230 JS_ReportErrorASCII(
3231 cx
, "StartTimingMutator should only be called from outside of GC");
3235 args
.rval().setUndefined();
3239 static bool StopTimingMutator(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3240 CallArgs args
= CallArgsFromVp(argc
, vp
);
3241 if (args
.length() > 0) {
3242 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
3243 JSSMSG_TOO_MANY_ARGS
, "stopTimingMutator");
3247 double mutator_ms
, gc_ms
;
3248 if (!cx
->runtime()->gc
.stats().stopTimingMutator(mutator_ms
, gc_ms
)) {
3249 JS_ReportErrorASCII(cx
,
3250 "stopTimingMutator called when not timing the mutator");
3253 double total_ms
= mutator_ms
+ gc_ms
;
3254 if (total_ms
> 0 && gOutFile
->isOpen()) {
3255 fprintf(gOutFile
->fp
, "Mutator: %.3fms (%.1f%%), GC: %.3fms (%.1f%%)\n",
3256 mutator_ms
, mutator_ms
/ total_ms
* 100.0, gc_ms
,
3257 gc_ms
/ total_ms
* 100.0);
3260 args
.rval().setUndefined();
3264 static const char* ToSource(JSContext
* cx
, HandleValue vp
, UniqueChars
* bytes
) {
3265 RootedString
str(cx
, JS_ValueToSource(cx
, vp
));
3267 *bytes
= JS_EncodeStringToUTF8(cx
, str
);
3269 return bytes
->get();
3272 JS_ClearPendingException(cx
);
3273 return "<<error converting value to string>>";
3276 static bool AssertEq(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3277 CallArgs args
= CallArgsFromVp(argc
, vp
);
3278 if (!(args
.length() == 2 || (args
.length() == 3 && args
[2].isString()))) {
3279 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
3280 (args
.length() < 2) ? JSSMSG_NOT_ENOUGH_ARGS
3281 : (args
.length() == 3) ? JSSMSG_INVALID_ARGS
3282 : JSSMSG_TOO_MANY_ARGS
,
3288 if (!JS::SameValue(cx
, args
[0], args
[1], &same
)) {
3292 UniqueChars bytes0
, bytes1
;
3293 const char* actual
= ToSource(cx
, args
[0], &bytes0
);
3294 const char* expected
= ToSource(cx
, args
[1], &bytes1
);
3295 if (args
.length() == 2) {
3296 JS_ReportErrorNumberUTF8(cx
, my_GetErrorMessage
, nullptr,
3297 JSSMSG_ASSERT_EQ_FAILED
, actual
, expected
);
3299 RootedString
message(cx
, args
[2].toString());
3300 UniqueChars bytes2
= QuoteString(cx
, message
);
3304 JS_ReportErrorNumberUTF8(cx
, my_GetErrorMessage
, nullptr,
3305 JSSMSG_ASSERT_EQ_FAILED_MSG
, actual
, expected
,
3310 args
.rval().setUndefined();
3314 static JSScript
* GetTopScript(JSContext
* cx
) {
3315 NonBuiltinScriptFrameIter
iter(cx
);
3316 return iter
.done() ? nullptr : iter
.script();
3319 static bool GetScriptAndPCArgs(JSContext
* cx
, CallArgs
& args
,
3320 MutableHandleScript scriptp
, int32_t* ip
) {
3321 RootedScript
script(cx
, GetTopScript(cx
));
3323 if (!args
.get(0).isUndefined()) {
3324 HandleValue v
= args
[0];
3325 unsigned intarg
= 0;
3326 if (v
.isObject() && JS::GetClass(&v
.toObject())->isJSFunction()) {
3327 script
= TestingFunctionArgumentToScript(cx
, v
);
3333 if (!args
.get(intarg
).isUndefined()) {
3334 if (!JS::ToInt32(cx
, args
[intarg
], ip
)) {
3337 if ((uint32_t)*ip
>= script
->length()) {
3338 JS_ReportErrorASCII(cx
, "Invalid PC");
3344 scriptp
.set(script
);
3349 static bool LineToPC(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3350 CallArgs args
= CallArgsFromVp(argc
, vp
);
3352 if (args
.length() == 0) {
3353 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
3354 JSSMSG_LINE2PC_USAGE
);
3358 RootedScript
script(cx
, GetTopScript(cx
));
3359 int32_t lineArg
= 0;
3360 if (args
[0].isObject() && args
[0].toObject().is
<JSFunction
>()) {
3361 script
= TestingFunctionArgumentToScript(cx
, args
[0]);
3369 if (!ToUint32(cx
, args
.get(lineArg
), &lineno
)) {
3373 jsbytecode
* pc
= LineNumberToPC(script
, lineno
);
3377 args
.rval().setInt32(script
->pcToOffset(pc
));
3381 static bool PCToLine(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3382 CallArgs args
= CallArgsFromVp(argc
, vp
);
3383 RootedScript
script(cx
);
3387 if (!GetScriptAndPCArgs(cx
, args
, &script
, &i
)) {
3390 lineno
= PCToLineNumber(script
, script
->offsetToPC(i
));
3394 args
.rval().setInt32(lineno
);
3398 #if defined(DEBUG) || defined(JS_JITSPEW)
3400 static bool Notes(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3401 CallArgs args
= CallArgsFromVp(argc
, vp
);
3402 JSSprinter
sprinter(cx
);
3403 if (!sprinter
.init()) {
3407 for (unsigned i
= 0; i
< args
.length(); i
++) {
3408 RootedScript
script(cx
, TestingFunctionArgumentToScript(cx
, args
[i
]));
3413 if (!JSScript::dumpSrcNotes(cx
, script
, &sprinter
)) {
3418 JSString
* str
= sprinter
.release(cx
);
3422 args
.rval().setString(str
);
3428 struct DisassembleOptionParser
{
3431 JSScript::DumpOptions options
;
3433 DisassembleOptionParser(unsigned argc
, Value
* argv
)
3434 : argc(argc
), argv(argv
) {}
3436 bool parse(JSContext
* cx
) {
3437 options
.recursive
= false;
3439 /* Read options off early arguments */
3440 while (argc
> 0 && argv
[0].isString()) {
3441 JSString
* str
= argv
[0].toString();
3442 JSLinearString
* linearStr
= JS_EnsureLinearString(cx
, str
);
3446 if (JS_LinearStringEqualsLiteral(linearStr
, "-r")) {
3447 options
.recursive
= true;
3458 } /* anonymous namespace */
3460 static bool DisassembleToSprinter(JSContext
* cx
, unsigned argc
, Value
* vp
,
3461 StringPrinter
* sp
) {
3462 CallArgs args
= CallArgsFromVp(argc
, vp
);
3463 DisassembleOptionParser
p(args
.length(), args
.array());
3469 /* Without arguments, disassemble the current script. */
3470 RootedScript
script(cx
, GetTopScript(cx
));
3472 JSAutoRealm
ar(cx
, script
);
3473 if (!JSScript::dump(cx
, script
, p
.options
, sp
)) {
3478 for (unsigned i
= 0; i
< p
.argc
; i
++) {
3479 RootedFunction
fun(cx
);
3480 RootedScript
script(cx
);
3481 RootedValue
value(cx
, p
.argv
[i
]);
3482 if (value
.isObject() && value
.toObject().is
<ShellModuleObjectWrapper
>()) {
3483 script
= value
.toObject()
3484 .as
<ShellModuleObjectWrapper
>()
3488 script
= TestingFunctionArgumentToScript(cx
, value
, fun
.address());
3494 if (!JSScript::dump(cx
, script
, p
.options
, sp
)) {
3503 static bool DisassembleToString(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3504 CallArgs args
= CallArgsFromVp(argc
, vp
);
3505 JSSprinter
sprinter(cx
);
3506 if (!sprinter
.init()) {
3509 if (!DisassembleToSprinter(cx
, args
.length(), vp
, &sprinter
)) {
3513 JSString
* str
= sprinter
.release(cx
);
3517 args
.rval().setString(str
);
3521 static bool Disassemble(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3522 CallArgs args
= CallArgsFromVp(argc
, vp
);
3524 if (!gOutFile
->isOpen()) {
3525 JS_ReportErrorASCII(cx
, "output file is closed");
3529 Sprinter
sprinter(cx
);
3530 if (!sprinter
.init()) {
3533 if (!DisassembleToSprinter(cx
, args
.length(), vp
, &sprinter
)) {
3537 JS::UniqueChars str
= sprinter
.release();
3541 fprintf(gOutFile
->fp
, "%s\n", str
.get());
3542 args
.rval().setUndefined();
3546 static bool DisassFile(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3547 CallArgs args
= CallArgsFromVp(argc
, vp
);
3549 if (!gOutFile
->isOpen()) {
3550 JS_ReportErrorASCII(cx
, "output file is closed");
3554 /* Support extra options at the start, just like Disassemble. */
3555 DisassembleOptionParser
p(args
.length(), args
.array());
3561 args
.rval().setUndefined();
3565 // We should change DisassembleOptionParser to store CallArgs.
3566 Rooted
<JSString
*> str(
3567 cx
, JS::ToString(cx
, HandleValue::fromMarkedLocation(&p
.argv
[0])));
3571 UniqueChars filename
= JS_EncodeStringToUTF8(cx
, str
);
3575 RootedScript
script(cx
);
3578 CompileOptions
options(cx
);
3579 options
.setIntroductionType("js shell disFile")
3580 .setFileAndLine(filename
.get(), 1)
3582 .setNoScriptRval(true)
3583 .setEagerDelazificationStrategy(defaultDelazificationMode
);
3585 script
= JS::CompileUtf8Path(cx
, options
, filename
.get());
3591 Sprinter
sprinter(cx
);
3592 if (!sprinter
.init()) {
3595 if (JSScript::dump(cx
, script
, p
.options
, &sprinter
)) {
3599 JS::UniqueChars chars
= sprinter
.release();
3603 fprintf(gOutFile
->fp
, "%s\n", chars
.get());
3605 args
.rval().setUndefined();
3609 static bool DisassWithSrc(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3610 CallArgs args
= CallArgsFromVp(argc
, vp
);
3612 if (!gOutFile
->isOpen()) {
3613 JS_ReportErrorASCII(cx
, "output file is closed");
3617 const size_t lineBufLen
= 512;
3618 unsigned len
, line1
, line2
, bupline
;
3619 char linebuf
[lineBufLen
];
3620 static const char sep
[] = ";-------------------------";
3622 RootedScript
script(cx
);
3623 for (unsigned i
= 0; i
< args
.length(); i
++) {
3624 script
= TestingFunctionArgumentToScript(cx
, args
[i
]);
3629 if (!script
->filename()) {
3630 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
3631 JSSMSG_FILE_SCRIPTS_ONLY
);
3635 FILE* file
= OpenFile(cx
, script
->filename(), "rb");
3639 auto closeFile
= MakeScopeExit([file
] { fclose(file
); });
3641 jsbytecode
* pc
= script
->code();
3642 jsbytecode
* end
= script
->codeEnd();
3644 Sprinter
sprinter(cx
);
3645 if (!sprinter
.init()) {
3649 /* burn the leading lines */
3650 line2
= PCToLineNumber(script
, pc
);
3651 for (line1
= 0; line1
< line2
- 1; line1
++) {
3652 char* tmp
= fgets(linebuf
, lineBufLen
, file
);
3654 JS_ReportErrorUTF8(cx
, "failed to read %s fully", script
->filename());
3661 line2
= PCToLineNumber(script
, pc
);
3663 if (line2
< line1
) {
3664 if (bupline
!= line2
) {
3666 sprinter
.printf("%s %3u: BACKUP\n", sep
, line2
);
3669 if (bupline
&& line1
== line2
) {
3670 sprinter
.printf("%s %3u: RESTORE\n", sep
, line2
);
3673 while (line1
< line2
) {
3674 if (!fgets(linebuf
, lineBufLen
, file
)) {
3675 JS_ReportErrorNumberUTF8(cx
, my_GetErrorMessage
, nullptr,
3676 JSSMSG_UNEXPECTED_EOF
, script
->filename());
3680 sprinter
.printf("%s %3u: %s", sep
, line1
, linebuf
);
3685 Disassemble1(cx
, script
, pc
, script
->pcToOffset(pc
), true, &sprinter
);
3693 JS::UniqueChars str
= sprinter
.release();
3697 fprintf(gOutFile
->fp
, "%s\n", str
.get());
3700 args
.rval().setUndefined();
3704 #endif /* defined(DEBUG) || defined(JS_JITSPEW) */
3706 #ifdef JS_CACHEIR_SPEW
3707 static bool CacheIRHealthReport(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3708 CallArgs args
= CallArgsFromVp(argc
, vp
);
3710 js::jit::CacheIRHealth cih
;
3711 RootedScript
script(cx
);
3713 // In the case that we are calling this function from the shell and
3714 // the environment variable is not set, AutoSpewChannel automatically
3715 // sets and unsets the proper channel for the duration of spewing
3717 AutoSpewChannel
channel(cx
, SpewChannel::CacheIRHealthReport
, script
);
3719 // Calling CacheIRHealthReport without any arguments will create health
3720 // reports for all scripts in the zone.
3721 if (jit::JitZone
* jitZone
= cx
->zone()->jitZone()) {
3722 jitZone
->forEachJitScript([&](jit::JitScript
* jitScript
) {
3723 script
= jitScript
->owningScript();
3724 if (!script
->selfHosted()) {
3725 cih
.healthReportForScript(cx
, script
, js::jit::SpewContext::Shell
);
3730 RootedValue
value(cx
, args
.get(0));
3732 if (value
.isObject() && value
.toObject().is
<ShellModuleObjectWrapper
>()) {
3734 value
.toObject().as
<ShellModuleObjectWrapper
>().get()->maybeScript();
3736 script
= TestingFunctionArgumentToScript(cx
, args
.get(0));
3743 cih
.healthReportForScript(cx
, script
, js::jit::SpewContext::Shell
);
3746 args
.rval().setUndefined();
3749 #endif /* JS_CACHEIR_SPEW */
3751 /* Pretend we can always preserve wrappers for dummy DOM objects. */
3752 static bool DummyPreserveWrapperCallback(JSContext
* cx
, HandleObject obj
) {
3756 static bool DummyHasReleasedWrapperCallback(HandleObject obj
) { return true; }
3758 #ifdef FUZZING_JS_FUZZILLI
3759 static bool fuzzilli_hash(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3760 CallArgs args
= CallArgsFromVp(argc
, vp
);
3761 args
.rval().setUndefined();
3767 JS::Handle
<JS::Value
> v
= args
.get(0);
3769 int32_t i
= v
.toInt32();
3770 hash
= FuzzilliHashDouble((double)i
);
3771 } else if (v
.isDouble()) {
3772 double d
= v
.toDouble();
3773 d
= JS::CanonicalizeNaN(d
);
3774 hash
= FuzzilliHashDouble(d
);
3775 } else if (v
.isNull()) {
3776 hash
= FuzzilliHashDouble(1.0);
3777 } else if (v
.isUndefined()) {
3778 hash
= FuzzilliHashDouble(2.0);
3779 } else if (v
.isBoolean()) {
3780 hash
= FuzzilliHashDouble(3.0 + v
.toBoolean());
3781 } else if (v
.isBigInt()) {
3782 JS::BigInt
* bigInt
= v
.toBigInt();
3783 hash
= FuzzilliHashBigInt(bigInt
);
3784 } else if (v
.isObject()) {
3785 JSObject
& obj
= v
.toObject();
3786 FuzzilliHashObject(cx
, &obj
);
3792 cx
->executionHashInputs
+= 1;
3793 cx
->executionHash
= mozilla::RotateLeft(cx
->executionHash
+ hash
, 1);
3797 // We have to assume that the fuzzer will be able to call this function e.g. by
3798 // enumerating the properties of the global object and eval'ing them. As such
3799 // this function is implemented in a way that requires passing some magic value
3800 // as first argument (with the idea being that the fuzzer won't be able to
3801 // generate this value) which then also acts as a selector for the operation
3803 static bool Fuzzilli(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3804 CallArgs args
= CallArgsFromVp(argc
, vp
);
3806 RootedString
arg(cx
, JS::ToString(cx
, args
.get(0)));
3810 Rooted
<JSLinearString
*> operation(cx
, StringToLinearString(cx
, arg
));
3815 if (StringEqualsAscii(operation
, "FUZZILLI_CRASH")) {
3817 if (!ToInt32(cx
, args
.get(1), &type
)) {
3821 // With this, we can test the various ways the JS shell can crash and make
3822 // sure that Fuzzilli is able to detect all of these failures properly.
3825 *((int*)0x41414141) = 0x1337;
3828 MOZ_RELEASE_ASSERT(false);
3839 } else if (StringEqualsAscii(operation
, "FUZZILLI_PRINT")) {
3840 static FILE* fzliout
= fdopen(REPRL_DWFD
, "w");
3844 "Fuzzer output channel not available, printing to stdout instead\n");
3848 RootedString
str(cx
, JS::ToString(cx
, args
.get(1)));
3852 UniqueChars bytes
= JS_EncodeStringToUTF8(cx
, str
);
3856 fprintf(fzliout
, "%s\n", bytes
.get());
3858 } else if (StringEqualsAscii(operation
, "FUZZILLI_RANDOM")) {
3859 // This is an entropy source which can be called during fuzzing.
3860 // Its currently used to tests whether Fuzzilli detects non-deterministic
3862 args
.rval().setInt32(static_cast<uint32_t>(mozilla::RandomUint64OrDie()));
3866 args
.rval().setUndefined();
3870 static bool FuzzilliReprlGetAndRun(JSContext
* cx
) {
3871 size_t scriptSize
= 0;
3874 MOZ_RELEASE_ASSERT(read(REPRL_CRFD
, &action
, 4) == 4);
3875 if (action
== 'cexe') {
3876 MOZ_RELEASE_ASSERT(read(REPRL_CRFD
, &scriptSize
, 8) == 8);
3878 fprintf(stderr
, "Unknown action: %u\n", action
);
3882 CompileOptions
options(cx
);
3883 options
.setIntroductionType("reprl")
3884 .setFileAndLine("reprl", 1)
3886 .setNoScriptRval(true)
3887 .setEagerDelazificationStrategy(defaultDelazificationMode
);
3889 char* scriptSrc
= static_cast<char*>(js_malloc(scriptSize
));
3891 char* ptr
= scriptSrc
;
3892 size_t remaining
= scriptSize
;
3893 while (remaining
> 0) {
3894 ssize_t rv
= read(REPRL_DRFD
, ptr
, remaining
);
3896 fprintf(stderr
, "Failed to load script\n");
3903 JS::SourceText
<Utf8Unit
> srcBuf
;
3904 if (!srcBuf
.init(cx
, scriptSrc
, scriptSize
,
3905 JS::SourceOwnership::TakeOwnership
)) {
3909 RootedScript
script(cx
, JS::Compile(cx
, options
, srcBuf
));
3914 if (!JS_ExecuteScript(cx
, script
)) {
3921 #endif /* FUZZING_JS_FUZZILLI */
3923 static bool FuzzilliUseReprlMode(OptionParser
* op
) {
3924 #ifdef FUZZING_JS_FUZZILLI
3925 // Check if we should use REPRL mode
3926 bool reprl_mode
= op
->getBoolOption("reprl");
3928 // Check in with parent
3929 char helo
[] = "HELO";
3930 if (write(REPRL_CWFD
, helo
, 4) != 4 || read(REPRL_CRFD
, helo
, 4) != 4) {
3934 if (memcmp(helo
, "HELO", 4) != 0) {
3935 fprintf(stderr
, "Invalid response from parent\n");
3942 #endif /* FUZZING_JS_FUZZILLI */
3945 static bool Crash(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3946 CallArgs args
= CallArgsFromVp(argc
, vp
);
3947 if (args
.length() == 0) {
3948 MOZ_CRASH("forced crash");
3950 RootedString
message(cx
, JS::ToString(cx
, args
[0]));
3954 UniqueChars utf8chars
= JS_EncodeStringToUTF8(cx
, message
);
3958 if (args
.get(1).isObject()) {
3960 RootedObject
opts(cx
, &args
[1].toObject());
3961 if (!JS_GetProperty(cx
, opts
, "suppress_minidump", &v
)) {
3964 if (v
.isBoolean() && v
.toBoolean()) {
3965 js::NoteIntentionalCrash();
3969 MOZ_ReportCrash(utf8chars
.get(), __FILE__
, __LINE__
);
3971 MOZ_CRASH_UNSAFE(utf8chars
.get());
3974 static bool GetSLX(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3975 CallArgs args
= CallArgsFromVp(argc
, vp
);
3976 RootedScript
script(cx
);
3978 script
= TestingFunctionArgumentToScript(cx
, args
.get(0));
3982 args
.rval().setInt32(GetScriptLineExtent(script
));
3986 static bool ThrowError(JSContext
* cx
, unsigned argc
, Value
* vp
) {
3987 JS_ReportErrorASCII(cx
, "This is an error");
3991 static bool CopyErrorReportToObject(JSContext
* cx
, JSErrorReport
* report
,
3993 RootedString
nameStr(cx
);
3994 if (report
->exnType
== JSEXN_WARN
) {
3995 nameStr
= JS_NewStringCopyZ(cx
, "Warning");
4000 nameStr
= GetErrorTypeName(cx
, report
->exnType
);
4001 // GetErrorTypeName doesn't set an exception, but
4002 // can fail for InternalError or non-error objects.
4004 nameStr
= cx
->runtime()->emptyString
;
4007 RootedValue
nameVal(cx
, StringValue(nameStr
));
4008 if (!DefineDataProperty(cx
, obj
, cx
->names().name
, nameVal
)) {
4012 RootedString
messageStr(cx
, report
->newMessageString(cx
));
4016 RootedValue
messageVal(cx
, StringValue(messageStr
));
4017 if (!DefineDataProperty(cx
, obj
, cx
->names().message
, messageVal
)) {
4021 RootedValue
linenoVal(cx
, Int32Value(report
->lineno
));
4022 if (!DefineDataProperty(cx
, obj
, cx
->names().lineNumber
, linenoVal
)) {
4026 RootedValue
columnVal(cx
, Int32Value(report
->column
.oneOriginValue()));
4027 if (!DefineDataProperty(cx
, obj
, cx
->names().columnNumber
, columnVal
)) {
4031 RootedObject
notesArray(cx
, CreateErrorNotesArray(cx
, report
));
4036 RootedValue
notesArrayVal(cx
, ObjectValue(*notesArray
));
4037 return DefineDataProperty(cx
, obj
, cx
->names().notes
, notesArrayVal
);
4040 static bool CreateErrorReport(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4041 CallArgs args
= CallArgsFromVp(argc
, vp
);
4043 // We don't have a stack here, so just initialize with null.
4044 JS::ExceptionStack
exnStack(cx
, args
.get(0), nullptr);
4045 JS::ErrorReportBuilder
report(cx
);
4046 if (!report
.init(cx
, exnStack
, JS::ErrorReportBuilder::WithSideEffects
)) {
4050 MOZ_ASSERT(!report
.report()->isWarning());
4052 RootedObject
obj(cx
, JS_NewPlainObject(cx
));
4057 RootedString
toString(cx
, NewStringCopyUTF8Z(cx
, report
.toStringResult()));
4062 if (!JS_DefineProperty(cx
, obj
, "toStringResult", toString
,
4063 JSPROP_ENUMERATE
)) {
4067 if (!CopyErrorReportToObject(cx
, report
.report(), obj
)) {
4071 args
.rval().setObject(*obj
);
4075 #define LAZY_STANDARD_CLASSES
4077 /* A class for easily testing the inner/outer object callbacks. */
4078 typedef struct ComplexObject
{
4085 static bool sandbox_enumerate(JSContext
* cx
, JS::HandleObject obj
,
4086 JS::MutableHandleIdVector properties
,
4087 bool enumerableOnly
) {
4090 if (!JS_GetProperty(cx
, obj
, "lazy", &v
)) {
4094 if (!ToBoolean(v
)) {
4098 return JS_NewEnumerateStandardClasses(cx
, obj
, properties
, enumerableOnly
);
4101 static bool sandbox_resolve(JSContext
* cx
, HandleObject obj
, HandleId id
,
4104 if (!JS_GetProperty(cx
, obj
, "lazy", &v
)) {
4109 return JS_ResolveStandardClass(cx
, obj
, id
, resolvedp
);
4114 static const JSClassOps sandbox_classOps
= {
4115 nullptr, // addProperty
4116 nullptr, // delProperty
4117 nullptr, // enumerate
4118 sandbox_enumerate
, // newEnumerate
4119 sandbox_resolve
, // resolve
4120 nullptr, // mayResolve
4121 nullptr, // finalize
4123 nullptr, // construct
4124 JS_GlobalObjectTraceHook
, // trace
4127 static const JSClass sandbox_class
= {"sandbox", JSCLASS_GLOBAL_FLAGS
,
4130 static void SetStandardRealmOptions(JS::RealmOptions
& options
) {
4131 options
.creationOptions()
4132 .setSharedMemoryAndAtomicsEnabled(enableSharedMemory
)
4133 .setCoopAndCoepEnabled(false)
4134 .setWeakRefsEnabled(enableWeakRefs
4135 ? JS::WeakRefSpecifier::EnabledWithCleanupSome
4136 : JS::WeakRefSpecifier::Disabled
)
4137 .setToSourceEnabled(enableToSource
)
4138 .setPropertyErrorMessageFixEnabled(enablePropertyErrorMessageFix
)
4139 .setIteratorHelpersEnabled(enableIteratorHelpers
)
4140 .setShadowRealmsEnabled(enableShadowRealms
)
4141 .setWellFormedUnicodeStringsEnabled(enableWellFormedUnicodeStrings
)
4142 .setArrayGroupingEnabled(enableArrayGrouping
)
4143 .setArrayBufferTransferEnabled(enableArrayBufferTransfer
)
4144 #ifdef NIGHTLY_BUILD
4145 .setNewSetMethodsEnabled(enableNewSetMethods
)
4146 .setSymbolsAsWeakMapKeysEnabled(enableSymbolsAsWeakMapKeys
)
4147 .setArrayBufferResizableEnabled(enableArrayBufferResizable
)
4148 .setSharedArrayBufferGrowableEnabled(enableArrayBufferResizable
)
4153 [[nodiscard
]] static bool CheckRealmOptions(JSContext
* cx
,
4154 JS::RealmOptions
& options
,
4155 JSPrincipals
* principals
) {
4156 JS::RealmCreationOptions
& creationOptions
= options
.creationOptions();
4157 if (creationOptions
.compartmentSpecifier() !=
4158 JS::CompartmentSpecifier::ExistingCompartment
) {
4162 JS::Compartment
* comp
= creationOptions
.compartment();
4164 // All realms in a compartment must be either system or non-system.
4166 principals
&& principals
== cx
->runtime()->trustedPrincipals();
4167 if (isSystem
!= IsSystemCompartment(comp
)) {
4168 JS_ReportErrorASCII(cx
,
4169 "Cannot create system and non-system realms in the "
4170 "same compartment");
4174 // Debugger visibility is per-compartment, not per-realm, so make sure the
4175 // requested visibility matches the existing compartment's.
4176 if (creationOptions
.invisibleToDebugger() != comp
->invisibleToDebugger()) {
4177 JS_ReportErrorASCII(cx
,
4178 "All the realms in a compartment must have "
4179 "the same debugger visibility");
4186 static JSObject
* NewSandbox(JSContext
* cx
, bool lazy
) {
4187 JS::RealmOptions options
;
4188 SetStandardRealmOptions(options
);
4190 if (defaultToSameCompartment
) {
4191 options
.creationOptions().setExistingCompartment(cx
->global());
4193 options
.creationOptions().setNewCompartmentAndZone();
4196 JSPrincipals
* principals
= nullptr;
4197 if (!CheckRealmOptions(cx
, options
, principals
)) {
4201 RootedObject
obj(cx
,
4202 JS_NewGlobalObject(cx
, &sandbox_class
, principals
,
4203 JS::DontFireOnNewGlobalHook
, options
));
4209 JSAutoRealm
ar(cx
, obj
);
4210 if (!lazy
&& !JS::InitRealmStandardClasses(cx
)) {
4214 RootedValue
value(cx
, BooleanValue(lazy
));
4215 if (!JS_DefineProperty(cx
, obj
, "lazy", value
,
4216 JSPROP_PERMANENT
| JSPROP_READONLY
)) {
4220 JS_FireOnNewGlobalObject(cx
, obj
);
4223 if (!cx
->compartment()->wrap(cx
, &obj
)) {
4229 static bool EvalInContext(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4230 CallArgs args
= CallArgsFromVp(argc
, vp
);
4231 if (!args
.requireAtLeast(cx
, "evalcx", 1)) {
4235 RootedString
str(cx
, ToString(cx
, args
[0]));
4240 RootedObject
sobj(cx
);
4241 if (args
.hasDefined(1)) {
4242 sobj
= ToObject(cx
, args
[1]);
4248 AutoStableStringChars
strChars(cx
);
4249 if (!strChars
.initTwoByte(cx
, str
)) {
4253 mozilla::Range
<const char16_t
> chars
= strChars
.twoByteRange();
4254 size_t srclen
= chars
.length();
4255 const char16_t
* src
= chars
.begin().get();
4259 if (src
[0] == 'l' && src
[1] == 'a' && src
[2] == 'z' && src
[3] == 'y') {
4266 sobj
= NewSandbox(cx
, lazy
);
4273 args
.rval().setObject(*sobj
);
4277 JS::AutoFilename filename
;
4280 DescribeScriptedCaller(cx
, &filename
, &lineno
);
4282 sobj
= UncheckedUnwrap(sobj
, true);
4284 JSAutoRealm
ar(cx
, sobj
);
4286 sobj
= ToWindowIfWindowProxy(sobj
);
4288 if (!JS_IsGlobalObject(sobj
)) {
4289 JS_ReportErrorASCII(cx
, "Invalid scope argument to evalcx");
4293 JS::CompileOptions
opts(cx
);
4294 opts
.setFileAndLine(filename
.get(), lineno
)
4295 .setEagerDelazificationStrategy(defaultDelazificationMode
);
4297 JS::SourceText
<char16_t
> srcBuf
;
4298 if (!srcBuf
.init(cx
, src
, srclen
, JS::SourceOwnership::Borrowed
) ||
4299 !JS::Evaluate(cx
, opts
, srcBuf
, args
.rval())) {
4304 if (!cx
->compartment()->wrap(cx
, args
.rval())) {
4311 static bool EnsureGeckoProfilingStackInstalled(JSContext
* cx
,
4313 if (cx
->geckoProfiler().infraInstalled()) {
4314 MOZ_ASSERT(sc
->geckoProfilingStack
);
4318 MOZ_ASSERT(!sc
->geckoProfilingStack
);
4319 sc
->geckoProfilingStack
= MakeUnique
<ProfilingStack
>();
4320 if (!sc
->geckoProfilingStack
) {
4321 JS_ReportOutOfMemory(cx
);
4325 SetContextProfilingStack(cx
, sc
->geckoProfilingStack
.get());
4329 struct WorkerInput
{
4330 JSRuntime
* parentRuntime
;
4331 UniqueTwoByteChars chars
;
4334 WorkerInput(JSRuntime
* parentRuntime
, UniqueTwoByteChars chars
, size_t length
)
4335 : parentRuntime(parentRuntime
), chars(std::move(chars
)), length(length
) {}
4338 static void DestroyShellCompartmentPrivate(JS::GCContext
* gcx
,
4339 JS::Compartment
* compartment
) {
4340 auto priv
= static_cast<ShellCompartmentPrivate
*>(
4341 JS_GetCompartmentPrivate(compartment
));
4345 static void SetWorkerContextOptions(JSContext
* cx
);
4346 static bool ShellBuildId(JS::BuildIdCharVector
* buildId
);
4348 static constexpr size_t gWorkerStackSize
= 2 * 128 * sizeof(size_t) * 1024;
4350 static void WorkerMain(UniquePtr
<WorkerInput
> input
) {
4351 MOZ_ASSERT(input
->parentRuntime
);
4353 JSContext
* cx
= JS_NewContext(8L * 1024L * 1024L, input
->parentRuntime
);
4357 auto destroyContext
= MakeScopeExit([cx
] { JS_DestroyContext(cx
); });
4359 UniquePtr
<ShellContext
> sc
=
4360 MakeUnique
<ShellContext
>(cx
, ShellContext::Worker
);
4361 if (!sc
|| !sc
->registerWithCx(cx
)) {
4365 if (!JS::InitSelfHostedCode(cx
)) {
4369 EnvironmentPreparer
environmentPreparer(cx
);
4372 JS::RealmOptions realmOptions
;
4373 SetStandardRealmOptions(realmOptions
);
4375 RootedObject
global(cx
, NewGlobalObject(cx
, realmOptions
, nullptr,
4376 ShellGlobalKind::WindowProxy
,
4377 /* immutablePrototype = */ true));
4382 JSAutoRealm
ar(cx
, global
);
4384 JS::ConstUTF8CharsZ
path(processWideModuleLoadPath
.get(),
4385 strlen(processWideModuleLoadPath
.get()));
4386 RootedString
moduleLoadPath(cx
, JS_NewStringCopyUTF8Z(cx
, path
));
4387 if (!moduleLoadPath
) {
4390 sc
->moduleLoader
= js::MakeUnique
<ModuleLoader
>();
4391 if (!sc
->moduleLoader
|| !sc
->moduleLoader
->init(cx
, moduleLoadPath
)) {
4395 JS::CompileOptions
options(cx
);
4396 options
.setFileAndLine("<string>", 1)
4398 .setEagerDelazificationStrategy(defaultDelazificationMode
);
4400 AutoReportException
are(cx
);
4401 JS::SourceText
<char16_t
> srcBuf
;
4402 if (!srcBuf
.init(cx
, input
->chars
.get(), input
->length
,
4403 JS::SourceOwnership::Borrowed
)) {
4407 RootedScript
script(cx
, JS::Compile(cx
, options
, srcBuf
));
4411 RootedValue
result(cx
);
4412 JS_ExecuteScript(cx
, script
, &result
);
4418 // Workers can spawn other workers, so we need a lock to access workerThreads.
4419 static Mutex
* workerThreadsLock
= nullptr;
4420 static Vector
<UniquePtr
<js::Thread
>, 0, SystemAllocPolicy
> workerThreads
;
4422 class MOZ_RAII AutoLockWorkerThreads
: public LockGuard
<Mutex
> {
4423 using Base
= LockGuard
<Mutex
>;
4426 AutoLockWorkerThreads() : Base(*workerThreadsLock
) {
4427 MOZ_ASSERT(workerThreadsLock
);
4431 static bool EvalInWorker(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4432 if (!CanUseExtraThreads()) {
4433 JS_ReportErrorASCII(cx
, "Can't create threads with --no-threads");
4437 CallArgs args
= CallArgsFromVp(argc
, vp
);
4438 if (!args
.get(0).isString()) {
4439 JS_ReportErrorASCII(cx
, "Invalid arguments");
4443 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
4444 if (cx
->runningOOMTest
) {
4445 JS_ReportErrorASCII(
4446 cx
, "Can't create threads while running simulated OOM test");
4451 if (!args
[0].toString()->ensureLinear(cx
)) {
4455 if (!workerThreadsLock
) {
4456 workerThreadsLock
= js_new
<Mutex
>(mutexid::ShellWorkerThreads
);
4457 if (!workerThreadsLock
) {
4458 ReportOutOfMemory(cx
);
4463 JSLinearString
* str
= &args
[0].toString()->asLinear();
4465 UniqueTwoByteChars
chars(js_pod_malloc
<char16_t
>(str
->length()));
4467 ReportOutOfMemory(cx
);
4471 CopyChars(chars
.get(), *str
);
4473 auto input
= js::MakeUnique
<WorkerInput
>(JS_GetParentRuntime(cx
),
4474 std::move(chars
), str
->length());
4476 ReportOutOfMemory(cx
);
4480 UniquePtr
<Thread
> thread
;
4482 AutoEnterOOMUnsafeRegion oomUnsafe
;
4483 thread
= js::MakeUnique
<Thread
>(
4484 Thread::Options().setStackSize(gWorkerStackSize
+ 512 * 1024));
4485 if (!thread
|| !thread
->init(WorkerMain
, std::move(input
))) {
4486 oomUnsafe
.crash("EvalInWorker");
4490 AutoLockWorkerThreads alwt
;
4491 if (!workerThreads
.append(std::move(thread
))) {
4492 ReportOutOfMemory(cx
);
4497 args
.rval().setUndefined();
4501 static bool ShapeOf(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
4502 CallArgs args
= CallArgsFromVp(argc
, vp
);
4503 if (!args
.get(0).isObject()) {
4504 JS_ReportErrorASCII(cx
, "shapeOf: object expected");
4507 JSObject
* obj
= &args
[0].toObject();
4508 args
.rval().set(JS_NumberValue(double(uintptr_t(obj
->shape()) >> 3)));
4512 static bool Sleep_fn(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4513 ShellContext
* sc
= GetShellContext(cx
);
4514 CallArgs args
= CallArgsFromVp(argc
, vp
);
4516 TimeDuration duration
= TimeDuration::FromSeconds(0.0);
4517 if (args
.length() > 0) {
4519 if (!ToNumber(cx
, args
[0], &t_secs
)) {
4522 if (std::isnan(t_secs
)) {
4523 JS_ReportErrorASCII(cx
, "sleep interval is not a number");
4527 duration
= TimeDuration::FromSeconds(std::max(0.0, t_secs
));
4528 const TimeDuration MAX_TIMEOUT_INTERVAL
=
4529 TimeDuration::FromSeconds(MAX_TIMEOUT_SECONDS
);
4530 if (duration
> MAX_TIMEOUT_INTERVAL
) {
4531 JS_ReportErrorASCII(cx
, "Excessive sleep interval");
4536 LockGuard
<Mutex
> guard(sc
->watchdogLock
);
4537 TimeStamp toWakeup
= TimeStamp::Now() + duration
;
4539 sc
->sleepWakeup
.wait_for(guard
, duration
);
4540 if (sc
->serviceInterrupt
) {
4543 auto now
= TimeStamp::Now();
4544 if (now
>= toWakeup
) {
4547 duration
= toWakeup
- now
;
4550 args
.rval().setUndefined();
4551 return !sc
->serviceInterrupt
;
4554 static void KillWatchdog(JSContext
* cx
) {
4555 ShellContext
* sc
= GetShellContext(cx
);
4556 Maybe
<Thread
> thread
;
4559 LockGuard
<Mutex
> guard(sc
->watchdogLock
);
4560 std::swap(sc
->watchdogThread
, thread
);
4562 // The watchdog thread becoming Nothing is its signal to exit.
4563 sc
->watchdogWakeup
.notify_one();
4570 MOZ_ASSERT(!sc
->watchdogThread
);
4573 static void WatchdogMain(JSContext
* cx
) {
4574 ThisThread::SetName("JS Watchdog");
4576 ShellContext
* sc
= GetShellContext(cx
);
4579 LockGuard
<Mutex
> guard(sc
->watchdogLock
);
4580 while (sc
->watchdogThread
) {
4581 auto now
= TimeStamp::Now();
4582 if (sc
->watchdogTimeout
&& now
>= sc
->watchdogTimeout
.value()) {
4584 * The timeout has just expired. Request an interrupt callback
4587 sc
->watchdogTimeout
= Nothing();
4589 UnlockGuard
<Mutex
> unlock(guard
);
4590 CancelExecution(cx
);
4593 /* Wake up any threads doing sleep. */
4594 sc
->sleepWakeup
.notify_all();
4596 if (sc
->watchdogTimeout
) {
4598 * Time hasn't expired yet. Simulate an interrupt callback
4599 * which doesn't abort execution.
4601 JS_RequestInterruptCallback(cx
);
4604 TimeDuration sleepDuration
= sc
->watchdogTimeout
4605 ? TimeDuration::FromSeconds(0.1)
4606 : TimeDuration::Forever();
4607 sc
->watchdogWakeup
.wait_for(guard
, sleepDuration
);
4613 static bool ScheduleWatchdog(JSContext
* cx
, double t
) {
4614 ShellContext
* sc
= GetShellContext(cx
);
4617 LockGuard
<Mutex
> guard(sc
->watchdogLock
);
4618 sc
->watchdogTimeout
= Nothing();
4626 auto interval
= TimeDuration::FromSeconds(t
);
4627 auto timeout
= TimeStamp::Now() + interval
;
4628 LockGuard
<Mutex
> guard(sc
->watchdogLock
);
4629 if (!sc
->watchdogThread
) {
4630 MOZ_ASSERT(!sc
->watchdogTimeout
);
4631 sc
->watchdogThread
.emplace();
4632 AutoEnterOOMUnsafeRegion oomUnsafe
;
4633 if (!sc
->watchdogThread
->init(WatchdogMain
, cx
)) {
4634 oomUnsafe
.crash("watchdogThread.init");
4636 } else if (!sc
->watchdogTimeout
|| timeout
< sc
->watchdogTimeout
.value()) {
4637 sc
->watchdogWakeup
.notify_one();
4639 sc
->watchdogTimeout
= Some(timeout
);
4643 static void KillWorkerThreads(JSContext
* cx
) {
4644 MOZ_ASSERT_IF(!CanUseExtraThreads(), workerThreads
.empty());
4646 if (!workerThreadsLock
) {
4647 MOZ_ASSERT(workerThreads
.empty());
4652 // We need to leave the AutoLockWorkerThreads scope before we call
4653 // js::Thread::join, to avoid deadlocks when AutoLockWorkerThreads is
4654 // used by the worker thread.
4655 UniquePtr
<Thread
> thread
;
4657 AutoLockWorkerThreads alwt
;
4658 if (workerThreads
.empty()) {
4661 thread
= std::move(workerThreads
.back());
4662 workerThreads
.popBack();
4667 workerThreads
.clearAndFree();
4669 js_delete(workerThreadsLock
);
4670 workerThreadsLock
= nullptr;
4673 static void CancelExecution(JSContext
* cx
) {
4674 ShellContext
* sc
= GetShellContext(cx
);
4675 sc
->serviceInterrupt
= true;
4676 JS_RequestInterruptCallback(cx
);
4679 static bool SetTimeoutValue(JSContext
* cx
, double t
) {
4680 if (std::isnan(t
)) {
4681 JS_ReportErrorASCII(cx
, "timeout is not a number");
4684 const TimeDuration MAX_TIMEOUT_INTERVAL
=
4685 TimeDuration::FromSeconds(MAX_TIMEOUT_SECONDS
);
4686 if (TimeDuration::FromSeconds(t
) > MAX_TIMEOUT_INTERVAL
) {
4687 JS_ReportErrorASCII(cx
, "Excessive timeout value");
4690 GetShellContext(cx
)->timeoutInterval
= t
;
4691 if (!ScheduleWatchdog(cx
, t
)) {
4692 JS_ReportErrorASCII(cx
, "Failed to create the watchdog");
4698 static bool Timeout(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4699 ShellContext
* sc
= GetShellContext(cx
);
4700 CallArgs args
= CallArgsFromVp(argc
, vp
);
4702 if (args
.length() == 0) {
4703 args
.rval().setNumber(sc
->timeoutInterval
);
4707 if (args
.length() > 2) {
4708 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
4713 if (!ToNumber(cx
, args
[0], &t
)) {
4717 if (args
.length() > 1) {
4718 RootedValue
value(cx
, args
[1]);
4719 if (!value
.isObject() || !value
.toObject().is
<JSFunction
>()) {
4720 JS_ReportErrorASCII(cx
, "Second argument must be a timeout function");
4723 sc
->interruptFunc
= value
;
4724 sc
->haveInterruptFunc
= true;
4727 args
.rval().setUndefined();
4728 return SetTimeoutValue(cx
, t
);
4731 static bool InterruptIf(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4732 CallArgs args
= CallArgsFromVp(argc
, vp
);
4734 if (args
.length() != 1) {
4735 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
4739 if (ToBoolean(args
[0])) {
4740 GetShellContext(cx
)->serviceInterrupt
= true;
4741 JS_RequestInterruptCallback(cx
);
4744 args
.rval().setUndefined();
4748 static bool InvokeInterruptCallbackWrapper(JSContext
* cx
, unsigned argc
,
4750 CallArgs args
= CallArgsFromVp(argc
, vp
);
4751 if (args
.length() != 1) {
4752 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
4756 GetShellContext(cx
)->serviceInterrupt
= true;
4757 JS_RequestInterruptCallback(cx
);
4758 bool interruptRv
= CheckForInterrupt(cx
);
4760 // The interrupt handler could have set a pending exception. Since we call
4761 // back into JS, don't have it see the pending exception. If we have an
4762 // uncatchable exception that's not propagating a debug mode forced
4764 if (!interruptRv
&& !cx
->isExceptionPending() &&
4765 !cx
->isPropagatingForcedReturn()) {
4769 JS::AutoSaveExceptionState
savedExc(cx
);
4771 FixedInvokeArgs
<1> iargs(cx
);
4773 iargs
[0].setBoolean(interruptRv
);
4776 if (!js::Call(cx
, args
[0], UndefinedHandleValue
, iargs
, &rv
)) {
4780 args
.rval().setUndefined();
4784 static bool SetInterruptCallback(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4785 CallArgs args
= CallArgsFromVp(argc
, vp
);
4787 if (args
.length() != 1) {
4788 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
4792 RootedValue
value(cx
, args
[0]);
4793 if (!value
.isObject() || !value
.toObject().is
<JSFunction
>()) {
4794 JS_ReportErrorASCII(cx
, "Argument must be a function");
4797 GetShellContext(cx
)->interruptFunc
= value
;
4798 GetShellContext(cx
)->haveInterruptFunc
= true;
4800 args
.rval().setUndefined();
4805 // var s0 = "A".repeat(10*1024);
4806 // interruptRegexp(/a(bc|bd)/, s0);
4807 // first arg is regexp
4808 // second arg is string
4809 static bool InterruptRegexp(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4810 CallArgs args
= CallArgsFromVp(argc
, vp
);
4811 ShellContext
* sc
= GetShellContext(cx
);
4812 RootedObject
callee(cx
, &args
.callee());
4814 if (args
.length() != 2) {
4815 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments.");
4818 if (!(args
[0].isObject() && args
[0].toObject().is
<RegExpObject
>())) {
4819 ReportUsageErrorASCII(cx
, callee
,
4820 "First argument must be a regular expression.");
4823 if (!args
[1].isString()) {
4824 ReportUsageErrorASCII(cx
, callee
, "Second argument must be a String.");
4827 // Set interrupt flags
4828 sc
->serviceInterrupt
= true;
4829 js::irregexp::IsolateSetShouldSimulateInterrupt(cx
->isolate
);
4831 RootedObject
regexp(cx
, &args
[0].toObject());
4832 RootedString
string(cx
, args
[1].toString());
4833 int32_t lastIndex
= 0;
4835 return js::RegExpMatcherRaw(cx
, regexp
, string
, lastIndex
, nullptr,
4840 static bool CheckRegExpSyntax(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4841 CallArgs args
= CallArgsFromVp(argc
, vp
);
4842 RootedObject
callee(cx
, &args
.callee());
4844 if (args
.length() != 1) {
4845 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments.");
4848 if (!args
[0].isString()) {
4849 ReportUsageErrorASCII(cx
, callee
, "First argument must be a string.");
4853 RootedString
string(cx
, args
[0].toString());
4854 AutoStableStringChars
stableChars(cx
);
4855 if (!stableChars
.initTwoByte(cx
, string
)) {
4859 const char16_t
* chars
= stableChars
.twoByteRange().begin().get();
4860 size_t length
= string
->length();
4862 Rooted
<JS::Value
> error(cx
);
4863 if (!JS::CheckRegExpSyntax(cx
, chars
, length
, JS::RegExpFlag::NoFlags
,
4868 args
.rval().set(error
);
4872 static bool SetJitCompilerOption(JSContext
* cx
, unsigned argc
, Value
* vp
) {
4873 CallArgs args
= CallArgsFromVp(argc
, vp
);
4874 RootedObject
callee(cx
, &args
.callee());
4876 if (args
.length() != 2) {
4877 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments.");
4881 if (!args
[0].isString()) {
4882 ReportUsageErrorASCII(cx
, callee
, "First argument must be a String.");
4886 if (!args
[1].isInt32()) {
4887 ReportUsageErrorASCII(cx
, callee
, "Second argument must be an Int32.");
4891 // Disallow setting JIT options when there are worker threads, to avoid
4893 if (workerThreadsLock
) {
4894 ReportUsageErrorASCII(
4895 cx
, callee
, "Can't set JIT options when there are worker threads.");
4899 JSLinearString
* strArg
= JS_EnsureLinearString(cx
, args
[0].toString());
4904 #define JIT_COMPILER_MATCH(key, string) \
4905 else if (JS_LinearStringEqualsLiteral(strArg, string)) opt = \
4906 JSJITCOMPILER_##key;
4908 JSJitCompilerOption opt
= JSJITCOMPILER_NOT_AN_OPTION
;
4911 JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH
);
4912 #undef JIT_COMPILER_MATCH
4914 if (opt
== JSJITCOMPILER_NOT_AN_OPTION
) {
4915 ReportUsageErrorASCII(
4917 "First argument does not name a valid option (see jsapi.h).");
4921 int32_t number
= args
[1].toInt32();
4926 // Disallow enabling or disabling the Baseline Interpreter at runtime.
4927 // Enabling is a problem because the Baseline Interpreter code is only
4928 // present if the interpreter was enabled when the JitRuntime was created.
4929 // To support disabling we would have to discard all JitScripts. Furthermore,
4930 // we really want JitOptions to be immutable after startup so it's better to
4932 if (opt
== JSJITCOMPILER_BASELINE_INTERPRETER_ENABLE
&&
4933 bool(number
) != jit::IsBaselineInterpreterEnabled()) {
4934 JS_ReportErrorASCII(cx
,
4935 "Enabling or disabling the Baseline Interpreter at "
4936 "runtime is not supported.");
4940 // Throw if disabling the JITs and there's JIT code on the stack, to avoid
4941 // assertion failures.
4942 if ((opt
== JSJITCOMPILER_BASELINE_ENABLE
||
4943 opt
== JSJITCOMPILER_ION_ENABLE
) &&
4945 js::jit::JitActivationIterator
iter(cx
);
4947 JS_ReportErrorASCII(cx
,
4948 "Can't turn off JITs with JIT code on the stack.");
4953 // Changing code memory protection settings at runtime is not supported. Don't
4954 // throw if not changing the setting because some jit-tests depend on that.
4955 if (opt
== JSJITCOMPILER_WRITE_PROTECT_CODE
) {
4956 uint32_t writeProtect
;
4957 MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption(
4958 cx
, JSJITCOMPILER_WRITE_PROTECT_CODE
, &writeProtect
));
4959 if (bool(number
) != writeProtect
) {
4960 JS_ReportErrorASCII(cx
, "Can't change code write protection at runtime");
4966 // Throw if trying to disable all the Wasm compilers. The logic here is that
4967 // if we're trying to disable a compiler that is currently enabled and that is
4968 // the last compiler enabled then we must throw.
4970 // Note that this check does not prevent an error from being thrown later.
4971 // Actual compiler availability is dynamic and depends on other conditions,
4972 // such as other options set and whether a debugger is present.
4973 if ((opt
== JSJITCOMPILER_WASM_JIT_BASELINE
||
4974 opt
== JSJITCOMPILER_WASM_JIT_OPTIMIZING
) &&
4976 uint32_t baseline
, optimizing
;
4977 MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption(
4978 cx
, JSJITCOMPILER_WASM_JIT_BASELINE
, &baseline
));
4979 MOZ_ALWAYS_TRUE(JS_GetGlobalJitCompilerOption(
4980 cx
, JSJITCOMPILER_WASM_JIT_OPTIMIZING
, &optimizing
));
4981 if (baseline
+ optimizing
== 1) {
4982 if ((opt
== JSJITCOMPILER_WASM_JIT_BASELINE
&& baseline
) ||
4983 (opt
== JSJITCOMPILER_WASM_JIT_OPTIMIZING
&& optimizing
)) {
4984 JS_ReportErrorASCII(
4986 "Disabling all the Wasm compilers at runtime is not supported.");
4992 // JIT compiler options are process-wide, so we have to stop off-thread
4993 // compilations for all runtimes to avoid races.
4994 WaitForAllHelperThreads();
4996 // Only release JIT code for the current runtime because there's no good
4997 // way to discard code for other runtimes.
4998 ReleaseAllJITCode(cx
->gcContext());
5000 JS_SetGlobalJitCompilerOption(cx
, opt
, uint32_t(number
));
5002 args
.rval().setUndefined();
5006 static bool EnableLastWarning(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5007 ShellContext
* sc
= GetShellContext(cx
);
5008 CallArgs args
= CallArgsFromVp(argc
, vp
);
5010 sc
->lastWarningEnabled
= true;
5011 sc
->lastWarning
.setNull();
5013 args
.rval().setUndefined();
5017 static bool DisableLastWarning(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5018 ShellContext
* sc
= GetShellContext(cx
);
5019 CallArgs args
= CallArgsFromVp(argc
, vp
);
5021 sc
->lastWarningEnabled
= false;
5022 sc
->lastWarning
.setNull();
5024 args
.rval().setUndefined();
5028 static bool GetLastWarning(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5029 ShellContext
* sc
= GetShellContext(cx
);
5030 CallArgs args
= CallArgsFromVp(argc
, vp
);
5032 if (!sc
->lastWarningEnabled
) {
5033 JS_ReportErrorASCII(cx
, "Call enableLastWarning first.");
5037 if (!JS_WrapValue(cx
, &sc
->lastWarning
)) {
5041 args
.rval().set(sc
->lastWarning
);
5045 static bool ClearLastWarning(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5046 ShellContext
* sc
= GetShellContext(cx
);
5047 CallArgs args
= CallArgsFromVp(argc
, vp
);
5049 if (!sc
->lastWarningEnabled
) {
5050 JS_ReportErrorASCII(cx
, "Call enableLastWarning first.");
5054 sc
->lastWarning
.setNull();
5056 args
.rval().setUndefined();
5060 #if defined(DEBUG) || defined(JS_JITSPEW)
5061 static bool StackDump(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5062 CallArgs args
= CallArgsFromVp(argc
, vp
);
5064 if (!gOutFile
->isOpen()) {
5065 JS_ReportErrorASCII(cx
, "output file is closed");
5069 bool showArgs
= ToBoolean(args
.get(0));
5070 bool showLocals
= ToBoolean(args
.get(1));
5071 bool showThisProps
= ToBoolean(args
.get(2));
5073 JS::UniqueChars buf
=
5074 JS::FormatStackDump(cx
, showArgs
, showLocals
, showThisProps
);
5076 fputs("Failed to format JavaScript stack for dump\n", gOutFile
->fp
);
5077 JS_ClearPendingException(cx
);
5079 fputs(buf
.get(), gOutFile
->fp
);
5082 args
.rval().setUndefined();
5087 static bool StackPointerInfo(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5088 CallArgs args
= CallArgsFromVp(argc
, vp
);
5090 // Copy the truncated stack pointer to the result. This value is not used
5091 // as a pointer but as a way to measure frame-size from JS.
5092 args
.rval().setInt32(int32_t(reinterpret_cast<size_t>(&args
) & 0xfffffff));
5096 static bool Elapsed(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5097 CallArgs args
= CallArgsFromVp(argc
, vp
);
5098 if (args
.length() == 0) {
5099 double d
= PRMJ_Now() - GetShellContext(cx
)->startTime
;
5100 args
.rval().setDouble(d
);
5103 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
5107 static ShellCompartmentPrivate
* EnsureShellCompartmentPrivate(JSContext
* cx
) {
5108 Compartment
* comp
= cx
->compartment();
5110 static_cast<ShellCompartmentPrivate
*>(JS_GetCompartmentPrivate(comp
));
5112 priv
= cx
->new_
<ShellCompartmentPrivate
>();
5113 JS_SetCompartmentPrivate(cx
->compartment(), priv
);
5118 static bool ParseModule(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5119 CallArgs args
= CallArgsFromVp(argc
, vp
);
5120 if (!args
.requireAtLeast(cx
, "parseModule", 1)) {
5124 if (!args
[0].isString()) {
5125 const char* typeName
= InformalValueTypeName(args
[0]);
5126 JS_ReportErrorASCII(cx
, "expected string to compile, got %s", typeName
);
5130 JSString
* scriptContents
= args
[0].toString();
5132 UniqueChars filename
;
5133 CompileOptions
options(cx
);
5134 if (args
.length() > 1) {
5135 if (!args
[1].isString()) {
5136 const char* typeName
= InformalValueTypeName(args
[1]);
5137 JS_ReportErrorASCII(cx
, "expected filename string, got %s", typeName
);
5141 RootedString
str(cx
, args
[1].toString());
5142 filename
= JS_EncodeStringToUTF8(cx
, str
);
5147 options
.setFileAndLine(filename
.get(), 1);
5149 options
.setFileAndLine("<string>", 1);
5151 options
.setModule();
5153 AutoStableStringChars
linearChars(cx
);
5154 if (!linearChars
.initTwoByte(cx
, scriptContents
)) {
5158 JS::SourceText
<char16_t
> srcBuf
;
5159 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
5163 AutoReportFrontendContext
fc(cx
);
5164 RootedObject
module(cx
, frontend::CompileModule(cx
, &fc
, options
, srcBuf
));
5169 Rooted
<ShellModuleObjectWrapper
*> wrapper(
5170 cx
, ShellModuleObjectWrapper::create(cx
, module
.as
<ModuleObject
>()));
5174 args
.rval().setObject(*wrapper
);
5178 // A JSObject that holds XDRBuffer.
5179 class XDRBufferObject
: public NativeObject
{
5180 static const size_t VECTOR_SLOT
= 0;
5181 static const unsigned RESERVED_SLOTS
= 1;
5184 static const JSClassOps classOps_
;
5185 static const JSClass class_
;
5187 [[nodiscard
]] inline static XDRBufferObject
* create(
5188 JSContext
* cx
, JS::TranscodeBuffer
&& buf
);
5190 JS::TranscodeBuffer
* data() const {
5191 Value value
= getReservedSlot(VECTOR_SLOT
);
5192 auto buf
= static_cast<JS::TranscodeBuffer
*>(value
.toPrivate());
5197 bool hasData() const {
5198 // Data may not be present if we hit OOM in initialization.
5199 return !getReservedSlot(VECTOR_SLOT
).isUndefined();
5202 static void finalize(JS::GCContext
* gcx
, JSObject
* obj
);
5205 /*static */ const JSClassOps
XDRBufferObject::classOps_
= {
5206 nullptr, // addProperty
5207 nullptr, // delProperty
5208 nullptr, // enumerate
5209 nullptr, // newEnumerate
5211 nullptr, // mayResolve
5212 XDRBufferObject::finalize
, // finalize
5214 nullptr, // construct
5218 /*static */ const JSClass
XDRBufferObject::class_
= {
5220 JSCLASS_HAS_RESERVED_SLOTS(XDRBufferObject::RESERVED_SLOTS
) |
5221 JSCLASS_BACKGROUND_FINALIZE
,
5222 &XDRBufferObject::classOps_
};
5224 XDRBufferObject
* XDRBufferObject::create(JSContext
* cx
,
5225 JS::TranscodeBuffer
&& buf
) {
5226 XDRBufferObject
* bufObj
=
5227 NewObjectWithGivenProto
<XDRBufferObject
>(cx
, nullptr);
5232 auto heapBuf
= cx
->make_unique
<JS::TranscodeBuffer
>(std::move(buf
));
5237 size_t len
= heapBuf
->length();
5238 InitReservedSlot(bufObj
, VECTOR_SLOT
, heapBuf
.release(), len
,
5239 MemoryUse::XDRBufferElements
);
5244 void XDRBufferObject::finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
5245 XDRBufferObject
* buf
= &obj
->as
<XDRBufferObject
>();
5246 if (buf
->hasData()) {
5247 gcx
->delete_(buf
, buf
->data(), buf
->data()->length(),
5248 MemoryUse::XDRBufferElements
);
5252 static bool InstantiateModuleStencil(JSContext
* cx
, uint32_t argc
, Value
* vp
) {
5253 CallArgs args
= CallArgsFromVp(argc
, vp
);
5255 if (!args
.requireAtLeast(cx
, "instantiateModuleStencil", 1)) {
5259 /* Prepare the input byte array. */
5260 if (!args
[0].isObject()) {
5261 JS_ReportErrorASCII(cx
,
5262 "instantiateModuleStencil: Stencil object expected");
5265 Rooted
<js::StencilObject
*> stencilObj(
5266 cx
, args
[0].toObject().maybeUnwrapIf
<js::StencilObject
>());
5268 JS_ReportErrorASCII(cx
,
5269 "instantiateModuleStencil: Stencil object expected");
5273 if (!stencilObj
->stencil()->isModule()) {
5274 JS_ReportErrorASCII(cx
,
5275 "instantiateModuleStencil: Module stencil expected");
5279 CompileOptions
options(cx
);
5280 UniqueChars fileNameBytes
;
5281 if (args
.length() == 2) {
5282 if (!args
[1].isObject()) {
5283 JS_ReportErrorASCII(
5284 cx
, "instantiateModuleStencil: The 2nd argument must be an object");
5288 RootedObject
opts(cx
, &args
[1].toObject());
5289 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
5294 /* Prepare the CompilationStencil for decoding. */
5295 AutoReportFrontendContext
fc(cx
);
5296 Rooted
<frontend::CompilationInput
> input(cx
,
5297 frontend::CompilationInput(options
));
5298 if (!input
.get().initForModule(&fc
)) {
5302 if (!js::ValidateLazinessOfStencilAndGlobal(cx
, *stencilObj
->stencil())) {
5306 /* Instantiate the stencil. */
5307 Rooted
<frontend::CompilationGCOutput
> output(cx
);
5308 if (!frontend::CompilationStencil::instantiateStencils(
5309 cx
, input
.get(), *stencilObj
->stencil(), output
.get())) {
5313 Rooted
<ModuleObject
*> modObject(cx
, output
.get().module
);
5314 Rooted
<ShellModuleObjectWrapper
*> wrapper(
5315 cx
, ShellModuleObjectWrapper::create(cx
, modObject
));
5319 args
.rval().setObject(*wrapper
);
5323 static bool InstantiateModuleStencilXDR(JSContext
* cx
, uint32_t argc
,
5325 CallArgs args
= CallArgsFromVp(argc
, vp
);
5327 if (!args
.requireAtLeast(cx
, "instantiateModuleStencilXDR", 1)) {
5331 /* Prepare the input byte array. */
5332 if (!args
[0].isObject()) {
5333 JS_ReportErrorASCII(
5334 cx
, "instantiateModuleStencilXDR: Stencil XDR object expected");
5337 Rooted
<StencilXDRBufferObject
*> xdrObj(
5338 cx
, args
[0].toObject().maybeUnwrapIf
<StencilXDRBufferObject
>());
5340 JS_ReportErrorASCII(
5341 cx
, "instantiateModuleStencilXDR: Stencil XDR object expected");
5344 MOZ_ASSERT(xdrObj
->hasBuffer());
5346 CompileOptions
options(cx
);
5347 UniqueChars fileNameBytes
;
5348 if (args
.length() == 2) {
5349 if (!args
[1].isObject()) {
5350 JS_ReportErrorASCII(
5352 "instantiateModuleStencilXDR: The 2nd argument must be an object");
5356 RootedObject
opts(cx
, &args
[1].toObject());
5357 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
5362 /* Prepare the CompilationStencil for decoding. */
5363 AutoReportFrontendContext
fc(cx
);
5364 Rooted
<frontend::CompilationInput
> input(cx
,
5365 frontend::CompilationInput(options
));
5366 if (!input
.get().initForModule(&fc
)) {
5369 frontend::CompilationStencil
stencil(nullptr);
5371 /* Deserialize the stencil from XDR. */
5372 JS::TranscodeRange
xdrRange(xdrObj
->buffer(), xdrObj
->bufferLength());
5373 bool succeeded
= false;
5374 if (!stencil
.deserializeStencils(&fc
, options
, xdrRange
, &succeeded
)) {
5378 fc
.clearAutoReport();
5379 JS_ReportErrorASCII(cx
, "Decoding failure");
5383 if (!stencil
.isModule()) {
5384 fc
.clearAutoReport();
5385 JS_ReportErrorASCII(cx
,
5386 "instantiateModuleStencilXDR: Module stencil expected");
5390 if (!js::ValidateLazinessOfStencilAndGlobal(cx
, stencil
)) {
5394 /* Instantiate the stencil. */
5395 Rooted
<frontend::CompilationGCOutput
> output(cx
);
5396 if (!frontend::CompilationStencil::instantiateStencils(
5397 cx
, input
.get(), stencil
, output
.get())) {
5401 Rooted
<ModuleObject
*> modObject(cx
, output
.get().module
);
5402 Rooted
<ShellModuleObjectWrapper
*> wrapper(
5403 cx
, ShellModuleObjectWrapper::create(cx
, modObject
));
5407 args
.rval().setObject(*wrapper
);
5411 static bool RegisterModule(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5412 CallArgs args
= CallArgsFromVp(argc
, vp
);
5413 if (!args
.requireAtLeast(cx
, "registerModule", 2)) {
5417 if (!args
[0].isString()) {
5418 const char* typeName
= InformalValueTypeName(args
[0]);
5419 JS_ReportErrorASCII(cx
, "expected string, got %s", typeName
);
5423 if (!args
[1].isObject() ||
5424 !args
[1].toObject().is
<ShellModuleObjectWrapper
>()) {
5425 const char* typeName
= InformalValueTypeName(args
[1]);
5426 JS_ReportErrorASCII(cx
, "expected module, got %s", typeName
);
5430 ShellContext
* sc
= GetShellContext(cx
);
5431 Rooted
<ModuleObject
*> module(
5432 cx
, args
[1].toObject().as
<ShellModuleObjectWrapper
>().get());
5434 Rooted
<JSAtom
*> specifier(cx
, AtomizeString(cx
, args
[0].toString()));
5439 RootedObject
moduleRequest(
5440 cx
, ModuleRequestObject::create(cx
, specifier
, nullptr));
5441 if (!moduleRequest
) {
5445 if (!sc
->moduleLoader
->registerTestModule(cx
, moduleRequest
, module
)) {
5449 Rooted
<ShellModuleObjectWrapper
*> wrapper(
5450 cx
, ShellModuleObjectWrapper::create(cx
, module
));
5454 args
.rval().setObject(*wrapper
);
5458 static bool ClearModules(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5459 CallArgs args
= CallArgsFromVp(argc
, vp
);
5460 ShellContext
* sc
= GetShellContext(cx
);
5461 sc
->moduleLoader
->clearModules(cx
);
5462 args
.rval().setUndefined();
5466 static bool ModuleLink(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5467 CallArgs args
= CallArgsFromVp(argc
, vp
);
5469 if (args
.length() != 1 || !args
[0].isObject()) {
5470 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_INVALID_ARGS
,
5475 RootedObject
object(cx
, UncheckedUnwrap(&args
[0].toObject()));
5476 if (!object
->is
<ShellModuleObjectWrapper
>()) {
5477 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_INVALID_ARGS
,
5482 AutoRealm
ar(cx
, object
);
5484 Rooted
<ModuleObject
*> module(cx
,
5485 object
->as
<ShellModuleObjectWrapper
>().get());
5486 if (!js::ModuleLink(cx
, module
)) {
5490 args
.rval().setUndefined();
5494 static bool ModuleEvaluate(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5495 CallArgs args
= CallArgsFromVp(argc
, vp
);
5497 if (args
.length() != 1 || !args
[0].isObject()) {
5498 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_INVALID_ARGS
,
5503 RootedObject
object(cx
, UncheckedUnwrap(&args
[0].toObject()));
5504 if (!object
->is
<ShellModuleObjectWrapper
>()) {
5505 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_INVALID_ARGS
,
5511 AutoRealm
ar(cx
, object
);
5513 Rooted
<ModuleObject
*> module(cx
,
5514 object
->as
<ShellModuleObjectWrapper
>().get());
5515 if (!js::ModuleEvaluate(cx
, module
, args
.rval())) {
5520 return JS_WrapValue(cx
, args
.rval());
5523 static ModuleEnvironmentObject
* GetModuleInitialEnvironment(
5524 JSContext
* cx
, Handle
<ModuleObject
*> module
) {
5525 // Use the initial environment so that tests can check bindings exists
5526 // before they have been instantiated.
5527 Rooted
<ModuleEnvironmentObject
*> env(cx
, &module
->initialEnvironment());
5532 static bool GetModuleEnvironmentNames(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5533 CallArgs args
= CallArgsFromVp(argc
, vp
);
5534 if (args
.length() != 1) {
5535 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
5539 if (!args
[0].isObject() ||
5540 !args
[0].toObject().is
<ShellModuleObjectWrapper
>()) {
5541 JS_ReportErrorASCII(cx
,
5542 "First argument should be a ShellModuleObjectWrapper");
5546 Rooted
<ModuleObject
*> module(
5547 cx
, args
[0].toObject().as
<ShellModuleObjectWrapper
>().get());
5548 if (module
->hadEvaluationError()) {
5549 JS_ReportErrorASCII(cx
, "Module environment unavailable");
5553 Rooted
<ModuleEnvironmentObject
*> env(cx
,
5554 GetModuleInitialEnvironment(cx
, module
));
5555 Rooted
<IdVector
> ids(cx
, IdVector(cx
));
5556 if (!JS_Enumerate(cx
, env
, &ids
)) {
5560 // The "*namespace*" binding is a detail of current implementation so hide
5561 // it to give stable results in tests.
5562 ids
.eraseIfEqual(NameToId(cx
->names().star_namespace_star_
));
5564 uint32_t length
= ids
.length();
5565 Rooted
<ArrayObject
*> array(cx
, NewDenseFullyAllocatedArray(cx
, length
));
5570 array
->setDenseInitializedLength(length
);
5571 for (uint32_t i
= 0; i
< length
; i
++) {
5572 array
->initDenseElement(i
, StringValue(ids
[i
].toString()));
5575 args
.rval().setObject(*array
);
5579 static bool GetModuleEnvironmentValue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5580 CallArgs args
= CallArgsFromVp(argc
, vp
);
5581 if (args
.length() != 2) {
5582 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
5586 if (!args
[0].isObject() ||
5587 !args
[0].toObject().is
<ShellModuleObjectWrapper
>()) {
5588 JS_ReportErrorASCII(cx
,
5589 "First argument should be a ShellModuleObjectWrapper");
5593 if (!args
[1].isString()) {
5594 JS_ReportErrorASCII(cx
, "Second argument should be a string");
5598 Rooted
<ModuleObject
*> module(
5599 cx
, args
[0].toObject().as
<ShellModuleObjectWrapper
>().get());
5600 if (module
->hadEvaluationError()) {
5601 JS_ReportErrorASCII(cx
, "Module environment unavailable");
5605 Rooted
<ModuleEnvironmentObject
*> env(cx
,
5606 GetModuleInitialEnvironment(cx
, module
));
5607 RootedString
name(cx
, args
[1].toString());
5609 if (!JS_StringToId(cx
, name
, &id
)) {
5613 if (!GetProperty(cx
, env
, env
, id
, args
.rval())) {
5617 if (args
.rval().isMagic(JS_UNINITIALIZED_LEXICAL
)) {
5618 ReportRuntimeLexicalError(cx
, JSMSG_UNINITIALIZED_LEXICAL
, id
);
5625 enum class DumpType
{
5630 template <typename Unit
>
5631 static bool DumpAST(JSContext
* cx
, const JS::ReadOnlyCompileOptions
& options
,
5632 const Unit
* units
, size_t length
,
5633 js::frontend::CompilationState
& compilationState
,
5634 js::frontend::ParseGoal goal
) {
5635 using namespace js::frontend
;
5637 AutoReportFrontendContext
fc(cx
);
5638 Parser
<FullParseHandler
, Unit
> parser(&fc
, options
, units
, length
,
5639 /* foldConstants = */ false,
5641 /* syntaxParser = */ nullptr);
5642 if (!parser
.checkOptions()) {
5646 // Emplace the top-level stencil.
5647 MOZ_ASSERT(compilationState
.scriptData
.length() ==
5648 CompilationStencil::TopLevelIndex
);
5649 if (!compilationState
.appendScriptStencilAndData(&fc
)) {
5653 js::frontend::ParseNode
* pn
;
5654 if (goal
== frontend::ParseGoal::Script
) {
5655 pn
= parser
.parse().unwrapOr(nullptr);
5657 ModuleBuilder
builder(&fc
, &parser
);
5659 SourceExtent extent
= SourceExtent::makeGlobalExtent(length
);
5660 ModuleSharedContext
modulesc(&fc
, options
, builder
, extent
);
5661 pn
= parser
.moduleBody(&modulesc
).unwrapOr(nullptr);
5669 js::Fprinter
out(stderr
);
5670 DumpParseTree(&parser
, pn
, out
);
5676 template <typename Unit
>
5677 [[nodiscard
]] static bool DumpStencil(JSContext
* cx
,
5678 const JS::ReadOnlyCompileOptions
& options
,
5679 const Unit
* units
, size_t length
,
5680 js::frontend::ParseGoal goal
) {
5681 Rooted
<frontend::CompilationInput
> input(cx
,
5682 frontend::CompilationInput(options
));
5684 JS::SourceText
<Unit
> srcBuf
;
5685 if (!srcBuf
.init(cx
, units
, length
, JS::SourceOwnership::Borrowed
)) {
5689 AutoReportFrontendContext
fc(cx
);
5690 js::frontend::NoScopeBindingCache scopeCache
;
5691 UniquePtr
<frontend::ExtensibleCompilationStencil
> stencil
;
5692 if (goal
== frontend::ParseGoal::Script
) {
5693 stencil
= frontend::CompileGlobalScriptToExtensibleStencil(
5694 cx
, &fc
, input
.get(), &scopeCache
, srcBuf
, ScopeKind::Global
);
5696 stencil
= frontend::ParseModuleToExtensibleStencil(
5697 cx
, &fc
, cx
->tempLifoAlloc(), input
.get(), &scopeCache
, srcBuf
);
5704 #if defined(DEBUG) || defined(JS_JITSPEW)
5711 static bool FrontendTest(JSContext
* cx
, unsigned argc
, Value
* vp
,
5712 const char* funcName
, DumpType dumpType
) {
5713 using namespace js::frontend
;
5715 CallArgs args
= CallArgsFromVp(argc
, vp
);
5717 if (!args
.requireAtLeast(cx
, funcName
, 1)) {
5720 if (!args
[0].isString()) {
5721 const char* typeName
= InformalValueTypeName(args
[0]);
5722 JS_ReportErrorASCII(cx
, "expected string to parse, got %s", typeName
);
5726 frontend::ParseGoal goal
= frontend::ParseGoal::Script
;
5727 #ifdef JS_ENABLE_SMOOSH
5728 bool smoosh
= false;
5731 CompileOptions
options(cx
);
5732 options
.setIntroductionType("js shell parse")
5733 .setFileAndLine("<string>", 1)
5735 .setNoScriptRval(true);
5737 if (args
.length() >= 2) {
5738 if (!args
[1].isObject()) {
5739 JS_ReportErrorASCII(cx
, "The 2nd argument must be an object");
5743 RootedObject
objOptions(cx
, &args
[1].toObject());
5745 RootedValue
optionModule(cx
);
5746 if (!JS_GetProperty(cx
, objOptions
, "module", &optionModule
)) {
5750 if (optionModule
.isBoolean()) {
5751 if (optionModule
.toBoolean()) {
5752 goal
= frontend::ParseGoal::Module
;
5754 } else if (!optionModule
.isUndefined()) {
5755 const char* typeName
= InformalValueTypeName(optionModule
);
5756 JS_ReportErrorASCII(cx
, "option `module` should be a boolean, got %s",
5760 if (!js::ParseCompileOptions(cx
, options
, objOptions
, nullptr)) {
5764 #ifdef JS_ENABLE_SMOOSH
5766 if (!JS_HasProperty(cx
, objOptions
, "rustFrontend", &found
)) {
5770 JS_ReportErrorASCII(cx
, "'rustFrontend' option is renamed to 'smoosh'");
5774 RootedValue
optionSmoosh(cx
);
5775 if (!JS_GetProperty(cx
, objOptions
, "smoosh", &optionSmoosh
)) {
5779 if (optionSmoosh
.isBoolean()) {
5780 smoosh
= optionSmoosh
.toBoolean();
5781 } else if (!optionSmoosh
.isUndefined()) {
5782 const char* typeName
= InformalValueTypeName(optionSmoosh
);
5783 JS_ReportErrorASCII(cx
, "option `smoosh` should be a boolean, got %s",
5787 #endif // JS_ENABLE_SMOOSH
5790 JSString
* scriptContents
= args
[0].toString();
5791 Rooted
<JSLinearString
*> linearString(cx
, scriptContents
->ensureLinear(cx
));
5792 if (!linearString
) {
5796 bool isAscii
= false;
5797 if (linearString
->hasLatin1Chars()) {
5798 JS::AutoCheckCannotGC nogc
;
5799 isAscii
= JS::StringIsASCII(mozilla::Span(
5800 reinterpret_cast<const char*>(linearString
->latin1Chars(nogc
)),
5801 linearString
->length()));
5804 AutoStableStringChars
stableChars(cx
);
5806 if (!stableChars
.init(cx
, scriptContents
)) {
5809 MOZ_ASSERT(stableChars
.isLatin1());
5811 if (!stableChars
.initTwoByte(cx
, scriptContents
)) {
5816 size_t length
= scriptContents
->length();
5817 #ifdef JS_ENABLE_SMOOSH
5818 if (dumpType
== DumpType::ParseNode
) {
5821 const Latin1Char
* chars
= stableChars
.latin1Range().begin().get();
5823 if (goal
== frontend::ParseGoal::Script
) {
5824 if (!SmooshParseScript(cx
, chars
, length
)) {
5828 if (!SmooshParseModule(cx
, chars
, length
)) {
5832 args
.rval().setUndefined();
5835 JS_ReportErrorASCII(cx
,
5836 "SmooshMonkey does not support non-ASCII chars yet");
5840 #endif // JS_ENABLE_SMOOSH
5842 if (goal
== frontend::ParseGoal::Module
) {
5843 // See frontend::CompileModule.
5844 options
.setForceStrictMode();
5845 options
.allowHTMLComments
= false;
5848 if (dumpType
== DumpType::Stencil
) {
5849 #ifdef JS_ENABLE_SMOOSH
5852 if (goal
== frontend::ParseGoal::Script
) {
5853 const Latin1Char
* latin1
= stableChars
.latin1Range().begin().get();
5854 auto utf8
= reinterpret_cast<const mozilla::Utf8Unit
*>(latin1
);
5855 JS::SourceText
<Utf8Unit
> srcBuf
;
5856 if (!srcBuf
.init(cx
, utf8
, length
, JS::SourceOwnership::Borrowed
)) {
5860 AutoReportFrontendContext
fc(cx
);
5861 Rooted
<frontend::CompilationInput
> input(
5862 cx
, frontend::CompilationInput(options
));
5863 UniquePtr
<frontend::ExtensibleCompilationStencil
> stencil
;
5864 if (!Smoosh::tryCompileGlobalScriptToExtensibleStencil(
5865 cx
, &fc
, input
.get(), srcBuf
, stencil
)) {
5869 fc
.clearAutoReport();
5870 JS_ReportErrorASCII(cx
, "SmooshMonkey failed to parse");
5876 frontend::BorrowingCompilationStencil
borrowingStencil(*stencil
);
5877 borrowingStencil
.dump();
5881 JS_ReportErrorASCII(cx
,
5882 "SmooshMonkey does not support module stencil");
5885 args
.rval().setUndefined();
5888 JS_ReportErrorASCII(cx
,
5889 "SmooshMonkey does not support non-ASCII chars yet");
5892 #endif // JS_ENABLE_SMOOSH
5895 const Latin1Char
* latin1
= stableChars
.latin1Range().begin().get();
5896 auto utf8
= reinterpret_cast<const mozilla::Utf8Unit
*>(latin1
);
5897 if (!DumpStencil
<mozilla::Utf8Unit
>(cx
, options
, utf8
, length
, goal
)) {
5901 MOZ_ASSERT(stableChars
.isTwoByte());
5902 const char16_t
* chars
= stableChars
.twoByteRange().begin().get();
5903 if (!DumpStencil
<char16_t
>(cx
, options
, chars
, length
, goal
)) {
5908 args
.rval().setUndefined();
5912 AutoReportFrontendContext
fc(cx
);
5913 Rooted
<frontend::CompilationInput
> input(cx
,
5914 frontend::CompilationInput(options
));
5915 if (goal
== frontend::ParseGoal::Script
) {
5916 if (!input
.get().initForGlobal(&fc
)) {
5920 if (!input
.get().initForModule(&fc
)) {
5925 LifoAllocScope
allocScope(&cx
->tempLifoAlloc());
5926 frontend::NoScopeBindingCache scopeCache
;
5927 frontend::CompilationState
compilationState(&fc
, allocScope
, input
.get());
5928 if (!compilationState
.init(&fc
, &scopeCache
)) {
5933 const Latin1Char
* latin1
= stableChars
.latin1Range().begin().get();
5934 auto utf8
= reinterpret_cast<const mozilla::Utf8Unit
*>(latin1
);
5935 if (!DumpAST
<mozilla::Utf8Unit
>(cx
, options
, utf8
, length
, compilationState
,
5940 MOZ_ASSERT(stableChars
.isTwoByte());
5941 const char16_t
* chars
= stableChars
.twoByteRange().begin().get();
5942 if (!DumpAST
<char16_t
>(cx
, options
, chars
, length
, compilationState
,
5947 args
.rval().setUndefined();
5951 static bool DumpStencil(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5952 return FrontendTest(cx
, argc
, vp
, "dumpStencil", DumpType::Stencil
);
5955 static bool Parse(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5956 // Parse returns local scope information with variables ordered
5957 // differently, depending on the underlying JIT implementation.
5958 if (js::SupportDifferentialTesting()) {
5959 JS_ReportErrorASCII(cx
,
5960 "Function not available in differential testing mode.");
5964 return FrontendTest(cx
, argc
, vp
, "parse", DumpType::ParseNode
);
5967 static bool SyntaxParse(JSContext
* cx
, unsigned argc
, Value
* vp
) {
5968 using namespace js::frontend
;
5970 CallArgs args
= CallArgsFromVp(argc
, vp
);
5972 if (!args
.requireAtLeast(cx
, "syntaxParse", 1)) {
5975 if (!args
[0].isString()) {
5976 const char* typeName
= InformalValueTypeName(args
[0]);
5977 JS_ReportErrorASCII(cx
, "expected string to parse, got %s", typeName
);
5981 JSString
* scriptContents
= args
[0].toString();
5983 CompileOptions
options(cx
);
5984 options
.setIntroductionType("js shell syntaxParse")
5985 .setFileAndLine("<string>", 1);
5987 AutoStableStringChars
stableChars(cx
);
5988 if (!stableChars
.initTwoByte(cx
, scriptContents
)) {
5992 const char16_t
* chars
= stableChars
.twoByteRange().begin().get();
5993 size_t length
= scriptContents
->length();
5995 AutoReportFrontendContext
fc(cx
);
5996 Rooted
<frontend::CompilationInput
> input(cx
,
5997 frontend::CompilationInput(options
));
5998 if (!input
.get().initForGlobal(&fc
)) {
6002 LifoAllocScope
allocScope(&cx
->tempLifoAlloc());
6003 frontend::NoScopeBindingCache scopeCache
;
6004 frontend::CompilationState
compilationState(&fc
, allocScope
, input
.get());
6005 if (!compilationState
.init(&fc
, &scopeCache
)) {
6009 Parser
<frontend::SyntaxParseHandler
, char16_t
> parser(
6010 &fc
, options
, chars
, length
,
6011 /* foldConstants = */ false, compilationState
,
6012 /* syntaxParser = */ nullptr);
6013 if (!parser
.checkOptions()) {
6017 bool succeeded
= parser
.parse().isOk();
6018 if (fc
.hadErrors()) {
6022 if (!succeeded
&& !parser
.hadAbortedSyntaxParse()) {
6023 // If no exception is posted, either there was an OOM or a language
6024 // feature unhandled by the syntax parser was encountered.
6025 MOZ_ASSERT(fc
.hadOutOfMemory());
6029 args
.rval().setBoolean(succeeded
);
6033 static bool OffThreadCompileToStencil(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6034 if (!CanUseExtraThreads()) {
6035 JS_ReportErrorASCII(
6036 cx
, "Can't use offThreadCompileToStencil with --no-threads");
6040 CallArgs args
= CallArgsFromVp(argc
, vp
);
6042 if (!args
.requireAtLeast(cx
, "offThreadCompileToStencil", 1)) {
6045 if (!args
[0].isString()) {
6046 const char* typeName
= InformalValueTypeName(args
[0]);
6047 JS_ReportErrorASCII(cx
, "expected string to parse, got %s", typeName
);
6051 UniqueChars fileNameBytes
;
6052 CompileOptions
options(cx
);
6053 options
.setIntroductionType("js shell offThreadCompileToStencil")
6054 .setFileAndLine("<string>", 1);
6056 if (args
.length() >= 2) {
6057 if (!args
[1].isObject()) {
6058 JS_ReportErrorASCII(
6059 cx
, "offThreadCompileToStencil: The 2nd argument must be an object");
6063 // Offthread compilation requires that the debug metadata be set when the
6064 // script is collected from offthread, rather than when compiled.
6065 RootedObject
opts(cx
, &args
[1].toObject());
6066 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
6071 // This option setting must override whatever the caller requested.
6072 options
.setIsRunOnce(true);
6074 JSString
* scriptContents
= args
[0].toString();
6075 AutoStableStringChars
stableChars(cx
);
6076 if (!stableChars
.initTwoByte(cx
, scriptContents
)) {
6080 size_t length
= scriptContents
->length();
6081 const char16_t
* chars
= stableChars
.twoByteChars();
6083 // Make sure we own the string's chars, so that they are not freed before
6084 // the compilation is finished.
6085 UniqueTwoByteChars ownedChars
;
6086 if (stableChars
.maybeGiveOwnershipToCaller()) {
6087 ownedChars
.reset(const_cast<char16_t
*>(chars
));
6089 ownedChars
.reset(cx
->pod_malloc
<char16_t
>(length
));
6094 mozilla::PodCopy(ownedChars
.get(), chars
, length
);
6097 if (!cx
->runtime()->canUseParallelParsing() || !js::CanUseExtraThreads()) {
6098 JS_ReportErrorASCII(cx
, "cannot compile code on helper thread");
6102 JS::SourceText
<char16_t
> srcBuf
;
6103 if (!srcBuf
.init(cx
, std::move(ownedChars
), length
)) {
6107 OffThreadJob
* job
= NewOffThreadJob(cx
, OffThreadJob::Kind::CompileScript
,
6108 options
, std::move(srcBuf
));
6113 if (!job
->dispatch()) {
6114 ReportOutOfMemory(cx
);
6115 DeleteOffThreadJob(cx
, job
);
6119 args
.rval().setInt32(job
->id
);
6123 static bool FinishOffThreadStencil(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6124 CallArgs args
= CallArgsFromVp(argc
, vp
);
6126 OffThreadJob
* job
= LookupOffThreadJobForArgs(cx
, args
, 0);
6131 job
->waitUntilDone();
6133 RefPtr
<JS::Stencil
> stencil
= job
->stealStencil(cx
);
6134 DeleteOffThreadJob(cx
, job
);
6138 RootedObject
stencilObj(cx
,
6139 js::StencilObject::create(cx
, std::move(stencil
)));
6144 args
.rval().setObject(*stencilObj
);
6148 static bool OffThreadCompileModuleToStencil(JSContext
* cx
, unsigned argc
,
6150 CallArgs args
= CallArgsFromVp(argc
, vp
);
6152 if (!args
.requireAtLeast(cx
, "offThreadCompileModuleToStencil", 1)) {
6155 if (!args
[0].isString()) {
6156 const char* typeName
= InformalValueTypeName(args
[0]);
6157 JS_ReportErrorASCII(cx
, "expected string to parse, got %s", typeName
);
6161 UniqueChars fileNameBytes
;
6162 CompileOptions
options(cx
);
6163 options
.setIntroductionType("js shell offThreadCompileModuleToStencil")
6164 .setFileAndLine("<string>", 1);
6166 if (args
.length() >= 2) {
6167 if (!args
[1].isObject()) {
6168 JS_ReportErrorASCII(cx
,
6169 "offThreadCompileModuleToStencil: The 2nd argument "
6170 "must be an object");
6174 // Offthread compilation requires that the debug metadata be set when the
6175 // script is collected from offthread, rather than when compiled.
6176 RootedObject
opts(cx
, &args
[1].toObject());
6177 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
6182 options
.setIsRunOnce(true).setSourceIsLazy(false);
6184 JSString
* scriptContents
= args
[0].toString();
6185 AutoStableStringChars
stableChars(cx
);
6186 if (!stableChars
.initTwoByte(cx
, scriptContents
)) {
6190 size_t length
= scriptContents
->length();
6191 const char16_t
* chars
= stableChars
.twoByteChars();
6193 // Make sure we own the string's chars, so that they are not freed before
6194 // the compilation is finished.
6195 UniqueTwoByteChars ownedChars
;
6196 if (stableChars
.maybeGiveOwnershipToCaller()) {
6197 ownedChars
.reset(const_cast<char16_t
*>(chars
));
6199 ownedChars
.reset(cx
->pod_malloc
<char16_t
>(length
));
6204 mozilla::PodCopy(ownedChars
.get(), chars
, length
);
6207 if (!cx
->runtime()->canUseParallelParsing() || !js::CanUseExtraThreads()) {
6208 JS_ReportErrorASCII(cx
, "cannot compile code on worker thread");
6212 JS::SourceText
<char16_t
> srcBuf
;
6213 if (!srcBuf
.init(cx
, std::move(ownedChars
), length
)) {
6217 OffThreadJob
* job
= NewOffThreadJob(cx
, OffThreadJob::Kind::CompileModule
,
6218 options
, std::move(srcBuf
));
6223 if (!job
->dispatch()) {
6224 ReportOutOfMemory(cx
);
6225 DeleteOffThreadJob(cx
, job
);
6229 args
.rval().setInt32(job
->id
);
6233 static bool OffThreadDecodeStencil(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6234 if (!CanUseExtraThreads()) {
6235 JS_ReportErrorASCII(cx
,
6236 "Can't use offThreadDecodeStencil with --no-threads");
6240 CallArgs args
= CallArgsFromVp(argc
, vp
);
6242 if (!args
.requireAtLeast(cx
, "offThreadDecodeStencil", 1)) {
6245 if (!args
[0].isObject() || !CacheEntry_isCacheEntry(&args
[0].toObject())) {
6246 const char* typeName
= InformalValueTypeName(args
[0]);
6247 JS_ReportErrorASCII(cx
, "expected cache entry, got %s", typeName
);
6250 RootedObject
cacheEntry(cx
, &args
[0].toObject());
6252 UniqueChars fileNameBytes
;
6253 CompileOptions
options(cx
);
6254 options
.setIntroductionType("js shell offThreadDecodeStencil")
6255 .setFileAndLine("<string>", 1);
6257 if (args
.length() >= 2) {
6258 if (!args
[1].isObject()) {
6259 JS_ReportErrorASCII(
6260 cx
, "offThreadDecodeStencil: The 2nd argument must be an object");
6264 RootedObject
opts(cx
, &args
[1].toObject());
6265 if (!js::ParseCompileOptions(cx
, options
, opts
, &fileNameBytes
)) {
6270 // This option setting must override whatever the caller requested, and
6271 // this should match `Evaluate` that encodes the script.
6272 options
.setIsRunOnce(false);
6274 JS::TranscodeBuffer loadBuffer
;
6275 size_t loadLength
= 0;
6276 uint8_t* loadData
= nullptr;
6277 loadData
= CacheEntry_getBytecode(cx
, cacheEntry
, &loadLength
);
6281 if (!loadBuffer
.append(loadData
, loadLength
)) {
6282 JS_ReportOutOfMemory(cx
);
6286 if (!cx
->runtime()->canUseParallelParsing() || !js::CanUseExtraThreads()) {
6287 JS_ReportErrorASCII(cx
, "cannot compile code on worker thread");
6291 OffThreadJob
* job
= NewOffThreadJob(cx
, OffThreadJob::Kind::Decode
, options
,
6292 std::move(loadBuffer
));
6297 if (!job
->dispatch()) {
6298 ReportOutOfMemory(cx
);
6299 DeleteOffThreadJob(cx
, job
);
6303 args
.rval().setInt32(job
->id
);
6307 class AutoCStringVector
{
6308 Vector
<char*> argv_
;
6311 explicit AutoCStringVector(JSContext
* cx
) : argv_(cx
) {}
6312 ~AutoCStringVector() {
6313 for (size_t i
= 0; i
< argv_
.length(); i
++) {
6317 bool append(UniqueChars
&& arg
) {
6318 if (!argv_
.append(arg
.get())) {
6322 // Now owned by this vector.
6323 (void)arg
.release();
6326 char* const* get() const { return argv_
.begin(); }
6327 size_t length() const { return argv_
.length(); }
6328 char* operator[](size_t i
) const { return argv_
[i
]; }
6329 void replace(size_t i
, UniqueChars arg
) {
6331 argv_
[i
] = arg
.release();
6336 static bool EscapeForShell(JSContext
* cx
, AutoCStringVector
& argv
) {
6337 // Windows will break arguments in argv by various spaces, so we wrap each
6338 // argument in quotes and escape quotes within. Even with quotes, \ will be
6339 // treated like an escape character, so inflate each \ to \\.
6341 for (size_t i
= 0; i
< argv
.length(); i
++) {
6346 size_t newLen
= 3; // quotes before and after and null-terminator
6347 for (char* p
= argv
[i
]; *p
; p
++) {
6349 if (*p
== '\"' || *p
== '\\') {
6354 auto escaped
= cx
->make_pod_array
<char>(newLen
);
6359 char* src
= argv
[i
];
6360 char* dst
= escaped
.get();
6363 if (*src
== '\"' || *src
== '\\') {
6370 MOZ_ASSERT(escaped
.get() + newLen
== dst
);
6372 argv
.replace(i
, std::move(escaped
));
6379 static bool ReadAll(int fd
, wasm::Bytes
* bytes
) {
6380 size_t lastLength
= bytes
->length();
6382 static const int ChunkSize
= 64 * 1024;
6383 if (!bytes
->growBy(ChunkSize
)) {
6389 readCount
= read(fd
, bytes
->begin() + lastLength
, ChunkSize
);
6390 if (readCount
>= 0) {
6393 if (errno
!= EINTR
) {
6398 if (readCount
< ChunkSize
) {
6399 bytes
->shrinkTo(lastLength
+ readCount
);
6400 if (readCount
== 0) {
6405 lastLength
= bytes
->length();
6409 static bool WriteAll(int fd
, const uint8_t* bytes
, size_t length
) {
6410 while (length
> 0) {
6411 int written
= write(fd
, bytes
, length
);
6413 if (errno
== EINTR
) {
6418 MOZ_ASSERT(unsigned(written
) <= length
);
6436 if (fds_
[0] != -1) {
6439 if (fds_
[1] != -1) {
6446 return !_pipe(fds_
, 4096, O_BINARY
);
6452 int reader() const {
6453 MOZ_ASSERT(fds_
[0] != -1);
6457 int writer() const {
6458 MOZ_ASSERT(fds_
[1] != -1);
6462 void closeReader() {
6463 MOZ_ASSERT(fds_
[0] != -1);
6468 void closeWriter() {
6469 MOZ_ASSERT(fds_
[1] != -1);
6477 char** shell::sArgv
;
6480 static const char sWasmCompileAndSerializeFlag
[] =
6481 "--wasm-compile-and-serialize";
6482 static Vector
<const char*, 5, js::SystemAllocPolicy
> sCompilerProcessFlags
;
6484 static bool CompileAndSerializeInSeparateProcess(JSContext
* cx
,
6485 const uint8_t* bytecode
,
6486 size_t bytecodeLength
,
6487 wasm::Bytes
* serialized
) {
6488 AutoPipe stdIn
, stdOut
;
6489 if (!stdIn
.init() || !stdOut
.init()) {
6493 AutoCStringVector
argv(cx
);
6495 UniqueChars argv0
= DuplicateString(cx
, sArgv
[0]);
6496 if (!argv0
|| !argv
.append(std::move(argv0
))) {
6500 // Put compiler flags first since they must precede the non-option
6501 // file-descriptor args (passed on Windows, below).
6502 for (unsigned i
= 0; i
< sCompilerProcessFlags
.length(); i
++) {
6503 UniqueChars flags
= DuplicateString(cx
, sCompilerProcessFlags
[i
]);
6504 if (!flags
|| !argv
.append(std::move(flags
))) {
6511 arg
= DuplicateString(sWasmCompileAndSerializeFlag
);
6512 if (!arg
|| !argv
.append(std::move(arg
))) {
6517 // The spawned process will have all the stdIn/stdOut file handles open, but
6518 // without the power of fork, we need some other way to communicate the
6519 // integer fd values so we encode them in argv and WasmCompileAndSerialize()
6520 // has a matching #ifdef XP_WIN to parse them out. Communicate both ends of
6521 // both pipes so the child process can closed the unused ends.
6523 arg
= JS_smprintf("%d", stdIn
.reader());
6524 if (!arg
|| !argv
.append(std::move(arg
))) {
6528 arg
= JS_smprintf("%d", stdIn
.writer());
6529 if (!arg
|| !argv
.append(std::move(arg
))) {
6533 arg
= JS_smprintf("%d", stdOut
.reader());
6534 if (!arg
|| !argv
.append(std::move(arg
))) {
6538 arg
= JS_smprintf("%d", stdOut
.writer());
6539 if (!arg
|| !argv
.append(std::move(arg
))) {
6544 // Required by both _spawnv and exec.
6545 if (!argv
.append(nullptr)) {
6550 if (!EscapeForShell(cx
, argv
)) {
6554 int childPid
= _spawnv(P_NOWAIT
, sArgv
[0], argv
.get());
6555 if (childPid
== -1) {
6559 pid_t childPid
= fork();
6564 // In the child process. Redirect stdin/stdout to the respective ends of
6565 // the pipes. Closing stdIn.writer() is necessary for stdin to hit EOF.
6566 // This case statement must not return before exec() takes over. Rather,
6567 // exit(-1) is used to return failure to the parent process.
6568 if (dup2(stdIn
.reader(), STDIN_FILENO
) == -1) {
6571 if (dup2(stdOut
.writer(), STDOUT_FILENO
) == -1) {
6574 close(stdIn
.reader());
6575 close(stdIn
.writer());
6576 close(stdOut
.reader());
6577 close(stdOut
.writer());
6578 execv(sArgv
[0], argv
.get());
6583 // In the parent process. Closing stdOut.writer() is necessary for
6584 // stdOut.reader() below to hit EOF.
6585 stdIn
.closeReader();
6586 stdOut
.closeWriter();
6588 if (!WriteAll(stdIn
.writer(), bytecode
, bytecodeLength
)) {
6592 stdIn
.closeWriter();
6594 if (!ReadAll(stdOut
.reader(), serialized
)) {
6598 stdOut
.closeReader();
6602 if (_cwait(&status
, childPid
, WAIT_CHILD
) == -1) {
6607 if (waitpid(childPid
, &status
, 0) >= 0) {
6610 if (errno
!= EINTR
) {
6619 static bool WasmCompileAndSerialize(JSContext
* cx
) {
6620 MOZ_ASSERT(wasm::CodeCachingAvailable(cx
));
6623 // See CompileAndSerializeInSeparateProcess for why we've had to smuggle
6624 // these fd values through argv. Closing the writing ends is necessary for
6625 // the reading ends to hit EOF.
6627 for (; flagIndex
< sArgc
; flagIndex
++) {
6628 if (!strcmp(sArgv
[flagIndex
], sWasmCompileAndSerializeFlag
)) {
6632 MOZ_RELEASE_ASSERT(flagIndex
< sArgc
);
6634 int fdsIndex
= flagIndex
+ 1;
6635 MOZ_RELEASE_ASSERT(fdsIndex
+ 4 == sArgc
);
6637 int stdInReader
= atoi(sArgv
[fdsIndex
+ 0]);
6638 int stdInWriter
= atoi(sArgv
[fdsIndex
+ 1]);
6639 int stdOutReader
= atoi(sArgv
[fdsIndex
+ 2]);
6640 int stdOutWriter
= atoi(sArgv
[fdsIndex
+ 3]);
6642 int stdIn
= stdInReader
;
6644 close(stdOutReader
);
6645 int stdOut
= stdOutWriter
;
6647 int stdIn
= STDIN_FILENO
;
6648 int stdOut
= STDOUT_FILENO
;
6651 wasm::MutableBytes bytecode
= js_new
<wasm::ShareableBytes
>();
6652 if (!ReadAll(stdIn
, &bytecode
->bytes
)) {
6656 wasm::Bytes serialized
;
6657 if (!wasm::CompileAndSerialize(cx
, *bytecode
, &serialized
)) {
6661 if (!WriteAll(stdOut
, serialized
.begin(), serialized
.length())) {
6668 static bool WasmCompileInSeparateProcess(JSContext
* cx
, unsigned argc
,
6670 if (!wasm::CodeCachingAvailable(cx
)) {
6671 JS_ReportErrorASCII(cx
, "WebAssembly caching not supported");
6675 CallArgs args
= CallArgsFromVp(argc
, vp
);
6676 if (!args
.requireAtLeast(cx
, "wasmCompileInSeparateProcess", 1)) {
6680 SharedMem
<uint8_t*> bytecode
;
6682 if (!args
[0].isObject() ||
6683 !IsBufferSource(&args
[0].toObject(), &bytecode
, &numBytes
)) {
6684 RootedObject
callee(cx
, &args
.callee());
6685 ReportUsageErrorASCII(cx
, callee
, "Argument must be a buffer source");
6689 wasm::Bytes serialized
;
6690 if (!CompileAndSerializeInSeparateProcess(cx
, bytecode
.unwrap(), numBytes
,
6692 if (!cx
->isExceptionPending()) {
6693 JS_ReportErrorASCII(cx
, "creating and executing child process");
6698 RootedObject
module(cx
);
6699 if (!wasm::DeserializeModule(cx
, serialized
, &module
)) {
6703 args
.rval().setObject(*module
);
6708 static bool DecompileFunction(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6709 CallArgs args
= CallArgsFromVp(argc
, vp
);
6710 if (args
.length() < 1 || !args
[0].isObject() ||
6711 !args
[0].toObject().is
<JSFunction
>()) {
6712 args
.rval().setUndefined();
6715 RootedFunction
fun(cx
, &args
[0].toObject().as
<JSFunction
>());
6716 JSString
* result
= JS_DecompileFunction(cx
, fun
);
6720 args
.rval().setString(result
);
6724 static bool DecompileThisScript(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6725 CallArgs args
= CallArgsFromVp(argc
, vp
);
6727 NonBuiltinScriptFrameIter
iter(cx
);
6729 args
.rval().setString(cx
->runtime()->emptyString
);
6734 JSAutoRealm
ar(cx
, iter
.script());
6736 RootedScript
script(cx
, iter
.script());
6737 JSString
* result
= JS_DecompileScript(cx
, script
);
6742 args
.rval().setString(result
);
6745 return JS_WrapValue(cx
, args
.rval());
6748 static bool ValueToSource(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6749 CallArgs args
= CallArgsFromVp(argc
, vp
);
6751 JSString
* str
= ValueToSource(cx
, args
.get(0));
6756 args
.rval().setString(str
);
6760 static bool ThisFilename(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6761 CallArgs args
= CallArgsFromVp(argc
, vp
);
6763 JS::AutoFilename filename
;
6764 if (!DescribeScriptedCaller(cx
, &filename
) || !filename
.get()) {
6765 args
.rval().setString(cx
->runtime()->emptyString
);
6769 JSString
* str
= NewStringCopyUTF8(cx
, filename
.get());
6774 args
.rval().setString(str
);
6778 static bool WrapWithProto(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6779 CallArgs args
= CallArgsFromVp(argc
, vp
);
6780 Value obj
= args
.get(0);
6781 Value proto
= args
.get(1);
6782 if (!obj
.isObject() || !proto
.isObjectOrNull()) {
6783 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
6784 JSSMSG_INVALID_ARGS
, "wrapWithProto");
6788 // Disallow constructing (deeply) nested wrapper chains, to avoid running
6789 // out of stack space in isCallable/isConstructor. See bug 1126105.
6790 if (IsWrapper(&obj
.toObject())) {
6791 JS_ReportErrorASCII(cx
, "wrapWithProto cannot wrap a wrapper");
6795 WrapperOptions
options(cx
);
6796 options
.setProto(proto
.toObjectOrNull());
6797 JSObject
* wrapped
= Wrapper::New(cx
, &obj
.toObject(),
6798 &Wrapper::singletonWithPrototype
, options
);
6803 args
.rval().setObject(*wrapped
);
6807 static bool NewGlobal(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6808 CallArgs args
= CallArgsFromVp(argc
, vp
);
6809 RootedObject
callee(cx
, &args
.callee());
6811 JS::RealmOptions options
;
6812 JS::RealmCreationOptions
& creationOptions
= options
.creationOptions();
6813 JS::RealmBehaviors
& behaviors
= options
.behaviors();
6814 ShellGlobalKind kind
= ShellGlobalKind::WindowProxy
;
6815 bool immutablePrototype
= true;
6817 SetStandardRealmOptions(options
);
6819 // Default to creating the global in the current compartment unless
6820 // --more-compartments is used.
6821 if (defaultToSameCompartment
) {
6822 creationOptions
.setExistingCompartment(cx
->global());
6824 creationOptions
.setNewCompartmentAndZone();
6827 JS::AutoHoldPrincipals
principals(cx
);
6829 if (args
.length() == 1 && args
[0].isObject()) {
6830 RootedObject
opts(cx
, &args
[0].toObject());
6833 if (!JS_GetProperty(cx
, opts
, "invisibleToDebugger", &v
)) {
6836 if (v
.isBoolean()) {
6837 creationOptions
.setInvisibleToDebugger(v
.toBoolean());
6840 if (!JS_GetProperty(cx
, opts
, "sameZoneAs", &v
)) {
6844 creationOptions
.setNewCompartmentInExistingZone(
6845 UncheckedUnwrap(&v
.toObject()));
6848 if (!JS_GetProperty(cx
, opts
, "sameCompartmentAs", &v
)) {
6852 creationOptions
.setExistingCompartment(UncheckedUnwrap(&v
.toObject()));
6855 if (!JS_GetProperty(cx
, opts
, "newCompartment", &v
)) {
6858 if (v
.isBoolean()) {
6859 if (v
.toBoolean()) {
6860 creationOptions
.setNewCompartmentAndZone();
6862 creationOptions
.setExistingCompartment(cx
->global());
6866 if (!JS_GetProperty(cx
, opts
, "discardSource", &v
)) {
6869 if (v
.isBoolean()) {
6870 behaviors
.setDiscardSource(v
.toBoolean());
6873 if (!JS_GetProperty(cx
, opts
, "useWindowProxy", &v
)) {
6876 if (v
.isBoolean()) {
6877 kind
= v
.toBoolean() ? ShellGlobalKind::WindowProxy
6878 : ShellGlobalKind::GlobalObject
;
6881 if (!JS_GetProperty(cx
, opts
, "immutablePrototype", &v
)) {
6884 if (v
.isBoolean()) {
6885 immutablePrototype
= v
.toBoolean();
6888 if (!JS_GetProperty(cx
, opts
, "systemPrincipal", &v
)) {
6891 if (v
.isBoolean()) {
6892 principals
.reset(&ShellPrincipals::fullyTrusted
);
6895 if (!JS_GetProperty(cx
, opts
, "principal", &v
)) {
6898 if (!v
.isUndefined()) {
6900 if (!ToUint32(cx
, v
, &bits
)) {
6903 JSPrincipals
* newPrincipals
= cx
->new_
<ShellPrincipals
>(bits
);
6904 if (!newPrincipals
) {
6907 principals
.reset(newPrincipals
);
6910 if (!JS_GetProperty(cx
, opts
, "enableCoopAndCoep", &v
)) {
6913 if (v
.isBoolean()) {
6914 creationOptions
.setCoopAndCoepEnabled(v
.toBoolean());
6917 if (!JS_GetProperty(cx
, opts
, "freezeBuiltins", &v
)) {
6920 if (v
.isBoolean()) {
6921 creationOptions
.setFreezeBuiltins(v
.toBoolean());
6924 // On the web, the SharedArrayBuffer constructor is not installed as a
6925 // global property in pages that aren't isolated in a separate process (and
6926 // thus can't allow the structured cloning of shared memory). Specify false
6927 // for this option to reproduce this behavior.
6928 if (!JS_GetProperty(cx
, opts
, "defineSharedArrayBufferConstructor", &v
)) {
6931 if (v
.isBoolean()) {
6932 creationOptions
.setDefineSharedArrayBufferConstructor(v
.toBoolean());
6935 if (!JS_GetProperty(cx
, opts
, "forceUTC", &v
)) {
6938 if (v
.isBoolean()) {
6939 creationOptions
.setForceUTC(v
.toBoolean());
6942 if (!JS_GetProperty(cx
, opts
, "alwaysUseFdlibm", &v
)) {
6945 if (v
.isBoolean()) {
6946 creationOptions
.setAlwaysUseFdlibm(v
.toBoolean());
6949 if (!JS_GetProperty(cx
, opts
, "locale", &v
)) {
6953 RootedString
str(cx
, v
.toString());
6954 UniqueChars locale
= StringToLocale(cx
, callee
, str
);
6958 creationOptions
.setLocaleCopyZ(locale
.get());
6962 if (!CheckRealmOptions(cx
, options
, principals
.get())) {
6966 RootedObject
global(cx
, NewGlobalObject(cx
, options
, principals
.get(), kind
,
6967 immutablePrototype
));
6972 RootedObject
wrapped(cx
, ToWindowProxyIfWindow(global
));
6973 if (!JS_WrapObject(cx
, &wrapped
)) {
6977 args
.rval().setObject(*wrapped
);
6981 static bool NukeAllCCWs(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6982 CallArgs args
= CallArgsFromVp(argc
, vp
);
6984 if (args
.length() != 0) {
6985 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
6986 JSSMSG_INVALID_ARGS
, "nukeAllCCWs");
6990 NukeCrossCompartmentWrappers(cx
, AllCompartments(), cx
->realm(),
6991 NukeWindowReferences
, NukeAllReferences
);
6992 args
.rval().setUndefined();
6996 static bool RecomputeWrappers(JSContext
* cx
, unsigned argc
, Value
* vp
) {
6997 CallArgs args
= CallArgsFromVp(argc
, vp
);
6999 if (args
.length() > 2) {
7000 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
7001 JSSMSG_INVALID_ARGS
, "recomputeWrappers");
7005 JS::Compartment
* sourceComp
= nullptr;
7006 if (args
.get(0).isObject()) {
7007 sourceComp
= JS::GetCompartment(UncheckedUnwrap(&args
[0].toObject()));
7010 JS::Compartment
* targetComp
= nullptr;
7011 if (args
.get(1).isObject()) {
7012 targetComp
= JS::GetCompartment(UncheckedUnwrap(&args
[1].toObject()));
7015 struct SingleOrAllCompartments final
: public CompartmentFilter
{
7016 JS::Compartment
* comp
;
7017 explicit SingleOrAllCompartments(JS::Compartment
* c
) : comp(c
) {}
7018 virtual bool match(JS::Compartment
* c
) const override
{
7019 return !comp
|| comp
== c
;
7023 if (!js::RecomputeWrappers(cx
, SingleOrAllCompartments(sourceComp
),
7024 SingleOrAllCompartments(targetComp
))) {
7028 args
.rval().setUndefined();
7032 static bool DumpObjectWrappers(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7033 CallArgs args
= CallArgsFromVp(argc
, vp
);
7035 bool printedHeader
= false;
7036 for (ZonesIter
zone(cx
->runtime(), WithAtoms
); !zone
.done(); zone
.next()) {
7037 bool printedZoneInfo
= false;
7038 for (CompartmentsInZoneIter
comp(zone
); !comp
.done(); comp
.next()) {
7039 bool printedCompartmentInfo
= false;
7040 for (Compartment::ObjectWrapperEnum
e(comp
); !e
.empty(); e
.popFront()) {
7041 JSObject
* wrapper
= e
.front().value().unbarrieredGet();
7042 JSObject
* wrapped
= e
.front().key();
7043 if (!printedHeader
) {
7044 fprintf(stderr
, "Cross-compartment object wrappers:\n");
7045 printedHeader
= true;
7047 if (!printedZoneInfo
) {
7048 fprintf(stderr
, " Zone %p:\n", zone
.get());
7049 printedZoneInfo
= true;
7051 if (!printedCompartmentInfo
) {
7052 fprintf(stderr
, " Compartment %p:\n", comp
.get());
7053 printedCompartmentInfo
= true;
7056 " Object wrapper %p -> %p in zone %p compartment %p\n",
7057 wrapper
, wrapped
, wrapped
->zone(), wrapped
->compartment());
7062 if (!printedHeader
) {
7063 fprintf(stderr
, "No cross-compartment object wrappers.\n");
7066 args
.rval().setUndefined();
7070 static bool GetMaxArgs(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7071 CallArgs args
= CallArgsFromVp(argc
, vp
);
7072 args
.rval().setInt32(ARGS_LENGTH_MAX
);
7076 static bool IsHTMLDDA_Call(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7077 CallArgs args
= CallArgsFromVp(argc
, vp
);
7079 // These are the required conditions under which this object may be called
7080 // by test262 tests, and the required behavior under those conditions.
7081 if (args
.length() == 0 ||
7082 (args
[0].isString() && args
[0].toString()->length() == 0)) {
7083 args
.rval().setNull();
7087 JS_ReportErrorASCII(
7088 cx
, "IsHTMLDDA object is being called in an impermissible manner");
7092 static bool CreateIsHTMLDDA(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7093 CallArgs args
= CallArgsFromVp(argc
, vp
);
7095 static const JSClassOps classOps
= {
7096 nullptr, // addProperty
7097 nullptr, // delProperty
7098 nullptr, // enumerate
7099 nullptr, // newEnumerate
7101 nullptr, // mayResolve
7102 nullptr, // finalize
7103 IsHTMLDDA_Call
, // call
7104 nullptr, // construct
7108 static const JSClass cls
= {
7110 JSCLASS_EMULATES_UNDEFINED
,
7114 JSObject
* obj
= JS_NewObject(cx
, &cls
);
7118 args
.rval().setObject(*obj
);
7122 static bool GetSelfHostedValue(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7123 CallArgs args
= CallArgsFromVp(argc
, vp
);
7125 if (args
.length() != 1 || !args
[0].isString()) {
7126 JS_ReportErrorNumberASCII(cx
, my_GetErrorMessage
, nullptr,
7127 JSSMSG_INVALID_ARGS
, "getSelfHostedValue");
7130 Rooted
<JSAtom
*> srcAtom(cx
, ToAtom
<CanGC
>(cx
, args
[0]));
7134 Rooted
<PropertyName
*> srcName(cx
, srcAtom
->asPropertyName());
7135 return GlobalObject::getIntrinsicValue(cx
, cx
->global(), srcName
,
7139 class ShellSourceHook
: public SourceHook
{
7140 // The function we should call to lazily retrieve source code.
7141 PersistentRootedFunction fun
;
7144 ShellSourceHook(JSContext
* cx
, JSFunction
& fun
) : fun(cx
, &fun
) {}
7146 bool load(JSContext
* cx
, const char* filename
, char16_t
** twoByteSource
,
7147 char** utf8Source
, size_t* length
) override
{
7148 MOZ_ASSERT((twoByteSource
!= nullptr) != (utf8Source
!= nullptr),
7149 "must be called requesting only one of UTF-8 or UTF-16 source");
7151 RootedString
str(cx
);
7153 str
= NewStringCopyUTF8(cx
, filename
);
7158 str
= JS_GetEmptyString(cx
);
7160 RootedValue
filenameValue(cx
, StringValue(str
));
7162 RootedValue
result(cx
);
7163 if (!Call(cx
, UndefinedHandleValue
, fun
, HandleValueArray(filenameValue
),
7168 str
= JS::ToString(cx
, result
);
7173 Rooted
<JSLinearString
*> linear(cx
, str
->ensureLinear(cx
));
7178 if (twoByteSource
) {
7179 *length
= JS_GetStringLength(linear
);
7181 *twoByteSource
= cx
->pod_malloc
<char16_t
>(*length
);
7182 if (!*twoByteSource
) {
7186 CopyChars(*twoByteSource
, *linear
);
7188 MOZ_ASSERT(utf8Source
!= nullptr);
7190 *length
= JS::GetDeflatedUTF8StringLength(linear
);
7192 *utf8Source
= cx
->pod_malloc
<char>(*length
);
7197 mozilla::DebugOnly
<size_t> dstLen
= JS::DeflateStringToUTF8Buffer(
7198 linear
, mozilla::Span(*utf8Source
, *length
));
7199 MOZ_ASSERT(dstLen
== *length
);
7206 static bool WithSourceHook(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7207 CallArgs args
= CallArgsFromVp(argc
, vp
);
7208 RootedObject
callee(cx
, &args
.callee());
7210 if (args
.length() != 2) {
7211 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments.");
7215 if (!args
[0].isObject() || !args
[0].toObject().is
<JSFunction
>() ||
7216 !args
[1].isObject() || !args
[1].toObject().is
<JSFunction
>()) {
7217 ReportUsageErrorASCII(cx
, callee
,
7218 "First and second arguments must be functions.");
7222 mozilla::UniquePtr
<ShellSourceHook
> hook
=
7223 mozilla::MakeUnique
<ShellSourceHook
>(cx
,
7224 args
[0].toObject().as
<JSFunction
>());
7229 mozilla::UniquePtr
<SourceHook
> savedHook
= js::ForgetSourceHook(cx
);
7230 js::SetSourceHook(cx
, std::move(hook
));
7232 RootedObject
fun(cx
, &args
[1].toObject());
7233 bool result
= Call(cx
, UndefinedHandleValue
, fun
,
7234 JS::HandleValueArray::empty(), args
.rval());
7235 js::SetSourceHook(cx
, std::move(savedHook
));
7239 static void PrintProfilerEvents_Callback(const char* msg
, const char* details
) {
7240 fprintf(stderr
, "PROFILER EVENT: %s %s\n", msg
, details
);
7243 static bool PrintProfilerEvents(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7244 CallArgs args
= CallArgsFromVp(argc
, vp
);
7245 if (cx
->runtime()->geckoProfiler().enabled()) {
7246 js::RegisterContextProfilingEventMarker(cx
, &PrintProfilerEvents_Callback
);
7248 args
.rval().setUndefined();
7252 #ifdef SINGLESTEP_PROFILING
7253 static void SingleStepCallback(void* arg
, jit::Simulator
* sim
, void* pc
) {
7254 JSContext
* cx
= reinterpret_cast<JSContext
*>(arg
);
7256 // If profiling is not enabled, don't do anything.
7257 if (!cx
->runtime()->geckoProfiler().enabled()) {
7261 JS::ProfilingFrameIterator::RegisterState state
;
7263 # if defined(JS_SIMULATOR_ARM)
7264 state
.sp
= (void*)sim
->get_register(jit::Simulator::sp
);
7265 state
.lr
= (void*)sim
->get_register(jit::Simulator::lr
);
7266 state
.fp
= (void*)sim
->get_register(jit::Simulator::fp
);
7267 state
.tempFP
= (void*)sim
->get_register(jit::Simulator::r7
);
7268 # elif defined(JS_SIMULATOR_MIPS64) || defined(JS_SIMULATOR_MIPS32)
7269 state
.sp
= (void*)sim
->getRegister(jit::Simulator::sp
);
7270 state
.lr
= (void*)sim
->getRegister(jit::Simulator::ra
);
7271 state
.fp
= (void*)sim
->getRegister(jit::Simulator::fp
);
7272 # elif defined(JS_SIMULATOR_LOONG64)
7273 state
.sp
= (void*)sim
->getRegister(jit::Simulator::sp
);
7274 state
.lr
= (void*)sim
->getRegister(jit::Simulator::ra
);
7275 state
.fp
= (void*)sim
->getRegister(jit::Simulator::fp
);
7277 # error "NYI: Single-step profiling support"
7280 mozilla::DebugOnly
<void*> lastStackAddress
= nullptr;
7282 uint32_t frameNo
= 0;
7283 AutoEnterOOMUnsafeRegion oomUnsafe
;
7284 for (JS::ProfilingFrameIterator
i(cx
, state
); !i
.done(); ++i
) {
7285 MOZ_ASSERT(i
.stackAddress() != nullptr);
7286 MOZ_ASSERT(lastStackAddress
<= i
.stackAddress());
7287 lastStackAddress
= i
.stackAddress();
7288 JS::ProfilingFrameIterator::Frame frames
[16];
7289 uint32_t nframes
= i
.extractStack(frames
, 0, 16);
7290 for (uint32_t i
= 0; i
< nframes
; i
++) {
7291 // Assert endStackAddress never exceeds sp (bug 1782188).
7292 MOZ_ASSERT(frames
[i
].endStackAddress
>= state
.sp
);
7294 if (!stack
.append(",", 1)) {
7295 oomUnsafe
.crash("stack.append");
7298 if (!stack
.append(frames
[i
].label
, strlen(frames
[i
].label
))) {
7299 oomUnsafe
.crash("stack.append");
7305 ShellContext
* sc
= GetShellContext(cx
);
7307 // Only append the stack if it differs from the last stack.
7308 if (sc
->stacks
.empty() || sc
->stacks
.back().length() != stack
.length() ||
7309 !ArrayEqual(sc
->stacks
.back().begin(), stack
.begin(), stack
.length())) {
7310 if (!sc
->stacks
.append(std::move(stack
))) {
7311 oomUnsafe
.crash("stacks.append");
7317 static bool EnableSingleStepProfiling(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7318 #ifdef SINGLESTEP_PROFILING
7319 CallArgs args
= CallArgsFromVp(argc
, vp
);
7321 jit::Simulator
* sim
= cx
->simulator();
7322 sim
->enable_single_stepping(SingleStepCallback
, cx
);
7324 args
.rval().setUndefined();
7327 JS_ReportErrorASCII(cx
, "single-step profiling not enabled on this platform");
7332 static bool DisableSingleStepProfiling(JSContext
* cx
, unsigned argc
,
7334 #ifdef SINGLESTEP_PROFILING
7335 CallArgs args
= CallArgsFromVp(argc
, vp
);
7337 jit::Simulator
* sim
= cx
->simulator();
7338 sim
->disable_single_stepping();
7340 ShellContext
* sc
= GetShellContext(cx
);
7342 RootedValueVector
elems(cx
);
7343 for (size_t i
= 0; i
< sc
->stacks
.length(); i
++) {
7345 JS_NewUCStringCopyN(cx
, sc
->stacks
[i
].begin(), sc
->stacks
[i
].length());
7349 if (!elems
.append(StringValue(stack
))) {
7354 JSObject
* array
= JS::NewArrayObject(cx
, elems
);
7360 args
.rval().setObject(*array
);
7363 JS_ReportErrorASCII(cx
, "single-step profiling not enabled on this platform");
7368 static bool IsLatin1(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7369 CallArgs args
= CallArgsFromVp(argc
, vp
);
7371 args
.get(0).isString() && args
[0].toString()->hasLatin1Chars();
7372 args
.rval().setBoolean(isLatin1
);
7376 static bool EnableGeckoProfiling(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7377 CallArgs args
= CallArgsFromVp(argc
, vp
);
7379 if (!EnsureGeckoProfilingStackInstalled(cx
, GetShellContext(cx
))) {
7383 cx
->runtime()->geckoProfiler().enableSlowAssertions(false);
7384 cx
->runtime()->geckoProfiler().enable(true);
7386 args
.rval().setUndefined();
7390 static bool EnableGeckoProfilingWithSlowAssertions(JSContext
* cx
, unsigned argc
,
7392 CallArgs args
= CallArgsFromVp(argc
, vp
);
7393 args
.rval().setUndefined();
7395 if (cx
->runtime()->geckoProfiler().enabled()) {
7396 // If profiling already enabled with slow assertions disabled,
7398 if (cx
->runtime()->geckoProfiler().slowAssertionsEnabled()) {
7402 // Slow assertions are off. Disable profiling before re-enabling
7403 // with slow assertions on.
7404 cx
->runtime()->geckoProfiler().enable(false);
7407 if (!EnsureGeckoProfilingStackInstalled(cx
, GetShellContext(cx
))) {
7411 cx
->runtime()->geckoProfiler().enableSlowAssertions(true);
7412 cx
->runtime()->geckoProfiler().enable(true);
7417 static bool DisableGeckoProfiling(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7418 CallArgs args
= CallArgsFromVp(argc
, vp
);
7419 args
.rval().setUndefined();
7421 if (!cx
->runtime()->geckoProfiler().enabled()) {
7425 cx
->runtime()->geckoProfiler().enable(false);
7429 // Global mailbox that is used to communicate a shareable object value from one
7430 // worker to another.
7432 // These object types are shareable:
7434 // - SharedArrayBuffer
7435 // - WasmMemoryObject (when constructed with shared:true)
7436 // - WasmModuleObject
7438 // For the SharedArrayBuffer and WasmMemoryObject we transmit the underlying
7439 // SharedArrayRawBuffer ("SARB"). For the WasmModuleObject we transmit the
7440 // underlying JS::WasmModule. The transmitted types are refcounted. When they
7441 // are in the mailbox their reference counts are at least 1, accounting for the
7442 // reference from the mailbox.
7444 // The lock guards the mailbox variable and prevents a race where two workers
7445 // try to set the mailbox at the same time to replace an object that is only
7446 // referenced from the mailbox: the workers will both decrement the reference
7447 // count on the old object, and one of those decrements will be on a garbage
7448 // object. We could implement this with atomics and a CAS loop but it's not
7449 // worth the bother.
7451 // Note that if a thread reads the mailbox repeatedly it will get distinct
7452 // objects on each read. The alternatives are to cache created objects locally,
7453 // but this retains storage we don't need to retain, or to somehow clear the
7454 // mailbox locally, but this creates a coordination headache. Buyer beware.
7456 enum class MailboxTag
{
7464 struct SharedObjectMailbox
{
7467 SharedArrayRawBuffer
* buffer
;
7469 bool isHugeMemory
; // For a WasmMemory tag, otherwise false
7471 JS::WasmModule
* module
;
7474 Value() : number(0.0) {}
7477 MailboxTag tag
= MailboxTag::Empty
;
7481 typedef ExclusiveData
<SharedObjectMailbox
> SOMailbox
;
7483 // Never null after successful initialization.
7484 static SOMailbox
* sharedObjectMailbox
;
7486 static bool InitSharedObjectMailbox() {
7487 sharedObjectMailbox
= js_new
<SOMailbox
>(mutexid::ShellObjectMailbox
);
7488 return sharedObjectMailbox
!= nullptr;
7491 static void DestructSharedObjectMailbox() {
7492 // All workers need to have terminated at this point.
7495 auto mbx
= sharedObjectMailbox
->lock();
7497 case MailboxTag::Empty
:
7498 case MailboxTag::Number
:
7500 case MailboxTag::SharedArrayBuffer
:
7501 case MailboxTag::WasmMemory
:
7502 mbx
->val
.sarb
.buffer
->dropReference();
7504 case MailboxTag::WasmModule
:
7505 mbx
->val
.module
->Release();
7512 js_delete(sharedObjectMailbox
);
7513 sharedObjectMailbox
= nullptr;
7516 static bool GetSharedObject(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7517 CallArgs args
= CallArgsFromVp(argc
, vp
);
7518 RootedObject
newObj(cx
);
7521 auto mbx
= sharedObjectMailbox
->lock();
7523 case MailboxTag::Empty
: {
7526 case MailboxTag::Number
: {
7527 args
.rval().setNumber(mbx
->val
.number
);
7530 case MailboxTag::SharedArrayBuffer
:
7531 case MailboxTag::WasmMemory
: {
7532 // Flag was set in the sender; ensure it is set in the receiver.
7534 cx
->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
7536 // The protocol for creating a SAB requires the refcount to be
7537 // incremented prior to the SAB creation.
7539 SharedArrayRawBuffer
* buf
= mbx
->val
.sarb
.buffer
;
7540 size_t length
= mbx
->val
.sarb
.length
;
7541 if (!buf
->addReference()) {
7542 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
7543 JSMSG_SC_SAB_REFCNT_OFLO
);
7547 // If the allocation fails we must decrement the refcount before
7550 Rooted
<ArrayBufferObjectMaybeShared
*> maybesab(
7551 cx
, SharedArrayBufferObject::New(cx
, buf
, length
));
7553 buf
->dropReference();
7557 // At this point the SAB was created successfully and it owns the
7558 // refcount-increase on the buffer that we performed above. So even
7559 // if we fail to allocate along any path below we must not decrement
7560 // the refcount; the garbage collector must be allowed to handle
7561 // that via finalization of the orphaned SAB object.
7563 if (mbx
->tag
== MailboxTag::SharedArrayBuffer
) {
7566 if (!GlobalObject::ensureConstructor(cx
, cx
->global(),
7567 JSProto_WebAssembly
)) {
7570 RootedObject
proto(cx
,
7571 &cx
->global()->getPrototype(JSProto_WasmMemory
));
7572 newObj
= WasmMemoryObject::create(cx
, maybesab
,
7573 mbx
->val
.sarb
.isHugeMemory
, proto
);
7574 MOZ_ASSERT_IF(newObj
, newObj
->as
<WasmMemoryObject
>().isShared());
7582 case MailboxTag::WasmModule
: {
7583 // Flag was set in the sender; ensure it is set in the receiver.
7585 cx
->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
7587 if (!GlobalObject::ensureConstructor(cx
, cx
->global(),
7588 JSProto_WebAssembly
)) {
7592 // WasmModuleObject::create() increments the refcount on the module
7593 // and signals an error and returns null if that fails.
7594 newObj
= mbx
->val
.module
->createObject(cx
);
7606 args
.rval().setObjectOrNull(newObj
);
7610 static bool SetSharedObject(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7611 CallArgs args
= CallArgsFromVp(argc
, vp
);
7613 MailboxTag tag
= MailboxTag::Empty
;
7614 SharedObjectMailbox::Value value
;
7616 // Increase refcounts when we obtain the value to avoid operating on dead
7617 // storage during self-assignment.
7619 if (args
.get(0).isObject()) {
7620 RootedObject
obj(cx
, &args
[0].toObject());
7621 if (obj
->is
<SharedArrayBufferObject
>()) {
7622 Rooted
<SharedArrayBufferObject
*> sab(cx
,
7623 &obj
->as
<SharedArrayBufferObject
>());
7624 tag
= MailboxTag::SharedArrayBuffer
;
7625 value
.sarb
.buffer
= sab
->rawBufferObject();
7626 value
.sarb
.length
= sab
->byteLength();
7627 value
.sarb
.isHugeMemory
= false;
7628 if (!value
.sarb
.buffer
->addReference()) {
7629 JS_ReportErrorASCII(cx
,
7630 "Reference count overflow on SharedArrayBuffer");
7633 } else if (obj
->is
<WasmMemoryObject
>()) {
7634 // Here we must transmit sab.byteLength() as the length; the SARB has its
7635 // own notion of the length which may be greater, and that's fine.
7636 if (obj
->as
<WasmMemoryObject
>().isShared()) {
7637 Rooted
<SharedArrayBufferObject
*> sab(
7638 cx
, &obj
->as
<WasmMemoryObject
>()
7640 .as
<SharedArrayBufferObject
>());
7641 tag
= MailboxTag::WasmMemory
;
7642 value
.sarb
.buffer
= sab
->rawBufferObject();
7643 value
.sarb
.length
= sab
->byteLength();
7644 value
.sarb
.isHugeMemory
= obj
->as
<WasmMemoryObject
>().isHuge();
7645 if (!value
.sarb
.buffer
->addReference()) {
7646 JS_ReportErrorASCII(cx
,
7647 "Reference count overflow on SharedArrayBuffer");
7651 JS_ReportErrorASCII(cx
, "Invalid argument to SetSharedObject");
7654 } else if (JS::IsWasmModuleObject(obj
)) {
7655 tag
= MailboxTag::WasmModule
;
7656 value
.module
= JS::GetWasmModule(obj
).forget().take();
7658 JS_ReportErrorASCII(cx
, "Invalid argument to SetSharedObject");
7661 } else if (args
.get(0).isNumber()) {
7662 tag
= MailboxTag::Number
;
7663 value
.number
= args
.get(0).toNumber();
7665 } else if (args
.get(0).isNullOrUndefined()) {
7668 JS_ReportErrorASCII(cx
, "Invalid argument to SetSharedObject");
7673 auto mbx
= sharedObjectMailbox
->lock();
7676 case MailboxTag::Empty
:
7677 case MailboxTag::Number
:
7679 case MailboxTag::SharedArrayBuffer
:
7680 case MailboxTag::WasmMemory
:
7681 mbx
->val
.sarb
.buffer
->dropReference();
7683 case MailboxTag::WasmModule
:
7684 mbx
->val
.module
->Release();
7694 args
.rval().setUndefined();
7698 typedef Vector
<uint8_t, 0, SystemAllocPolicy
> Uint8Vector
;
7700 class StreamCacheEntry
: public AtomicRefCounted
<StreamCacheEntry
>,
7701 public JS::OptimizedEncodingListener
{
7702 typedef AtomicRefCounted
<StreamCacheEntry
> AtomicBase
;
7705 ExclusiveData
<Uint8Vector
> optimized_
;
7708 explicit StreamCacheEntry(Uint8Vector
&& original
)
7709 : bytes_(std::move(original
)),
7710 optimized_(mutexid::ShellStreamCacheEntryState
) {}
7712 // Implement JS::OptimizedEncodingListener:
7714 MozExternalRefCountType MOZ_XPCOM_ABI
AddRef() override
{
7715 AtomicBase::AddRef();
7718 MozExternalRefCountType MOZ_XPCOM_ABI
Release() override
{
7719 AtomicBase::Release();
7723 const Uint8Vector
& bytes() const { return bytes_
; }
7725 void storeOptimizedEncoding(const uint8_t* srcBytes
,
7726 size_t srcLength
) override
{
7727 MOZ_ASSERT(srcLength
> 0);
7729 // Tolerate races since a single StreamCacheEntry object can be used as
7730 // the source of multiple streaming compilations.
7731 auto dstBytes
= optimized_
.lock();
7732 if (dstBytes
->length() > 0) {
7736 if (!dstBytes
->resize(srcLength
)) {
7739 memcpy(dstBytes
->begin(), srcBytes
, srcLength
);
7742 bool hasOptimizedEncoding() const { return !optimized_
.lock()->empty(); }
7743 const Uint8Vector
& optimizedEncoding() const {
7744 return optimized_
.lock().get();
7748 typedef RefPtr
<StreamCacheEntry
> StreamCacheEntryPtr
;
7750 class StreamCacheEntryObject
: public NativeObject
{
7751 static const unsigned CACHE_ENTRY_SLOT
= 0;
7752 static const JSClassOps classOps_
;
7753 static const JSPropertySpec properties_
;
7755 static void finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
7756 obj
->as
<StreamCacheEntryObject
>().cache().Release();
7759 static bool cachedGetter(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7760 CallArgs args
= CallArgsFromVp(argc
, vp
);
7761 if (!args
.thisv().isObject() ||
7762 !args
.thisv().toObject().is
<StreamCacheEntryObject
>()) {
7766 StreamCacheEntryObject
& obj
=
7767 args
.thisv().toObject().as
<StreamCacheEntryObject
>();
7768 args
.rval().setBoolean(obj
.cache().hasOptimizedEncoding());
7771 static bool getBuffer(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7772 CallArgs args
= CallArgsFromVp(argc
, vp
);
7773 if (!args
.thisv().isObject() ||
7774 !args
.thisv().toObject().is
<StreamCacheEntryObject
>()) {
7779 args
.thisv().toObject().as
<StreamCacheEntryObject
>().cache().bytes();
7780 auto* buffer
= ArrayBufferObject::createZeroed(cx
, bytes
.length());
7785 memcpy(buffer
->dataPointer(), bytes
.begin(), bytes
.length());
7787 args
.rval().setObject(*buffer
);
7792 static const unsigned RESERVED_SLOTS
= 1;
7793 static const JSClass class_
;
7794 static const JSPropertySpec properties
[];
7796 static bool construct(JSContext
* cx
, unsigned argc
, Value
* vp
) {
7797 CallArgs args
= CallArgsFromVp(argc
, vp
);
7798 if (!args
.requireAtLeast(cx
, "streamCacheEntry", 1)) {
7802 SharedMem
<uint8_t*> ptr
;
7804 if (!args
[0].isObject() ||
7805 !IsBufferSource(&args
[0].toObject(), &ptr
, &numBytes
)) {
7806 RootedObject
callee(cx
, &args
.callee());
7807 ReportUsageErrorASCII(cx
, callee
, "Argument must be an ArrayBuffer");
7812 if (!bytes
.resize(numBytes
)) {
7816 memcpy(bytes
.begin(), ptr
.unwrap(), numBytes
);
7818 RefPtr
<StreamCacheEntry
> cache
=
7819 cx
->new_
<StreamCacheEntry
>(std::move(bytes
));
7824 Rooted
<NativeObject
*> obj(
7825 cx
, NewObjectWithGivenProto
<StreamCacheEntryObject
>(cx
, nullptr));
7829 obj
->initReservedSlot(CACHE_ENTRY_SLOT
,
7830 PrivateValue(cache
.forget().take()));
7832 if (!JS_DefineProperty(cx
, obj
, "cached", cachedGetter
, nullptr, 0)) {
7835 if (!JS_DefineFunction(cx
, obj
, "getBuffer", getBuffer
, 0, 0)) {
7839 args
.rval().setObject(*obj
);
7843 StreamCacheEntry
& cache() const {
7844 return *(StreamCacheEntry
*)getReservedSlot(CACHE_ENTRY_SLOT
).toPrivate();
7848 const JSClassOps
StreamCacheEntryObject::classOps_
= {
7849 nullptr, // addProperty
7850 nullptr, // delProperty
7851 nullptr, // enumerate
7852 nullptr, // newEnumerate
7854 nullptr, // mayResolve
7855 StreamCacheEntryObject::finalize
, // finalize
7857 nullptr, // construct
7861 const JSClass
StreamCacheEntryObject::class_
= {
7862 "StreamCacheEntryObject",
7863 JSCLASS_HAS_RESERVED_SLOTS(StreamCacheEntryObject::RESERVED_SLOTS
) |
7864 JSCLASS_BACKGROUND_FINALIZE
,
7865 &StreamCacheEntryObject::classOps_
};
7867 struct BufferStreamJob
{
7868 Variant
<Uint8Vector
, StreamCacheEntryPtr
> source
;
7870 JS::StreamConsumer
* consumer
;
7872 BufferStreamJob(Uint8Vector
&& source
, JS::StreamConsumer
* consumer
)
7873 : source(AsVariant
<Uint8Vector
>(std::move(source
))), consumer(consumer
) {}
7874 BufferStreamJob(StreamCacheEntry
& source
, JS::StreamConsumer
* consumer
)
7875 : source(AsVariant
<StreamCacheEntryPtr
>(&source
)), consumer(consumer
) {}
7878 struct BufferStreamState
{
7879 Vector
<UniquePtr
<BufferStreamJob
>, 0, SystemAllocPolicy
> jobs
;
7884 BufferStreamState() : delayMillis(1), chunkSize(10), shutdown(false) {}
7886 ~BufferStreamState() { MOZ_ASSERT(jobs
.empty()); }
7889 static ExclusiveWaitableData
<BufferStreamState
>* bufferStreamState
;
7891 static void BufferStreamMain(BufferStreamJob
* job
) {
7892 const uint8_t* bytes
;
7894 JS::OptimizedEncodingListener
* listener
;
7895 if (job
->source
.is
<StreamCacheEntryPtr
>()) {
7896 StreamCacheEntry
& cache
= *job
->source
.as
<StreamCacheEntryPtr
>();
7897 if (cache
.hasOptimizedEncoding()) {
7898 const Uint8Vector
& optimized
= cache
.optimizedEncoding();
7899 job
->consumer
->consumeOptimizedEncoding(optimized
.begin(),
7900 optimized
.length());
7904 bytes
= cache
.bytes().begin();
7905 byteLength
= cache
.bytes().length();
7908 bytes
= job
->source
.as
<Uint8Vector
>().begin();
7909 byteLength
= job
->source
.as
<Uint8Vector
>().length();
7916 if (byteOffset
== byteLength
) {
7917 job
->consumer
->streamEnd(listener
);
7925 auto state
= bufferStreamState
->lock();
7926 shutdown
= state
->shutdown
;
7927 delayMillis
= state
->delayMillis
;
7928 chunkSize
= state
->chunkSize
;
7932 job
->consumer
->streamError(JSMSG_STREAM_CONSUME_ERROR
);
7936 ThisThread::SleepMilliseconds(delayMillis
);
7938 chunkSize
= std::min(chunkSize
, byteLength
- byteOffset
);
7940 if (!job
->consumer
->consumeChunk(bytes
+ byteOffset
, chunkSize
)) {
7944 byteOffset
+= chunkSize
;
7948 auto state
= bufferStreamState
->lock();
7949 size_t jobIndex
= 0;
7950 while (state
->jobs
[jobIndex
].get() != job
) {
7953 job
->thread
.detach(); // quiet assert in ~Thread() called by erase().
7954 state
->jobs
.erase(state
->jobs
.begin() + jobIndex
);
7955 if (state
->jobs
.empty()) {
7956 state
.notify_all(/* jobs empty */);
7960 static bool ConsumeBufferSource(JSContext
* cx
, JS::HandleObject obj
,
7961 JS::MimeType
, JS::StreamConsumer
* consumer
) {
7963 RootedValue
url(cx
);
7964 if (!JS_GetProperty(cx
, obj
, "url", &url
)) {
7967 UniqueChars urlChars
;
7968 if (url
.isString()) {
7969 Rooted
<JSString
*> str(cx
, url
.toString());
7970 urlChars
= JS_EncodeStringToUTF8(cx
, str
);
7976 RootedValue
mapUrl(cx
);
7977 if (!JS_GetProperty(cx
, obj
, "sourceMappingURL", &mapUrl
)) {
7980 UniqueChars mapUrlChars
;
7981 if (mapUrl
.isString()) {
7982 Rooted
<JSString
*> str(cx
, mapUrl
.toString());
7983 mapUrlChars
= JS_EncodeStringToUTF8(cx
, str
);
7989 consumer
->noteResponseURLs(urlChars
.get(), mapUrlChars
.get());
7992 UniquePtr
<BufferStreamJob
> job
;
7994 SharedMem
<uint8_t*> dataPointer
;
7996 if (IsBufferSource(obj
, &dataPointer
, &byteLength
)) {
7998 if (!bytes
.resize(byteLength
)) {
7999 JS_ReportOutOfMemory(cx
);
8003 memcpy(bytes
.begin(), dataPointer
.unwrap(), byteLength
);
8004 job
= cx
->make_unique
<BufferStreamJob
>(std::move(bytes
), consumer
);
8005 } else if (obj
->is
<StreamCacheEntryObject
>()) {
8006 job
= cx
->make_unique
<BufferStreamJob
>(
8007 obj
->as
<StreamCacheEntryObject
>().cache(), consumer
);
8009 JS_ReportErrorASCII(
8011 "shell streaming consumes a buffer source (buffer or view) "
8012 "or StreamCacheEntryObject");
8019 BufferStreamJob
* jobPtr
= job
.get();
8022 auto state
= bufferStreamState
->lock();
8023 MOZ_ASSERT(!state
->shutdown
);
8024 if (!state
->jobs
.append(std::move(job
))) {
8025 JS_ReportOutOfMemory(cx
);
8031 AutoEnterOOMUnsafeRegion oomUnsafe
;
8032 if (!jobPtr
->thread
.init(BufferStreamMain
, jobPtr
)) {
8033 oomUnsafe
.crash("ConsumeBufferSource");
8040 static void ReportStreamError(JSContext
* cx
, size_t errorNumber
) {
8041 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, errorNumber
);
8044 static bool SetBufferStreamParams(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8045 CallArgs args
= CallArgsFromVp(argc
, vp
);
8046 if (!args
.requireAtLeast(cx
, "setBufferStreamParams", 2)) {
8051 if (!ToNumber(cx
, args
[0], &delayMillis
)) {
8056 if (!ToNumber(cx
, args
[1], &chunkSize
)) {
8061 auto state
= bufferStreamState
->lock();
8062 state
->delayMillis
= delayMillis
;
8063 state
->chunkSize
= chunkSize
;
8066 args
.rval().setUndefined();
8070 static void ShutdownBufferStreams() {
8071 auto state
= bufferStreamState
->lock();
8072 state
->shutdown
= true;
8073 while (!state
->jobs
.empty()) {
8074 state
.wait(/* jobs empty */);
8076 state
->jobs
.clearAndFree();
8079 static bool DumpScopeChain(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8080 CallArgs args
= CallArgsFromVp(argc
, vp
);
8081 RootedObject
callee(cx
, &args
.callee());
8083 if (js::SupportDifferentialTesting()) {
8084 ReportUsageErrorASCII(
8085 cx
, callee
, "Function not available in differential testing mode.");
8089 if (args
.length() != 1) {
8090 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8094 if (!args
[0].isObject() ||
8095 !(args
[0].toObject().is
<JSFunction
>() ||
8096 args
[0].toObject().is
<ShellModuleObjectWrapper
>())) {
8097 ReportUsageErrorASCII(
8098 cx
, callee
, "Argument must be an interpreted function or a module");
8102 RootedObject
obj(cx
, &args
[0].toObject());
8103 RootedScript
script(cx
);
8105 if (obj
->is
<JSFunction
>()) {
8106 RootedFunction
fun(cx
, &obj
->as
<JSFunction
>());
8107 if (!fun
->isInterpreted()) {
8108 ReportUsageErrorASCII(cx
, callee
,
8109 "Argument must be an interpreted function");
8112 script
= JSFunction::getOrCreateScript(cx
, fun
);
8117 script
= obj
->as
<ShellModuleObjectWrapper
>().get()->maybeScript();
8119 JS_ReportErrorASCII(cx
, "module does not have an associated script");
8124 script
->bodyScope()->dump();
8126 args
.rval().setUndefined();
8130 // For testing GC marking, blackRoot() and grayRoot() will heap-allocate an
8131 // array whose elements (as well as the array itself) will be marked as roots in
8134 // Note that EnsureGrayRoot() will blacken the returned object, so it will not
8135 // actually end up marked gray until the following GC clears the black bit
8136 // (assuming nothing is holding onto it.)
8138 // The idea is that you can set up a whole graph of objects to be marked gray,
8139 // hanging off of the object returned from grayRoot(). Then you GC to clear the
8140 // black bits and set the gray bits.
8142 // To test grayness, register the objects of interest with addMarkObservers(),
8143 // which takes an Array of objects (which will be marked black at the time
8144 // they're passed in). Their mark bits may be retrieved at any time with
8145 // getMarks(), in the form of an array of strings with each index corresponding
8146 // to the original objects passed to addMarkObservers().
8148 static bool EnsureRootArray(JSContext
* cx
, gc::MarkColor color
, unsigned argc
,
8150 CallArgs args
= CallArgsFromVp(argc
, vp
);
8152 auto priv
= EnsureShellCompartmentPrivate(cx
);
8157 GCPtr
<ArrayObject
*>& root
=
8158 (color
== gc::MarkColor::Black
) ? priv
->blackRoot
: priv
->grayRoot
;
8160 if (!root
&& !(root
= NewTenuredDenseEmptyArray(cx
))) {
8164 // Barrier to enforce the invariant that JS does not touch gray objects.
8165 JSObject
* obj
= root
;
8166 JS::ExposeObjectToActiveJS(obj
);
8168 args
.rval().setObject(*obj
);
8172 static bool EnsureBlackRoot(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8173 return EnsureRootArray(cx
, gc::MarkColor::Black
, argc
, vp
);
8176 static bool EnsureGrayRoot(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8177 return EnsureRootArray(cx
, gc::MarkColor::Gray
, argc
, vp
);
8180 static MarkBitObservers
* EnsureMarkBitObservers(JSContext
* cx
) {
8181 ShellContext
* sc
= GetShellContext(cx
);
8182 if (!sc
->markObservers
) {
8184 cx
->new_
<MarkBitObservers
>(cx
->runtime(), NonshrinkingGCObjectVector());
8188 sc
->markObservers
.reset(observers
);
8190 return sc
->markObservers
.get();
8193 static bool ClearMarkObservers(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8194 CallArgs args
= CallArgsFromVp(argc
, vp
);
8196 auto markObservers
= EnsureMarkBitObservers(cx
);
8197 if (!markObservers
) {
8201 markObservers
->get().clear();
8203 args
.rval().setUndefined();
8207 static bool AddMarkObservers(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8208 CallArgs args
= CallArgsFromVp(argc
, vp
);
8210 auto markObservers
= EnsureMarkBitObservers(cx
);
8211 if (!markObservers
) {
8215 if (!args
.get(0).isObject()) {
8216 JS_ReportErrorASCII(cx
, "argument must be an Array of objects");
8220 RootedObject
observersArg(cx
, &args
[0].toObject());
8222 if (!GetLengthProperty(cx
, observersArg
, &length
)) {
8226 if (length
> UINT32_MAX
) {
8227 JS_ReportErrorASCII(cx
, "Invalid length for observers array");
8231 RootedValue
value(cx
);
8232 RootedObject
object(cx
);
8233 for (uint32_t i
= 0; i
< length
; i
++) {
8234 if (!JS_GetElement(cx
, observersArg
, i
, &value
)) {
8238 if (!value
.isObject()) {
8239 JS_ReportErrorASCII(cx
, "argument must be an Array of objects");
8243 object
= &value
.toObject();
8244 if (gc::IsInsideNursery(object
)) {
8245 // WeakCaches are not swept during a minor GC. To prevent
8246 // nursery-allocated contents from having the mark bits be deceptively
8247 // black until the second GC, they would need to be marked weakly (cf
8248 // NurseryAwareHashMap). It is simpler to evict the nursery to prevent
8249 // nursery objects from being observed.
8250 cx
->runtime()->gc
.evictNursery();
8253 if (!markObservers
->get().append(object
)) {
8258 args
.rval().setInt32(length
);
8262 static bool GetMarks(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8263 CallArgs args
= CallArgsFromVp(argc
, vp
);
8265 auto& observers
= GetShellContext(cx
)->markObservers
;
8267 args
.rval().setUndefined();
8271 size_t length
= observers
->get().length();
8272 Rooted
<ArrayObject
*> ret(cx
, js::NewDenseEmptyArray(cx
));
8277 for (uint32_t i
= 0; i
< length
; i
++) {
8279 JSObject
* obj
= observers
->get()[i
];
8282 } else if (obj
->zone()->isGCPreparing()) {
8285 gc::TenuredCell
* cell
= &obj
->asTenured();
8286 if (cell
->isMarkedGray()) {
8288 } else if (cell
->isMarkedBlack()) {
8294 JSString
* s
= JS_NewStringCopyZ(cx
, color
);
8298 if (!NewbornArrayPush(cx
, ret
, StringValue(s
))) {
8303 args
.rval().setObject(*ret
);
8310 class ShellAutoEntryMonitor
: JS::dbg::AutoEntryMonitor
{
8311 Vector
<UniqueChars
, 1, js::SystemAllocPolicy
> log
;
8313 bool enteredWithoutExit
;
8316 explicit ShellAutoEntryMonitor(JSContext
* cx
)
8317 : AutoEntryMonitor(cx
), oom(false), enteredWithoutExit(false) {}
8319 ~ShellAutoEntryMonitor() { MOZ_ASSERT(!enteredWithoutExit
); }
8321 void Entry(JSContext
* cx
, JSFunction
* function
, JS::HandleValue asyncStack
,
8322 const char* asyncCause
) override
{
8323 MOZ_ASSERT(!enteredWithoutExit
);
8324 enteredWithoutExit
= true;
8326 RootedString
displayId(cx
, JS_GetMaybePartialFunctionDisplayId(function
));
8328 UniqueChars displayIdStr
= JS_EncodeStringToUTF8(cx
, displayId
);
8329 if (!displayIdStr
) {
8330 // We report OOM in buildResult.
8331 cx
->recoverFromOutOfMemory();
8335 oom
= !log
.append(std::move(displayIdStr
));
8339 oom
= !log
.append(DuplicateString("anonymous"));
8342 void Entry(JSContext
* cx
, JSScript
* script
, JS::HandleValue asyncStack
,
8343 const char* asyncCause
) override
{
8344 MOZ_ASSERT(!enteredWithoutExit
);
8345 enteredWithoutExit
= true;
8347 UniqueChars
label(JS_smprintf("eval:%s", JS_GetScriptFilename(script
)));
8348 oom
= !label
|| !log
.append(std::move(label
));
8351 void Exit(JSContext
* cx
) override
{
8352 MOZ_ASSERT(enteredWithoutExit
);
8353 enteredWithoutExit
= false;
8356 bool buildResult(JSContext
* cx
, MutableHandleValue resultValue
) {
8358 JS_ReportOutOfMemory(cx
);
8362 RootedObject
result(cx
, JS::NewArrayObject(cx
, log
.length()));
8367 for (size_t i
= 0; i
< log
.length(); i
++) {
8368 char* name
= log
[i
].get();
8369 RootedString
string(cx
, AtomizeUTF8Chars(cx
, name
, strlen(name
)));
8373 RootedValue
value(cx
, StringValue(string
));
8374 if (!JS_SetElement(cx
, result
, i
, value
)) {
8379 resultValue
.setObject(*result
.get());
8384 } // namespace shell
8387 static bool EntryPoints(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8388 CallArgs args
= CallArgsFromVp(argc
, vp
);
8390 if (args
.length() != 1) {
8391 JS_ReportErrorASCII(cx
, "Wrong number of arguments");
8395 RootedObject
opts(cx
, ToObject(cx
, args
[0]));
8400 // { function: f } --- Call f.
8402 RootedValue
fun(cx
), dummy(cx
);
8404 if (!JS_GetProperty(cx
, opts
, "function", &fun
)) {
8407 if (!fun
.isUndefined()) {
8408 js::shell::ShellAutoEntryMonitor
sarep(cx
);
8409 if (!Call(cx
, UndefinedHandleValue
, fun
, JS::HandleValueArray::empty(),
8413 return sarep
.buildResult(cx
, args
.rval());
8417 // { object: o, property: p, value: v } --- Fetch o[p], or if
8418 // v is present, assign o[p] = v.
8420 RootedValue
objectv(cx
), propv(cx
), valuev(cx
);
8422 if (!JS_GetProperty(cx
, opts
, "object", &objectv
) ||
8423 !JS_GetProperty(cx
, opts
, "property", &propv
))
8425 if (!objectv
.isUndefined() && !propv
.isUndefined()) {
8426 RootedObject
object(cx
, ToObject(cx
, objectv
));
8431 RootedString
string(cx
, ToString(cx
, propv
));
8436 if (!JS_StringToId(cx
, string
, &id
)) {
8440 if (!JS_GetProperty(cx
, opts
, "value", &valuev
)) {
8444 js::shell::ShellAutoEntryMonitor
sarep(cx
);
8446 if (!valuev
.isUndefined()) {
8447 if (!JS_SetPropertyById(cx
, object
, id
, valuev
)) {
8451 if (!JS_GetPropertyById(cx
, object
, id
, &valuev
)) {
8456 return sarep
.buildResult(cx
, args
.rval());
8460 // { ToString: v } --- Apply JS::ToString to v.
8464 if (!JS_GetProperty(cx
, opts
, "ToString", &v
)) {
8467 if (!v
.isUndefined()) {
8468 js::shell::ShellAutoEntryMonitor
sarep(cx
);
8469 if (!JS::ToString(cx
, v
)) {
8472 return sarep
.buildResult(cx
, args
.rval());
8476 // { ToNumber: v } --- Apply JS::ToNumber to v.
8481 if (!JS_GetProperty(cx
, opts
, "ToNumber", &v
)) {
8484 if (!v
.isUndefined()) {
8485 js::shell::ShellAutoEntryMonitor
sarep(cx
);
8486 if (!JS::ToNumber(cx
, v
, &dummy
)) {
8489 return sarep
.buildResult(cx
, args
.rval());
8493 // { eval: code } --- Apply ToString and then Evaluate to code.
8495 RootedValue
code(cx
), dummy(cx
);
8497 if (!JS_GetProperty(cx
, opts
, "eval", &code
)) {
8500 if (!code
.isUndefined()) {
8501 RootedString
codeString(cx
, ToString(cx
, code
));
8506 AutoStableStringChars
linearChars(cx
);
8507 if (!linearChars
.initTwoByte(cx
, codeString
)) {
8510 JS::SourceText
<char16_t
> srcBuf
;
8511 if (!srcBuf
.initMaybeBorrowed(cx
, linearChars
)) {
8515 CompileOptions
options(cx
);
8516 options
.setIntroductionType("entryPoint eval")
8517 .setFileAndLine("entryPoint eval", 1);
8519 js::shell::ShellAutoEntryMonitor
sarep(cx
);
8520 if (!JS::Evaluate(cx
, options
, srcBuf
, &dummy
)) {
8523 return sarep
.buildResult(cx
, args
.rval());
8527 JS_ReportErrorASCII(cx
, "bad 'params' object");
8532 static bool WasmTextToBinary(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8533 CallArgs args
= CallArgsFromVp(argc
, vp
);
8534 RootedObject
callee(cx
, &args
.callee());
8536 if (!args
.requireAtLeast(cx
, "wasmTextToBinary", 1)) {
8540 if (!args
[0].isString()) {
8541 ReportUsageErrorASCII(cx
, callee
, "First argument must be a String");
8545 size_t textLen
= args
[0].toString()->length();
8547 AutoStableStringChars
twoByteChars(cx
);
8548 if (!twoByteChars
.initTwoByte(cx
, args
[0].toString())) {
8554 if (!wasm::TextToBinary(twoByteChars
.twoByteChars(), textLen
, &bytes
,
8556 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr, JSMSG_WASM_TEXT_FAIL
,
8557 error
.get() ? error
.get() : "out of memory");
8561 RootedObject
binary(cx
, JS_NewUint8Array(cx
, bytes
.length()));
8566 memcpy(binary
->as
<TypedArrayObject
>().dataPointerUnshared(), bytes
.begin(),
8569 args
.rval().setObject(*binary
);
8573 # ifndef __AFL_HAVE_MANUAL_CONTROL
8574 # define __AFL_LOOP(x) true
8577 static bool WasmLoop(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8578 CallArgs args
= CallArgsFromVp(argc
, vp
);
8579 RootedObject
callee(cx
, &args
.callee());
8581 if (args
.length() < 1 || args
.length() > 2) {
8582 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8586 if (!args
[0].isString()) {
8587 ReportUsageErrorASCII(cx
, callee
, "First argument must be a String");
8591 RootedObject
importObj(cx
);
8592 if (!args
.get(1).isUndefined()) {
8593 if (!args
.get(1).isObject()) {
8594 ReportUsageErrorASCII(cx
, callee
,
8595 "Second argument, if present, must be an Object");
8598 importObj
= &args
[1].toObject();
8601 RootedString
givenPath(cx
, args
[0].toString());
8602 RootedString
filename(cx
, ResolvePath(cx
, givenPath
, RootRelative
));
8607 while (__AFL_LOOP(1000)) {
8608 Rooted
<JSObject
*> ret(cx
, FileAsTypedArray(cx
, filename
));
8613 Rooted
<TypedArrayObject
*> typedArray(cx
, &ret
->as
<TypedArrayObject
>());
8614 Rooted
<WasmInstanceObject
*> instanceObj(cx
);
8615 if (!wasm::Eval(cx
, typedArray
, importObj
, &instanceObj
)) {
8616 // Clear any pending exceptions, we don't care about them
8617 cx
->clearPendingException();
8621 # ifdef __AFL_HAVE_MANUAL_CONTROL // to silence unreachable code warning
8627 static constexpr uint32_t DOM_OBJECT_SLOT
= 0;
8628 static constexpr uint32_t DOM_OBJECT_SLOT2
= 1;
8630 static const JSClass
* GetDomClass();
8632 static JSObject
* GetDOMPrototype(JSContext
* cx
, JSObject
* global
);
8634 static const JSClass TransplantableDOMObjectClass
= {
8635 "TransplantableDOMObject",
8636 JSCLASS_IS_DOMJSCLASS
| JSCLASS_HAS_RESERVED_SLOTS(1)};
8638 static const JSClass TransplantableDOMProxyObjectClass
=
8639 PROXY_CLASS_DEF("TransplantableDOMProxyObject",
8640 JSCLASS_IS_DOMJSCLASS
| JSCLASS_HAS_RESERVED_SLOTS(1));
8642 class TransplantableDOMProxyHandler final
: public ForwardingProxyHandler
{
8644 static const TransplantableDOMProxyHandler singleton
;
8645 static const char family
;
8647 constexpr TransplantableDOMProxyHandler() : ForwardingProxyHandler(&family
) {}
8649 // These two proxy traps are called in |js::DeadProxyTargetValue|, which in
8650 // turn is called when nuking proxies. Because this proxy can temporarily be
8651 // without an object in its private slot, see |EnsureExpandoObject|, the
8652 // default implementation inherited from ForwardingProxyHandler can't be used,
8653 // since it tries to derive the callable/constructible value from the target.
8654 bool isCallable(JSObject
* obj
) const override
{ return false; }
8655 bool isConstructor(JSObject
* obj
) const override
{ return false; }
8657 // Simplified implementation of |DOMProxyHandler::GetAndClearExpandoObject|.
8658 static JSObject
* GetAndClearExpandoObject(JSObject
* obj
) {
8659 const Value
& v
= GetProxyPrivate(obj
);
8660 if (v
.isUndefined()) {
8664 JSObject
* expandoObject
= &v
.toObject();
8665 SetProxyPrivate(obj
, UndefinedValue());
8666 return expandoObject
;
8669 // Simplified implementation of |DOMProxyHandler::EnsureExpandoObject|.
8670 static JSObject
* EnsureExpandoObject(JSContext
* cx
, JS::HandleObject obj
) {
8671 const Value
& v
= GetProxyPrivate(obj
);
8673 return &v
.toObject();
8675 MOZ_ASSERT(v
.isUndefined());
8677 JSObject
* expando
= JS_NewObjectWithGivenProto(cx
, nullptr, nullptr);
8681 SetProxyPrivate(obj
, ObjectValue(*expando
));
8686 const TransplantableDOMProxyHandler
TransplantableDOMProxyHandler::singleton
;
8687 const char TransplantableDOMProxyHandler::family
= 0;
8689 enum TransplantObjectSlots
{
8690 TransplantSourceObject
= 0,
8693 static bool TransplantObject(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8694 CallArgs args
= CallArgsFromVp(argc
, vp
);
8695 RootedFunction
callee(cx
, &args
.callee().as
<JSFunction
>());
8697 if (args
.length() != 1 || !args
[0].isObject()) {
8698 JS_ReportErrorASCII(cx
, "transplant() must be called with an object");
8702 // |newGlobal| needs to be a GlobalObject.
8703 RootedObject
newGlobal(
8704 cx
, js::CheckedUnwrapDynamic(&args
[0].toObject(), cx
,
8705 /* stopAtWindowProxy = */ false));
8707 ReportAccessDenied(cx
);
8710 if (!JS_IsGlobalObject(newGlobal
)) {
8711 JS_ReportErrorNumberASCII(
8712 cx
, GetErrorMessage
, nullptr, JSMSG_UNEXPECTED_TYPE
,
8713 "\"global\" passed to transplant()", "not a global object");
8717 const Value
& reserved
=
8718 GetFunctionNativeReserved(callee
, TransplantSourceObject
);
8719 RootedObject
source(cx
, CheckedUnwrapStatic(&reserved
.toObject()));
8721 ReportAccessDenied(cx
);
8724 if (JS_IsDeadWrapper(source
)) {
8725 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_DEAD_OBJECT
);
8728 MOZ_ASSERT(source
->getClass()->isDOMClass());
8730 // The following steps aim to replicate the behavior of UpdateReflectorGlobal
8731 // in dom/bindings/BindingUtils.cpp. In detail:
8732 // 1. Check the recursion depth using checkConservative.
8733 // 2. Enter the target compartment.
8734 // 3. Clone the source object using JS_CloneObject.
8735 // 4. Check if new wrappers can be created if source and target are in
8736 // different compartments.
8737 // 5. Copy all properties from source to a temporary holder object.
8738 // 6. Actually transplant the object.
8739 // 7. And finally copy the properties back to the source object.
8741 // As an extension to the algorithm in UpdateReflectorGlobal, we also allow
8742 // to transplant an object into the same compartment as the source object to
8743 // cover all operations supported by JS_TransplantObject.
8745 AutoCheckRecursionLimit
recursion(cx
);
8746 if (!recursion
.checkConservative(cx
)) {
8750 bool isProxy
= IsProxy(source
);
8751 RootedObject
expandoObject(cx
);
8754 TransplantableDOMProxyHandler::GetAndClearExpandoObject(source
);
8757 JSAutoRealm
ar(cx
, newGlobal
);
8759 RootedObject
proto(cx
);
8760 if (JS::GetClass(source
) == GetDomClass()) {
8761 proto
= GetDOMPrototype(cx
, newGlobal
);
8763 proto
= JS::GetRealmObjectPrototype(cx
);
8769 RootedObject
target(cx
, JS_CloneObject(cx
, source
, proto
));
8774 if (JS::GetCompartment(source
) != JS::GetCompartment(target
) &&
8775 !AllowNewWrapper(JS::GetCompartment(source
), target
)) {
8776 JS_ReportErrorASCII(cx
, "Cannot transplant into nuked compartment");
8780 RootedObject
copyFrom(cx
, isProxy
? expandoObject
: source
);
8781 RootedObject
propertyHolder(cx
,
8782 JS_NewObjectWithGivenProto(cx
, nullptr, nullptr));
8783 if (!propertyHolder
) {
8787 if (!JS_CopyOwnPropertiesAndPrivateFields(cx
, propertyHolder
, copyFrom
)) {
8791 JS::SetReservedSlot(target
, DOM_OBJECT_SLOT
,
8792 JS::GetReservedSlot(source
, DOM_OBJECT_SLOT
));
8793 JS::SetReservedSlot(source
, DOM_OBJECT_SLOT
, JS::PrivateValue(nullptr));
8794 if (JS::GetClass(source
) == GetDomClass()) {
8795 JS::SetReservedSlot(target
, DOM_OBJECT_SLOT2
,
8796 JS::GetReservedSlot(source
, DOM_OBJECT_SLOT2
));
8797 JS::SetReservedSlot(source
, DOM_OBJECT_SLOT2
, UndefinedValue());
8800 source
= JS_TransplantObject(cx
, source
, target
);
8805 RootedObject
copyTo(cx
);
8807 copyTo
= TransplantableDOMProxyHandler::EnsureExpandoObject(cx
, source
);
8814 if (!JS_CopyOwnPropertiesAndPrivateFields(cx
, copyTo
, propertyHolder
)) {
8818 args
.rval().setUndefined();
8822 static bool TransplantableObject(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8823 CallArgs args
= CallArgsFromVp(argc
, vp
);
8824 RootedObject
callee(cx
, &args
.callee());
8826 if (args
.length() > 1) {
8827 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8831 bool createProxy
= false;
8832 RootedObject
source(cx
);
8833 if (args
.length() == 1 && !args
[0].isUndefined()) {
8834 if (!args
[0].isObject()) {
8835 ReportUsageErrorASCII(cx
, callee
, "Argument must be an object");
8839 RootedObject
options(cx
, &args
[0].toObject());
8840 RootedValue
value(cx
);
8842 if (!JS_GetProperty(cx
, options
, "proxy", &value
)) {
8845 createProxy
= JS::ToBoolean(value
);
8847 if (!JS_GetProperty(cx
, options
, "object", &value
)) {
8850 if (!value
.isUndefined()) {
8851 if (!value
.isObject()) {
8852 ReportUsageErrorASCII(cx
, callee
, "'object' option must be an object");
8856 source
= &value
.toObject();
8857 if (JS::GetClass(source
) != GetDomClass()) {
8858 ReportUsageErrorASCII(cx
, callee
, "Object not a FakeDOMObject");
8862 // |source| must be a tenured object to be transplantable.
8863 if (gc::IsInsideNursery(source
)) {
8866 MOZ_ASSERT(!gc::IsInsideNursery(source
),
8867 "Live objects should be tenured after one GC, because "
8868 "the nursery has only a single generation");
8875 source
= NewBuiltinClassInstance(cx
, &TransplantableDOMObjectClass
,
8881 JS::SetReservedSlot(source
, DOM_OBJECT_SLOT
, JS::PrivateValue(nullptr));
8883 JSObject
* expando
= JS_NewPlainObject(cx
);
8887 RootedValue
expandoVal(cx
, ObjectValue(*expando
));
8889 ProxyOptions options
;
8890 options
.setClass(&TransplantableDOMProxyObjectClass
);
8891 options
.setLazyProto(true);
8893 source
= NewProxyObject(cx
, &TransplantableDOMProxyHandler::singleton
,
8894 expandoVal
, nullptr, options
);
8899 SetProxyReservedSlot(source
, DOM_OBJECT_SLOT
, JS::PrivateValue(nullptr));
8903 jsid emptyId
= NameToId(cx
->names().empty_
);
8904 RootedObject
transplant(
8905 cx
, NewFunctionByIdWithReserved(cx
, TransplantObject
, 0, 0, emptyId
));
8910 SetFunctionNativeReserved(transplant
, TransplantSourceObject
,
8911 ObjectValue(*source
));
8913 RootedObject
result(cx
, JS_NewPlainObject(cx
));
8918 RootedValue
sourceVal(cx
, ObjectValue(*source
));
8919 RootedValue
transplantVal(cx
, ObjectValue(*transplant
));
8920 if (!JS_DefineProperty(cx
, result
, "object", sourceVal
, 0) ||
8921 !JS_DefineProperty(cx
, result
, "transplant", transplantVal
, 0)) {
8925 args
.rval().setObject(*result
);
8930 static bool DebugGetQueuedJobs(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8931 CallArgs args
= CallArgsFromVp(argc
, vp
);
8933 JSObject
* jobs
= js::GetJobsInInternalJobQueue(cx
);
8938 args
.rval().setObject(*jobs
);
8943 #ifdef FUZZING_INTERFACES
8945 size_t gluesmith(uint8_t* data
, size_t size
, uint8_t* out
, size_t maxsize
);
8948 static bool GetWasmSmithModule(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8949 CallArgs args
= CallArgsFromVp(argc
, vp
);
8950 RootedObject
callee(cx
, &args
.callee());
8952 if (args
.length() != 1) {
8953 ReportUsageErrorASCII(cx
, callee
, "Wrong number of arguments");
8957 if (!args
[0].isObject() || !args
[0].toObject().is
<ArrayBufferObject
>()) {
8958 ReportUsageErrorASCII(cx
, callee
, "Argument must be ArrayBuffer.");
8962 ArrayBufferObject
* arrayBuffer
= &args
[0].toObject().as
<ArrayBufferObject
>();
8963 size_t length
= arrayBuffer
->byteLength();
8964 uint8_t* data
= arrayBuffer
->dataPointer();
8966 const size_t maxModuleSize
= 4096;
8967 uint8_t tmp
[maxModuleSize
];
8969 size_t outSize
= gluesmith(data
, length
, tmp
, maxModuleSize
);
8971 JS_ReportErrorASCII(cx
, "Generated module is too large.");
8975 JS::Rooted
<JSObject
*> outArr(cx
, JS_NewUint8ClampedArray(cx
, outSize
));
8981 JS::AutoCheckCannotGC nogc
;
8983 uint8_t* data
= JS_GetUint8ClampedArrayData(outArr
, &isShared
, nogc
);
8984 MOZ_RELEASE_ASSERT(!isShared
);
8985 memcpy(data
, tmp
, outSize
);
8988 args
.rval().setObject(*outArr
);
8994 static bool IsValidJSON(JSContext
* cx
, unsigned argc
, Value
* vp
) {
8995 CallArgs args
= CallArgsFromVp(argc
, vp
);
8996 RootedObject
callee(cx
, &args
.callee());
8998 if (!args
.get(0).isString()) {
8999 ReportUsageErrorASCII(cx
, callee
, "First argument must be a String");
9003 JS::Rooted
<JSLinearString
*> input(cx
, args
[0].toString()->ensureLinear(cx
));
9009 if (input
->hasLatin1Chars()) {
9010 JS::AutoCheckCannotGC nogc
;
9011 result
= JS::IsValidJSON(input
->latin1Chars(nogc
), input
->length());
9013 JS::AutoCheckCannotGC nogc
;
9014 result
= JS::IsValidJSON(input
->twoByteChars(nogc
), input
->length());
9017 args
.rval().setBoolean(result
);
9022 static const JSFunctionSpecWithHelp shell_functions
[] = {
9023 JS_FN_HELP("options", Options
, 0, 0,
9024 "options([option ...])",
9025 " Get or toggle JavaScript options."),
9027 JS_FN_HELP("load", Load
, 1, 0,
9028 "load(['foo.js' ...])",
9029 " Load files named by string arguments. Filename is relative to the\n"
9030 " current working directory."),
9032 JS_FN_HELP("loadRelativeToScript", LoadScriptRelativeToScript
, 1, 0,
9033 "loadRelativeToScript(['foo.js' ...])",
9034 " Load files named by string arguments. Filename is relative to the\n"
9035 " calling script."),
9037 JS_FN_HELP("evaluate", Evaluate
, 2, 0,
9038 "evaluate(code[, options])",
9039 " Evaluate code as though it were the contents of a file.\n"
9040 " options is an optional object that may have these properties:\n"
9041 " isRunOnce: use the isRunOnce compiler option (default: false)\n"
9042 " noScriptRval: use the no-script-rval compiler option (default: false)\n"
9043 " fileName: filename for error messages and debug info\n"
9044 " skipFileNameValidation: skip the filename-validation callback\n"
9045 " lineNumber: starting line number for error messages and debug info\n"
9046 " columnNumber: starting column number for error messages and debug info\n"
9047 " global: global in which to execute the code\n"
9048 " newContext: if true, create and use a new cx (default: false)\n"
9049 " catchTermination: if true, catch termination (failure without\n"
9050 " an exception value, as for slow scripts or out-of-memory)\n"
9051 " and return 'terminated'\n"
9052 " element: if present with value |v|, convert |v| to an object |o| and\n"
9053 " mark the source as being attached to the DOM element |o|. If the\n"
9054 " property is omitted or |v| is null, don't attribute the source to\n"
9055 " any DOM element.\n"
9056 " elementAttributeName: if present and not undefined, the name of\n"
9057 " property of 'element' that holds this code. This is what\n"
9058 " Debugger.Source.prototype.elementAttributeName returns.\n"
9059 " sourceMapURL: if present with value |v|, convert |v| to a string, and\n"
9060 " provide that as the code's source map URL. If omitted, attach no\n"
9061 " source map URL to the code (although the code may provide one itself,\n"
9062 " via a //#sourceMappingURL comment).\n"
9063 " sourceIsLazy: if present and true, indicates that, after compilation, \n"
9064 " script source should not be cached by the JS engine and should be \n"
9065 " lazily loaded from the embedding as-needed.\n"
9066 " forceFullParse: if present and true, disable syntax-parse.\n"
9067 " loadBytecode: if true, and if the source is a CacheEntryObject,\n"
9068 " the bytecode would be loaded and decoded from the cache entry instead\n"
9069 " of being parsed, then it would be executed as usual.\n"
9070 " saveIncrementalBytecode: if true, and if the source is a\n"
9071 " CacheEntryObject, the bytecode would be incrementally encoded and\n"
9072 " saved into the cache entry.\n"
9073 " execute: if false, do not execute the script, but do parse and/or\n"
9075 " assertEqBytecode: if true, and if both loadBytecode and either\n"
9076 " saveIncrementalBytecode is true, then the loaded\n"
9077 " bytecode and the encoded bytecode are compared.\n"
9078 " and an assertion is raised if they differ.\n"
9079 " envChainObject: object to put on the scope chain, with its fields added\n"
9080 " as var bindings, akin to how elements are added to the environment in\n"
9081 " event handlers in Gecko.\n"
9084 JS_FN_HELP("run", Run
, 1, 0,
9086 " Run the file named by the first argument, returning the number of\n"
9087 " of milliseconds spent compiling and executing it."),
9089 JS_FN_HELP("readline", ReadLine
, 0, 0,
9091 " Read a single line from stdin."),
9093 JS_FN_HELP("readlineBuf", ReadLineBuf
, 1, 0,
9094 "readlineBuf([ buf ])",
9095 " Emulate readline() on the specified string. The first call with a string\n"
9096 " argument sets the source buffer. Subsequent calls without an argument\n"
9097 " then read from this buffer line by line.\n"),
9099 JS_FN_HELP("print", Print
, 0, 0,
9101 " Evaluate and print expressions to stdout."),
9103 JS_FN_HELP("printErr", PrintErr
, 0, 0,
9104 "printErr([exp ...])",
9105 " Evaluate and print expressions to stderr."),
9107 JS_FN_HELP("putstr", PutStr
, 0, 0,
9109 " Evaluate and print expression without newline."),
9111 JS_FN_HELP("dateNow", Now
, 0, 0,
9113 " Return the current time with sub-ms precision."),
9115 JS_FN_HELP("help", Help
, 0, 0,
9116 "help([function or interface object or /pattern/])",
9117 " Display usage and help messages."),
9119 JS_FN_HELP("quit", Quit
, 0, 0,
9121 " Quit the shell."),
9123 JS_FN_HELP("assertEq", AssertEq
, 2, 0,
9124 "assertEq(actual, expected[, msg])",
9125 " Throw if the first two arguments are not the same (both +0 or both -0,\n"
9126 " both NaN, or non-zero and ===)."),
9128 JS_FN_HELP("startTimingMutator", StartTimingMutator
, 0, 0,
9129 "startTimingMutator()",
9130 " Start accounting time to mutator vs GC."),
9132 JS_FN_HELP("stopTimingMutator", StopTimingMutator
, 0, 0,
9133 "stopTimingMutator()",
9134 " Stop accounting time to mutator vs GC and dump the results."),
9136 JS_FN_HELP("throwError", ThrowError
, 0, 0,
9138 " Throw an error from JS_ReportError."),
9140 JS_FN_HELP("createErrorReport", CreateErrorReport
, 1, 0,
9141 "createErrorReport(value)",
9142 " Create an JS::ErrorReportBuilder object from the given value and serialize\n"
9145 #if defined(DEBUG) || defined(JS_JITSPEW)
9146 JS_FN_HELP("disassemble", DisassembleToString
, 1, 0,
9147 "disassemble([fun/code])",
9148 " Return the disassembly for the given function or code.\n"
9149 " All disassembly functions take these options as leading string arguments:\n"
9150 " \"-r\" (disassemble recursively)\n"
9151 " \"-l\" (show line numbers)\n"
9152 " \"-S\" (omit source notes)"),
9154 JS_FN_HELP("dis", Disassemble
, 1, 0,
9156 " Disassemble functions into bytecodes."),
9158 JS_FN_HELP("disfile", DisassFile
, 1, 0,
9159 "disfile('foo.js')",
9160 " Disassemble script file into bytecodes.\n"),
9162 JS_FN_HELP("dissrc", DisassWithSrc
, 1, 0,
9163 "dissrc([fun/code])",
9164 " Disassemble functions with source lines."),
9166 JS_FN_HELP("notes", Notes
, 1, 0,
9168 " Show source notes for functions."),
9170 JS_FN_HELP("stackDump", StackDump
, 3, 0,
9171 "stackDump(showArgs, showLocals, showThisProps)",
9172 " Tries to print a lot of information about the current stack. \n"
9173 " Similar to the DumpJSStack() function in the browser."),
9177 JS_FN_HELP("getslx", GetSLX
, 1, 0,
9179 " Get script line extent."),
9181 JS_FN_HELP("evalcx", EvalInContext
, 1, 0,
9183 " Evaluate s in optional sandbox object o.\n"
9184 " if (s == '' && !o) return new o with eager standard classes\n"
9185 " if (s == 'lazy' && !o) return new o with lazy standard classes"),
9187 JS_FN_HELP("evalInWorker", EvalInWorker
, 1, 0,
9188 "evalInWorker(str)",
9189 " Evaluate 'str' in a separate thread with its own runtime.\n"),
9191 JS_FN_HELP("getSharedObject", GetSharedObject
, 0, 0,
9192 "getSharedObject()",
9193 " Retrieve the shared object from the cross-worker mailbox.\n"
9194 " The object retrieved may not be identical to the object that was\n"
9195 " installed, but it references the same shared memory.\n"
9196 " getSharedObject performs an ordering memory barrier.\n"),
9198 JS_FN_HELP("setSharedObject", SetSharedObject
, 0, 0,
9199 "setSharedObject(obj)",
9200 " Install the shared object in the cross-worker mailbox. The object\n"
9201 " may be null. setSharedObject performs an ordering memory barrier.\n"),
9203 JS_FN_HELP("getSharedArrayBuffer", GetSharedObject
, 0, 0,
9204 "getSharedArrayBuffer()",
9205 " Obsolete alias for getSharedObject().\n"),
9207 JS_FN_HELP("setSharedArrayBuffer", SetSharedObject
, 0, 0,
9208 "setSharedArrayBuffer(obj)",
9209 " Obsolete alias for setSharedObject(obj).\n"),
9211 JS_FN_HELP("shapeOf", ShapeOf
, 1, 0,
9213 " Get the shape of obj (an implementation detail)."),
9216 JS_FN_HELP("arrayInfo", ArrayInfo
, 1, 0,
9217 "arrayInfo(a1, a2, ...)",
9218 " Report statistics about arrays."),
9221 JS_FN_HELP("sleep", Sleep_fn
, 1, 0,
9223 " Sleep for dt seconds."),
9225 JS_FN_HELP("parseModule", ParseModule
, 1, 0,
9226 "parseModule(code)",
9227 " Parses source text as a module and returns a ModuleObject wrapper object."),
9229 JS_FN_HELP("instantiateModuleStencil", InstantiateModuleStencil
, 1, 0,
9230 "instantiateModuleStencil(stencil, [options])",
9231 " Instantiates the given stencil as module, and return the module object."),
9233 JS_FN_HELP("instantiateModuleStencilXDR", InstantiateModuleStencilXDR
, 1, 0,
9234 "instantiateModuleStencilXDR(stencil, [options])",
9235 " Reads the given stencil XDR object, instantiates the stencil as module, and"
9236 " return the module object."),
9238 JS_FN_HELP("registerModule", RegisterModule
, 2, 0,
9239 "registerModule(specifier, module)",
9240 " Register a module with the module loader, so that subsequent import from\n"
9241 " |specifier| will resolve to |module|. Returns |module|."),
9243 JS_FN_HELP("clearModules", ClearModules
, 0, 0,
9245 " Clear knowledge of all loaded modules."),
9247 JS_FN_HELP("moduleLink", ModuleLink
, 1, 0,
9248 "moduleLink(moduleOjbect)",
9249 " Link a module graph, performing the spec's Link method."),
9251 JS_FN_HELP("moduleEvaluate", ModuleEvaluate
, 1, 0,
9252 "moduleEvaluate(moduleOjbect)",
9253 " Evaluate a module graph, performing the spec's Evaluate method."),
9255 JS_FN_HELP("getModuleEnvironmentNames", GetModuleEnvironmentNames
, 1, 0,
9256 "getModuleEnvironmentNames(module)",
9257 " Get the list of a module environment's bound names for a specified module.\n"),
9259 JS_FN_HELP("getModuleEnvironmentValue", GetModuleEnvironmentValue
, 2, 0,
9260 "getModuleEnvironmentValue(module, name)",
9261 " Get the value of a bound name in a module environment.\n"),
9263 JS_FN_HELP("dumpStencil", DumpStencil
, 1, 0,
9264 "dumpStencil(code, [options])",
9265 " Parses a string and returns string that represents stencil.\n"
9266 " If present, |options| may have properties saying how the code should be\n"
9268 " module: if present and true, compile the source as module.\n"
9269 " smoosh: if present and true, use SmooshMonkey.\n"
9270 " CompileOptions-related properties of evaluate function's option can also\n"
9273 JS_FN_HELP("parse", Parse
, 1, 0,
9274 "parse(code, [options])",
9275 " Parses a string, potentially throwing. If present, |options| may\n"
9276 " have properties saying how the code should be compiled:\n"
9277 " module: if present and true, compile the source as module.\n"
9278 " smoosh: if present and true, use SmooshMonkey.\n"
9279 " CompileOptions-related properties of evaluate function's option can also\n"
9280 " be used. except forceFullParse. This function always use full parse."),
9282 JS_FN_HELP("syntaxParse", SyntaxParse
, 1, 0,
9283 "syntaxParse(code)",
9284 " Check the syntax of a string, returning success value"),
9286 JS_FN_HELP("offThreadCompileModuleToStencil", OffThreadCompileModuleToStencil
, 1, 0,
9287 "offThreadCompileModuleToStencil(code[, options])",
9288 " Compile |code| on a helper thread, returning a job ID. To wait for the\n"
9289 " compilation to finish and and get the module stencil object call\n"
9290 " |finishOffThreadStencil| passing the job ID."),
9292 JS_FN_HELP("offThreadDecodeStencil", OffThreadDecodeStencil
, 1, 0,
9293 "offThreadDecodeStencil(cacheEntry[, options])",
9294 " Decode |code| on a helper thread, returning a job ID. To wait for the\n"
9295 " decoding to finish and run the code, call |finishOffThreadStencil| passing\n"
9296 " the job ID. If present, |options| may have properties saying how the code\n"
9297 " should be compiled (see also offThreadCompileToStencil)."),
9299 JS_FN_HELP("offThreadCompileToStencil", OffThreadCompileToStencil
, 1, 0,
9300 "offThreadCompileToStencil(code[, options])",
9301 " Compile |code| on a helper thread, returning a job ID. To wait for the\n"
9302 " compilation to finish and get the stencil object, call\n"
9303 " |finishOffThreadStencil| passing the job ID. If present, \n"
9304 " |options| may have properties saying how the code should be compiled:\n"
9305 " noScriptRval: use the no-script-rval compiler option (default: false)\n"
9306 " fileName: filename for error messages and debug info\n"
9307 " lineNumber: starting line number for error messages and debug info\n"
9308 " columnNumber: starting column number for error messages and debug info\n"
9309 " element: if present with value |v|, convert |v| to an object |o| and\n"
9310 " mark the source as being attached to the DOM element |o|. If the\n"
9311 " property is omitted or |v| is null, don't attribute the source to\n"
9312 " any DOM element.\n"
9313 " elementAttributeName: if present and not undefined, the name of\n"
9314 " property of 'element' that holds this code. This is what\n"
9315 " Debugger.Source.prototype.elementAttributeName returns."),
9317 JS_FN_HELP("finishOffThreadStencil", FinishOffThreadStencil
, 0, 0,
9318 "finishOffThreadStencil([jobID])",
9319 " Wait for an off-thread compilation or decode job to complete. The job ID\n"
9320 " can be ommitted if there is only one job pending. If an error occurred,\n"
9321 " throw the appropriate exception; otherwise, return the stencil object,"
9322 " that can be passed to |evalStencil|."),
9324 JS_FN_HELP("timeout", Timeout
, 1, 0,
9325 "timeout([seconds], [func])",
9326 " Get/Set the limit in seconds for the execution time for the current context.\n"
9327 " When the timeout expires the current interrupt callback is invoked.\n"
9328 " The timeout is used just once. If the callback returns a falsy value, the\n"
9329 " script is aborted. A negative value for seconds (this is the default) cancels\n"
9330 " any pending timeout.\n"
9331 " If a second argument is provided, it is installed as the interrupt handler,\n"
9332 " exactly as if by |setInterruptCallback|.\n"),
9334 JS_FN_HELP("interruptIf", InterruptIf
, 1, 0,
9335 "interruptIf(cond)",
9336 " Requests interrupt callback if cond is true. If a callback function is set via\n"
9337 " |timeout| or |setInterruptCallback|, it will be called. No-op otherwise."),
9339 JS_FN_HELP("invokeInterruptCallback", InvokeInterruptCallbackWrapper
, 0, 0,
9340 "invokeInterruptCallback(fun)",
9341 " Forcefully set the interrupt flag and invoke the interrupt handler. If a\n"
9342 " callback function is set via |timeout| or |setInterruptCallback|, it will\n"
9343 " be called. Before returning, fun is called with the return value of the\n"
9344 " interrupt handler."),
9346 JS_FN_HELP("setInterruptCallback", SetInterruptCallback
, 1, 0,
9347 "setInterruptCallback(func)",
9348 " Sets func as the interrupt callback function.\n"
9349 " Calling this function will replace any callback set by |timeout|.\n"
9350 " If the callback returns a falsy value, the script is aborted.\n"),
9352 JS_FN_HELP("setJitCompilerOption", SetJitCompilerOption
, 2, 0,
9353 "setJitCompilerOption(<option>, <number>)",
9354 " Set a compiler option indexed in JSCompileOption enum to a number.\n"),
9356 JS_FN_HELP("interruptRegexp", InterruptRegexp
, 2, 0,
9357 "interruptRegexp(<regexp>, <string>)",
9358 " Interrrupt the execution of regular expression.\n"),
9360 JS_FN_HELP("checkRegExpSyntax", CheckRegExpSyntax
, 1, 0,
9361 "checkRegExpSyntax(<string>)",
9362 " Return undefined if the string parses as a RegExp. If the string does not\n"
9363 " parse correctly, return the SyntaxError that occurred."),
9365 JS_FN_HELP("enableLastWarning", EnableLastWarning
, 0, 0,
9366 "enableLastWarning()",
9367 " Enable storing the last warning."),
9368 JS_FN_HELP("disableLastWarning", DisableLastWarning
, 0, 0,
9369 "disableLastWarning()",
9370 " Disable storing the last warning."),
9372 JS_FN_HELP("getLastWarning", GetLastWarning
, 0, 0,
9374 " Returns an object that represents the last warning."),
9376 JS_FN_HELP("clearLastWarning", ClearLastWarning
, 0, 0,
9377 "clearLastWarning()",
9378 " Clear the last warning."),
9380 JS_FN_HELP("elapsed", Elapsed
, 0, 0,
9382 " Execution time elapsed for the current thread."),
9384 JS_FN_HELP("decompileFunction", DecompileFunction
, 1, 0,
9385 "decompileFunction(func)",
9386 " Decompile a function."),
9388 JS_FN_HELP("decompileThis", DecompileThisScript
, 0, 0,
9390 " Decompile the currently executing script."),
9392 JS_FN_HELP("valueToSource", ValueToSource
, 1, 0,
9393 "valueToSource(value)",
9394 " Format a value for inspection."),
9396 JS_FN_HELP("thisFilename", ThisFilename
, 0, 0,
9398 " Return the filename of the current script"),
9400 JS_FN_HELP("newGlobal", NewGlobal
, 1, 0,
9401 "newGlobal([options])",
9402 " Return a new global object/realm. The new global is created in the\n"
9403 " 'newGlobal' function object's compartment and zone, unless the\n"
9404 " '--more-compartments' command-line flag was given, in which case new\n"
9405 " globals get a fresh compartment and zone. If options is given, it may\n"
9406 " have any of the following properties:\n"
9407 " sameCompartmentAs: If an object, the global will be in the same\n"
9408 " compartment and zone as the given object.\n"
9409 " sameZoneAs: The global will be in a new compartment in the same zone\n"
9410 " as the given object.\n"
9411 " newCompartment: If true, the global will always be created in a new\n"
9412 " compartment and zone.\n"
9413 " invisibleToDebugger: If true, the global will be invisible to the\n"
9414 " debugger (default false)\n"
9415 " discardSource: If true, discard source after compiling a script\n"
9416 " (default false).\n"
9417 " useWindowProxy: the global will be created with a WindowProxy attached. In this\n"
9418 " case, the WindowProxy will be returned.\n"
9419 " freezeBuiltins: certain builtin constructors will be frozen when created and\n"
9420 " their prototypes will be sealed. These constructors will be defined on the\n"
9421 " global as non-configurable and non-writable.\n"
9422 " immutablePrototype: whether the global's prototype is immutable.\n"
9423 " principal: if present, its value converted to a number must be an\n"
9424 " integer that fits in 32 bits; use that as the new realm's\n"
9425 " principal. Shell principals are toys, meant only for testing; one\n"
9426 " shell principal subsumes another if its set bits are a superset of\n"
9427 " the other's. Thus, a principal of 0 subsumes nothing, while a\n"
9428 " principals of ~0 subsumes all other principals. The absence of a\n"
9429 " principal is treated as if its bits were 0xffff, for subsumption\n"
9430 " purposes. If this property is omitted, supply no principal.\n"
9431 " systemPrincipal: If true, use the shell's trusted principals for the\n"
9432 " new realm. This creates a realm that's marked as a 'system' realm."),
9434 JS_FN_HELP("nukeAllCCWs", NukeAllCCWs
, 0, 0,
9436 " Like nukeCCW, but for all CrossCompartmentWrappers targeting the current realm."),
9438 JS_FN_HELP("recomputeWrappers", RecomputeWrappers
, 2, 0,
9439 "recomputeWrappers([src, [target]])",
9440 " Recompute all cross-compartment wrappers. src and target are both optional\n"
9441 " and can be used to filter source or target compartments: the unwrapped\n"
9442 " object's compartment is used as CompartmentFilter.\n"),
9444 JS_FN_HELP("dumpObjectWrappers", DumpObjectWrappers
, 2, 0,
9445 "dumpObjectWrappers()",
9446 " Print information about cross-compartment object wrappers.\n"),
9448 JS_FN_HELP("wrapWithProto", WrapWithProto
, 2, 0,
9449 "wrapWithProto(obj)",
9450 " Wrap an object into a noop wrapper with prototype semantics."),
9452 JS_FN_HELP("createExternalArrayBuffer", CreateExternalArrayBuffer
, 1, 0,
9453 "createExternalArrayBuffer(size)",
9454 " Create an array buffer that has external data of size."),
9456 JS_FN_HELP("createMappedArrayBuffer", CreateMappedArrayBuffer
, 1, 0,
9457 "createMappedArrayBuffer(filename, [offset, [size]])",
9458 " Create an array buffer that mmaps the given file."),
9460 JS_FN_HELP("createUserArrayBuffer", CreateUserArrayBuffer
, 1, 0,
9461 "createUserArrayBuffer(size)",
9462 " Create an array buffer that uses user-controlled memory."),
9464 JS_FN_HELP("addPromiseReactions", AddPromiseReactions
, 3, 0,
9465 "addPromiseReactions(promise, onResolve, onReject)",
9466 " Calls the JS::AddPromiseReactions JSAPI function with the given arguments."),
9468 JS_FN_HELP("ignoreUnhandledRejections", IgnoreUnhandledRejections
, 0, 0,
9469 "ignoreUnhandledRejections()",
9470 " By default, js shell tracks unhandled promise rejections and reports\n"
9471 " them at the end of the exectuion. If a testcase isn't interested\n"
9472 " in those rejections, call this to stop tracking and reporting."),
9474 JS_FN_HELP("getMaxArgs", GetMaxArgs
, 0, 0,
9476 " Return the maximum number of supported args for a call."),
9478 JS_FN_HELP("createIsHTMLDDA", CreateIsHTMLDDA
, 0, 0,
9479 "createIsHTMLDDA()",
9480 " Return an object |obj| that \"looks like\" the |document.all| object in\n"
9481 " browsers in certain ways: |typeof obj === \"undefined\"|, |obj == null|\n"
9482 " and |obj == undefined| (vice versa for !=), |ToBoolean(obj) === false|,\n"
9483 " and when called with no arguments or the single argument \"\" returns\n"
9484 " null. (Calling |obj| any other way crashes or throws an exception.)\n"
9485 " This function implements the exact requirements of the $262.IsHTMLDDA\n"
9486 " property in test262."),
9488 JS_FN_HELP("cacheEntry", CacheEntry
, 1, 0,
9490 " Return a new opaque object which emulates a cache entry of a script. This\n"
9491 " object encapsulates the code and its cached content. The cache entry is filled\n"
9492 " and read by the \"evaluate\" function by using it in place of the source, and\n"
9493 " by setting \"saveIncrementalBytecode\" and \"loadBytecode\" options."),
9495 JS_FN_HELP("streamCacheEntry", StreamCacheEntryObject::construct
, 1, 0,
9496 "streamCacheEntry(buffer)",
9497 " Create a shell-only object that holds wasm bytecode and can be streaming-\n"
9498 " compiled and cached by WebAssembly.{compile,instantiate}Streaming(). On a\n"
9499 " second compilation of the same cache entry, the cached code will be used."),
9501 JS_FN_HELP("printProfilerEvents", PrintProfilerEvents
, 0, 0,
9502 "printProfilerEvents()",
9503 " Register a callback with the profiler that prints javascript profiler events\n"
9504 " to stderr. Callback is only registered if profiling is enabled."),
9506 JS_FN_HELP("enableSingleStepProfiling", EnableSingleStepProfiling
, 0, 0,
9507 "enableSingleStepProfiling()",
9508 " This function will fail on platforms that don't support single-step profiling\n"
9509 " (currently ARM and MIPS64 support it). When enabled, at every instruction a\n"
9510 " backtrace will be recorded and stored in an array. Adjacent duplicate backtraces\n"
9513 JS_FN_HELP("disableSingleStepProfiling", DisableSingleStepProfiling
, 0, 0,
9514 "disableSingleStepProfiling()",
9515 " Return the array of backtraces recorded by enableSingleStepProfiling."),
9517 JS_FN_HELP("enableGeckoProfiling", EnableGeckoProfiling
, 0, 0,
9518 "enableGeckoProfiling()",
9519 " Enables Gecko Profiler instrumentation and corresponding assertions, with slow\n"
9520 " assertions disabled.\n"),
9522 JS_FN_HELP("enableGeckoProfilingWithSlowAssertions", EnableGeckoProfilingWithSlowAssertions
, 0, 0,
9523 "enableGeckoProfilingWithSlowAssertions()",
9524 " Enables Gecko Profiler instrumentation and corresponding assertions, with slow\n"
9525 " assertions enabled.\n"),
9527 JS_FN_HELP("disableGeckoProfiling", DisableGeckoProfiling
, 0, 0,
9528 "disableGeckoProfiling()",
9529 " Disables Gecko Profiler instrumentation"),
9531 JS_FN_HELP("isLatin1", IsLatin1
, 1, 0,
9533 " Return true iff the string's characters are stored as Latin1."),
9535 JS_FN_HELP("stackPointerInfo", StackPointerInfo
, 0, 0,
9536 "stackPointerInfo()",
9537 " Return an int32 value which corresponds to the offset of the latest stack\n"
9538 " pointer, such that one can take the differences of 2 to estimate a frame-size."),
9540 JS_FN_HELP("entryPoints", EntryPoints
, 1, 0,
9541 "entryPoints(params)",
9542 "Carry out some JSAPI operation as directed by |params|, and return an array of\n"
9543 "objects describing which JavaScript entry points were invoked as a result.\n"
9544 "|params| is an object whose properties indicate what operation to perform. Here\n"
9545 "are the recognized groups of properties:\n"
9547 "{ function }: Call the object |params.function| with no arguments.\n"
9549 "{ object, property }: Fetch the property named |params.property| of\n"
9550 "|params.object|.\n"
9552 "{ ToString }: Apply JS::ToString to |params.toString|.\n"
9554 "{ ToNumber }: Apply JS::ToNumber to |params.toNumber|.\n"
9556 "{ eval }: Apply JS::Evaluate to |params.eval|.\n"
9558 "The return value is an array of strings, with one element for each\n"
9559 "JavaScript invocation that occurred as a result of the given\n"
9560 "operation. Each element is the name of the function invoked, or the\n"
9561 "string 'eval:FILENAME' if the code was invoked by 'eval' or something\n"
9564 JS_FN_HELP("enqueueJob", EnqueueJob
, 1, 0,
9566 " Enqueue 'fn' on the shell's job queue."),
9568 JS_FN_HELP("globalOfFirstJobInQueue", GlobalOfFirstJobInQueue
, 0, 0,
9569 "globalOfFirstJobInQueue()",
9570 " Returns the global of the first item in the job queue. Throws an exception\n"
9571 " if the queue is empty.\n"),
9573 JS_FN_HELP("drainJobQueue", DrainJobQueue
, 0, 0,
9575 "Take jobs from the shell's job queue in FIFO order and run them until the\n"
9576 "queue is empty.\n"),
9578 JS_FN_HELP("setPromiseRejectionTrackerCallback", SetPromiseRejectionTrackerCallback
, 1, 0,
9579 "setPromiseRejectionTrackerCallback()",
9580 "Sets the callback to be invoked whenever a Promise rejection is unhandled\n"
9581 "or a previously-unhandled rejection becomes handled."),
9583 JS_FN_HELP("dumpScopeChain", DumpScopeChain
, 1, 0,
9584 "dumpScopeChain(obj)",
9585 " Prints the scope chain of an interpreted function or a module."),
9587 JS_FN_HELP("blackRoot", EnsureBlackRoot
, 0, 0,
9589 " Return an array in the current compartment whose elements will be marked\n"
9590 " as black roots by the GC."),
9592 JS_FN_HELP("grayRoot", EnsureGrayRoot
, 0, 0,
9594 " Return an array in the current compartment whose elements will be marked\n"
9595 " as gray roots by the GC."),
9597 JS_FN_HELP("addMarkObservers", AddMarkObservers
, 1, 0,
9598 "addMarkObservers(array_of_objects)",
9599 " Register an array of objects whose mark bits will be tested by calls to\n"
9600 " getMarks. The objects will be in calling compartment. Objects from\n"
9601 " multiple compartments may be monitored by calling this function in\n"
9602 " different compartments."),
9604 JS_FN_HELP("clearMarkObservers", ClearMarkObservers
, 1, 0,
9605 "clearMarkObservers()",
9606 " Clear out the list of objects whose mark bits will be tested.\n"),
9608 JS_FN_HELP("getMarks", GetMarks
, 0, 0,
9610 " Return an array of strings representing the current state of the mark\n"
9611 " bits ('gray' or 'black', or 'dead' if the object has been collected)\n"
9612 " for the objects registered via addMarkObservers. Note that some of the\n"
9613 " objects tested may be from different compartments than the one in which\n"
9614 " this function runs."),
9616 JS_FN_HELP("bindToAsyncStack", BindToAsyncStack
, 2, 0,
9617 "bindToAsyncStack(fn, { stack, cause, explicit })",
9618 " Returns a new function that calls 'fn' with no arguments, passing\n"
9619 " 'undefined' as the 'this' value, and supplies an async stack for the\n"
9620 " call as described by the second argument, an object with the following\n"
9621 " properties (which are not optional, unless specified otherwise):\n"
9623 " stack: A SavedFrame object, like that returned by 'saveStack'. Stacks\n"
9624 " captured during calls to the returned function capture this as\n"
9625 " their async stack parent, accessible via a SavedFrame's\n"
9626 " 'asyncParent' property.\n"
9628 " cause: A string, supplied as the async cause on the top frame of\n"
9629 " captured async stacks.\n"
9631 " explicit: A boolean value, indicating whether the given 'stack' should\n"
9632 " always supplant the returned function's true callers (true),\n"
9633 " or only when there are no other JavaScript frames on the stack\n"
9634 " below it (false). If omitted, this is treated as 'true'."),
9636 #ifdef JS_HAS_INTL_API
9637 JS_FN_HELP("addIntlExtras", AddIntlExtras
, 1, 0,
9638 "addIntlExtras(obj)",
9639 "Adds various not-yet-standardized Intl functions as properties on the\n"
9640 "provided object (this should generally be Intl itself). The added\n"
9641 "functions and their behavior are experimental: don't depend upon them\n"
9642 "unless you're willing to update your code if these experimental APIs change\n"
9644 #endif // JS_HAS_INTL_API
9647 JS_FN_HELP("wasmCompileInSeparateProcess", WasmCompileInSeparateProcess
, 1, 0,
9648 "wasmCompileInSeparateProcess(buffer)",
9649 " Compile the given buffer in a separate process, serialize the resulting\n"
9650 " wasm::Module into bytes, and deserialize those bytes in the current\n"
9651 " process, returning the resulting WebAssembly.Module."),
9653 JS_FN_HELP("wasmTextToBinary", WasmTextToBinary
, 1, 0,
9654 "wasmTextToBinary(str)",
9655 " Translates the given text wasm module into its binary encoding."),
9658 JS_FN_HELP("transplantableObject", TransplantableObject
, 0, 0,
9659 "transplantableObject([options])",
9660 " Returns the pair {object, transplant}. |object| is an object which can be\n"
9661 " transplanted into a new object when the |transplant| function, which must\n"
9662 " be invoked with a global object, is called.\n"
9663 " |object| is swapped with a cross-compartment wrapper if the global object\n"
9664 " is in a different compartment.\n"
9666 " If options is given, it may have any of the following properties:\n"
9667 " proxy: Create a DOM Proxy object instead of a plain DOM object.\n"
9668 " object: Don't create a new DOM object, but instead use the supplied\n"
9671 JS_FN_HELP("cpuNow", CpuNow
, /* nargs= */ 0, /* flags = */ 0,
9673 " Returns the approximate processor time used by the process since an arbitrary epoch, in seconds.\n"
9674 " Only the difference between two calls to `cpuNow()` is meaningful."),
9676 #ifdef FUZZING_JS_FUZZILLI
9677 JS_FN_HELP("fuzzilli", Fuzzilli
, 0, 0,
9678 "fuzzilli(operation, arg)",
9679 " Exposes functionality used by the Fuzzilli JavaScript fuzzer."),
9682 #ifdef FUZZING_INTERFACES
9683 JS_FN_HELP("getWasmSmithModule", GetWasmSmithModule
, 1, 0,
9684 "getWasmSmithModule(arrayBuffer)",
9685 " Call wasm-smith to generate a random wasm module from the provided data."),
9688 JS_FN_HELP("isValidJSON", IsValidJSON
, 1, 0,
9689 "isValidJSON(source)",
9690 " Returns true if the given source is valid JSON."),
9697 #ifdef FUZZING_JS_FUZZILLI
9698 static const JSFunctionSpec shell_function_fuzzilli_hash
[] = {
9699 JS_INLINABLE_FN("fuzzilli_hash", fuzzilli_hash
, 1, 0, FuzzilliHash
),
9706 static const JSFunctionSpecWithHelp diff_testing_unsafe_functions
[] = {
9713 static const JSFunctionSpecWithHelp fuzzing_unsafe_functions
[] = {
9714 JS_FN_HELP("getSelfHostedValue", GetSelfHostedValue
, 1, 0,
9715 "getSelfHostedValue()",
9716 " Get a self-hosted value by its name. Note that these values don't get \n"
9717 " cached, so repeatedly getting the same value creates multiple distinct clones."),
9719 JS_FN_HELP("line2pc", LineToPC
, 0, 0,
9720 "line2pc([fun,] line)",
9721 " Map line number to PC."),
9723 JS_FN_HELP("pc2line", PCToLine
, 0, 0,
9724 "pc2line(fun[, pc])",
9725 " Map PC to line number."),
9727 JS_INLINABLE_FN_HELP("assertFloat32", testingFunc_assertFloat32
, 2, 0, TestAssertFloat32
,
9728 "assertFloat32(value, isFloat32)",
9729 " In IonMonkey only, asserts that value has (resp. hasn't) the MIRType::Float32 if isFloat32 is true (resp. false)."),
9731 JS_INLINABLE_FN_HELP("assertRecoveredOnBailout", testingFunc_assertRecoveredOnBailout
, 2, 0,
9732 TestAssertRecoveredOnBailout
,
9733 "assertRecoveredOnBailout(var)",
9734 " In IonMonkey only, asserts that variable has RecoveredOnBailout flag."),
9736 JS_FN_HELP("withSourceHook", WithSourceHook
, 1, 0,
9737 "withSourceHook(hook, fun)",
9738 " Set this JS runtime's lazy source retrieval hook (that is, the hook\n"
9739 " used to find sources compiled with |CompileOptions::LAZY_SOURCE|) to\n"
9740 " |hook|; call |fun| with no arguments; and then restore the runtime's\n"
9741 " original hook. Return or throw whatever |fun| did. |hook| gets\n"
9742 " passed the requested code's URL, and should return a string.\n"
9746 " 1) SpiderMonkey may assert if the returned code isn't close enough\n"
9747 " to the script's real code, so this function is not fuzzer-safe.\n"
9749 " 2) The runtime can have only one source retrieval hook active at a\n"
9750 " time. If |fun| is not careful, |hook| could be asked to retrieve the\n"
9751 " source code for compilations that occurred long before it was set,\n"
9752 " and that it knows nothing about. The reverse applies as well: the\n"
9753 " original hook, that we reinstate after the call to |fun| completes,\n"
9754 " might be asked for the source code of compilations that |fun|\n"
9755 " performed, and which, presumably, only |hook| knows how to find.\n"),
9757 JS_FN_HELP("crash", Crash
, 0, 0,
9758 "crash([message, [{disable_minidump:true}]])",
9759 " Crashes the process with a MOZ_CRASH, optionally providing a message.\n"
9760 " An options object may be passed as the second argument. If the key\n"
9761 " 'suppress_minidump' is set to true, then a minidump will not be\n"
9762 " generated by the crash (which only has an effect if the breakpad\n"
9763 " dumping library is loaded.)"),
9766 JS_FN_HELP("wasmLoop", WasmLoop
, 2, 0,
9767 "wasmLoop(filename, imports)",
9768 " Performs an AFL-style persistent loop reading data from the given file and passing it\n"
9769 " to the 'wasmEval' function together with the specified imports object."),
9772 JS_FN_HELP("setBufferStreamParams", SetBufferStreamParams
, 2, 0,
9773 "setBufferStreamParams(delayMillis, chunkByteSize)",
9774 " Set the delay time (between calls to StreamConsumer::consumeChunk) and chunk\n"
9775 " size (in bytes)."),
9777 #ifdef JS_CACHEIR_SPEW
9778 JS_FN_HELP("cacheIRHealthReport", CacheIRHealthReport
, 0, 0,
9779 "cacheIRHealthReport()",
9780 " Show health rating of CacheIR stubs."),
9784 JS_FN_HELP("debugGetQueuedJobs", DebugGetQueuedJobs
, 0, 0,
9785 "debugGetQueuedJobs()",
9786 " Returns an array of queued jobs."),
9794 static const JSFunctionSpecWithHelp performance_functions
[] = {
9795 JS_FN_HELP("now", Now
, 0, 0,
9797 " Return the current time with sub-ms precision.\n"
9798 " This function is an alias of the dateNow() function."),
9804 static const JSFunctionSpecWithHelp console_functions
[] = {
9805 JS_FN_HELP("log", Print
, 0, 0,
9807 " Evaluate and print expressions to stdout.\n"
9808 " This function is an alias of the print() function."),
9813 bool DefineConsole(JSContext
* cx
, HandleObject global
) {
9814 RootedObject
obj(cx
, JS_NewPlainObject(cx
));
9815 return obj
&& JS_DefineFunctionsWithHelp(cx
, obj
, console_functions
) &&
9816 JS_DefineProperty(cx
, global
, "console", obj
, 0);
9819 #ifdef MOZ_PROFILING
9820 # define PROFILING_FUNCTION_COUNT 5
9821 # ifdef MOZ_CALLGRIND
9822 # define CALLGRIND_FUNCTION_COUNT 3
9824 # define CALLGRIND_FUNCTION_COUNT 0
9827 # define VTUNE_FUNCTION_COUNT 4
9829 # define VTUNE_FUNCTION_COUNT 0
9831 # define EXTERNAL_FUNCTION_COUNT \
9832 (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT)
9834 # define EXTERNAL_FUNCTION_COUNT 0
9837 #undef PROFILING_FUNCTION_COUNT
9838 #undef CALLGRIND_FUNCTION_COUNT
9839 #undef VTUNE_FUNCTION_COUNT
9840 #undef EXTERNAL_FUNCTION_COUNT
9842 static bool PrintHelpString(JSContext
* cx
, HandleValue v
) {
9843 RootedString
str(cx
, v
.toString());
9844 MOZ_ASSERT(gOutFile
->isOpen());
9846 UniqueChars bytes
= JS_EncodeStringToUTF8(cx
, str
);
9851 fprintf(gOutFile
->fp
, "%s\n", bytes
.get());
9855 static bool PrintHelp(JSContext
* cx
, HandleObject obj
) {
9856 RootedValue
usage(cx
);
9857 if (!JS_GetProperty(cx
, obj
, "usage", &usage
)) {
9860 RootedValue
help(cx
);
9861 if (!JS_GetProperty(cx
, obj
, "help", &help
)) {
9865 if (!usage
.isString() || !help
.isString()) {
9869 return PrintHelpString(cx
, usage
) && PrintHelpString(cx
, help
);
9872 static bool PrintEnumeratedHelp(JSContext
* cx
, HandleObject obj
,
9873 HandleObject pattern
, bool brief
) {
9874 RootedIdVector
idv(cx
);
9875 if (!GetPropertyKeys(cx
, obj
, JSITER_OWNONLY
| JSITER_HIDDEN
, &idv
)) {
9879 Rooted
<RegExpObject
*> regex(cx
);
9881 regex
= &UncheckedUnwrap(pattern
)->as
<RegExpObject
>();
9884 for (size_t i
= 0; i
< idv
.length(); i
++) {
9886 RootedId
id(cx
, idv
[i
]);
9887 if (!JS_GetPropertyById(cx
, obj
, id
, &v
)) {
9890 if (!v
.isObject()) {
9894 RootedObject
funcObj(cx
, &v
.toObject());
9896 // Only pay attention to objects with a 'help' property, which will
9897 // either be documented functions or interface objects.
9898 if (!JS_GetProperty(cx
, funcObj
, "help", &v
)) {
9901 if (!v
.isString()) {
9905 // For functions, match against the name. For interface objects,
9906 // match against the usage string.
9907 if (!JS_GetProperty(cx
, funcObj
, "name", &v
)) {
9910 if (!v
.isString()) {
9911 if (!JS_GetProperty(cx
, funcObj
, "usage", &v
)) {
9914 if (!v
.isString()) {
9919 Rooted
<JSString
*> inputStr(cx
, v
.toString());
9920 if (!inputStr
->ensureLinear(cx
)) {
9924 // Execute the regular expression in |regex|'s compartment.
9925 AutoRealm
ar(cx
, regex
);
9926 if (!cx
->compartment()->wrap(cx
, &inputStr
)) {
9929 Rooted
<JSLinearString
*> input(cx
, &inputStr
->asLinear());
9931 if (!ExecuteRegExpLegacy(cx
, nullptr, regex
, input
, &ignored
, true, &v
)) {
9939 if (!PrintHelp(cx
, funcObj
)) {
9947 static bool Help(JSContext
* cx
, unsigned argc
, Value
* vp
) {
9948 if (!gOutFile
->isOpen()) {
9949 JS_ReportErrorASCII(cx
, "output file is closed");
9953 CallArgs args
= CallArgsFromVp(argc
, vp
);
9954 args
.rval().setUndefined();
9955 RootedObject
global(cx
, JS::CurrentGlobalOrNull(cx
));
9957 // help() - display the version and dump out help for all functions on the
9959 if (args
.length() == 0) {
9960 fprintf(gOutFile
->fp
, "%s\n", JS_GetImplementationVersion());
9962 if (!PrintEnumeratedHelp(cx
, global
, nullptr, false)) {
9970 if (args
[0].isPrimitive()) {
9972 JS_ReportErrorASCII(cx
, "primitive arg");
9976 RootedObject
obj(cx
, &args
[0].toObject());
9981 if (!JS::ObjectIsRegExp(cx
, obj
, &isRegexp
)) {
9987 return PrintEnumeratedHelp(cx
, global
, obj
, false);
9991 // help(namespace_obj)
9992 return PrintHelp(cx
, obj
);
9995 static const JSErrorFormatString jsShell_ErrorFormatString
[JSShellErr_Limit
] = {
9996 #define MSG_DEF(name, count, exception, format) \
9997 {#name, format, count, JSEXN_ERR},
9998 #include "jsshell.msg"
10002 const JSErrorFormatString
* js::shell::my_GetErrorMessage(
10003 void* userRef
, const unsigned errorNumber
) {
10004 if (errorNumber
== 0 || errorNumber
>= JSShellErr_Limit
) {
10008 return &jsShell_ErrorFormatString
[errorNumber
];
10011 static bool CreateLastWarningObject(JSContext
* cx
, JSErrorReport
* report
) {
10012 RootedObject
warningObj(cx
, JS_NewObject(cx
, nullptr));
10017 if (!CopyErrorReportToObject(cx
, report
, warningObj
)) {
10021 GetShellContext(cx
)->lastWarning
.setObject(*warningObj
);
10025 static FILE* ErrorFilePointer() {
10026 if (gErrFile
->isOpen()) {
10027 return gErrFile
->fp
;
10030 fprintf(stderr
, "error file is closed; falling back to stderr\n");
10034 bool shell::PrintStackTrace(JSContext
* cx
, HandleObject stackObj
) {
10035 if (!stackObj
|| !stackObj
->is
<SavedFrame
>()) {
10039 JSPrincipals
* principals
= stackObj
->nonCCWRealm()->principals();
10040 RootedString
stackStr(cx
);
10041 if (!BuildStackString(cx
, principals
, stackObj
, &stackStr
, 2)) {
10045 UniqueChars stack
= JS_EncodeStringToUTF8(cx
, stackStr
);
10050 FILE* fp
= ErrorFilePointer();
10051 fputs("Stack:\n", fp
);
10052 fputs(stack
.get(), fp
);
10057 js::shell::AutoReportException::~AutoReportException() {
10058 if (!JS_IsExceptionPending(cx
)) {
10062 auto printError
= [](JSContext
* cx
, auto& report
, const auto& exnStack
,
10063 const char* prefix
= nullptr) {
10064 if (!report
.init(cx
, exnStack
, JS::ErrorReportBuilder::WithSideEffects
)) {
10065 fprintf(stderr
, "out of memory initializing JS::ErrorReportBuilder\n");
10067 JS_ClearPendingException(cx
);
10071 MOZ_ASSERT(!report
.report()->isWarning());
10073 FILE* fp
= ErrorFilePointer();
10077 JS::PrintError(fp
, report
, reportWarnings
);
10078 JS_ClearPendingException(cx
);
10080 // If possible, use the original error stack as the source of truth, because
10081 // finally block handlers may have overwritten the exception stack.
10082 RootedObject
stack(cx
, exnStack
.stack());
10083 if (exnStack
.exception().isObject()) {
10084 RootedObject
exception(cx
, &exnStack
.exception().toObject());
10085 if (JSObject
* exceptionStack
= JS::ExceptionStackOrNull(exception
)) {
10086 stack
.set(exceptionStack
);
10090 if (!PrintStackTrace(cx
, stack
)) {
10091 fputs("(Unable to print stack trace)\n", fp
);
10092 JS_ClearPendingException(cx
);
10098 // Get exception object and stack before printing and clearing exception.
10099 JS::ExceptionStack
exnStack(cx
);
10100 if (!JS::StealPendingExceptionStack(cx
, &exnStack
)) {
10101 fprintf(stderr
, "out of memory while stealing exception\n");
10103 JS_ClearPendingException(cx
);
10107 ShellContext
* sc
= GetShellContext(cx
);
10108 JS::ErrorReportBuilder
report(cx
);
10109 if (!printError(cx
, report
, exnStack
)) {
10110 // Return if we couldn't initialize the error report.
10114 // Print the error's cause, if available.
10115 if (exnStack
.exception().isObject()) {
10116 JSObject
* exception
= &exnStack
.exception().toObject();
10117 if (exception
->is
<ErrorObject
>()) {
10118 auto* error
= &exception
->as
<ErrorObject
>();
10119 if (auto maybeCause
= error
->getCause()) {
10120 RootedValue
cause(cx
, maybeCause
.value());
10122 RootedObject
causeStack(cx
);
10123 if (cause
.isObject()) {
10124 RootedObject
causeObj(cx
, &cause
.toObject());
10125 causeStack
= JS::ExceptionStackOrNull(causeObj
);
10128 JS::ExceptionStack
exnStack(cx
, cause
, causeStack
);
10129 JS::ErrorReportBuilder
report(cx
);
10130 printError(cx
, report
, exnStack
, "Caused by: ");
10135 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
10136 // Don't quit the shell if an unhandled exception is reported during OOM
10138 if (cx
->runningOOMTest
) {
10143 if (report
.report()->errorNumber
== JSMSG_OUT_OF_MEMORY
) {
10144 sc
->exitCode
= EXITCODE_OUT_OF_MEMORY
;
10146 sc
->exitCode
= EXITCODE_RUNTIME_ERROR
;
10150 void js::shell::WarningReporter(JSContext
* cx
, JSErrorReport
* report
) {
10151 ShellContext
* sc
= GetShellContext(cx
);
10152 FILE* fp
= ErrorFilePointer();
10154 MOZ_ASSERT(report
->isWarning());
10156 if (sc
->lastWarningEnabled
) {
10157 JS::AutoSaveExceptionState
savedExc(cx
);
10158 if (!CreateLastWarningObject(cx
, report
)) {
10159 fputs("Unhandled error happened while creating last warning object.\n",
10163 savedExc
.restore();
10166 // Print the warning.
10167 JS::PrintError(fp
, report
, reportWarnings
);
10170 static bool global_enumerate(JSContext
* cx
, JS::HandleObject obj
,
10171 JS::MutableHandleIdVector properties
,
10172 bool enumerableOnly
) {
10173 #ifdef LAZY_STANDARD_CLASSES
10174 return JS_NewEnumerateStandardClasses(cx
, obj
, properties
, enumerableOnly
);
10180 static bool global_resolve(JSContext
* cx
, HandleObject obj
, HandleId id
,
10182 #ifdef LAZY_STANDARD_CLASSES
10183 if (!JS_ResolveStandardClass(cx
, obj
, id
, resolvedp
)) {
10190 static bool global_mayResolve(const JSAtomState
& names
, jsid id
,
10191 JSObject
* maybeObj
) {
10192 return JS_MayResolveStandardClass(names
, id
, maybeObj
);
10195 static const JSClassOps global_classOps
= {
10196 nullptr, // addProperty
10197 nullptr, // delProperty
10198 nullptr, // enumerate
10199 global_enumerate
, // newEnumerate
10200 global_resolve
, // resolve
10201 global_mayResolve
, // mayResolve
10202 nullptr, // finalize
10204 nullptr, // construct
10205 JS_GlobalObjectTraceHook
, // trace
10208 static constexpr uint32_t DOM_PROTOTYPE_SLOT
= JSCLASS_GLOBAL_SLOT_COUNT
;
10209 static constexpr uint32_t DOM_GLOBAL_SLOTS
= 1;
10211 static const JSClass global_class
= {
10213 JSCLASS_GLOBAL_FLAGS
| JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS
),
10217 * Define a FakeDOMObject constructor. It returns an object with a getter,
10218 * setter and method with attached JitInfo. This object can be used to test
10219 * IonMonkey DOM optimizations in the shell.
10222 /* Fow now just use to a constant we can check. */
10223 static const void* DOM_PRIVATE_VALUE
= (void*)0x1234;
10225 static bool dom_genericGetter(JSContext
* cx
, unsigned argc
, JS::Value
* vp
);
10227 static bool dom_genericSetter(JSContext
* cx
, unsigned argc
, JS::Value
* vp
);
10229 static bool dom_genericMethod(JSContext
* cx
, unsigned argc
, JS::Value
* vp
);
10231 static bool dom_get_x(JSContext
* cx
, HandleObject obj
, void* self
,
10232 JSJitGetterCallArgs args
) {
10233 MOZ_ASSERT(JS::GetClass(obj
) == GetDomClass());
10234 MOZ_ASSERT(self
== DOM_PRIVATE_VALUE
);
10235 args
.rval().set(JS_NumberValue(double(3.14)));
10239 static bool dom_set_x(JSContext
* cx
, HandleObject obj
, void* self
,
10240 JSJitSetterCallArgs args
) {
10241 MOZ_ASSERT(JS::GetClass(obj
) == GetDomClass());
10242 MOZ_ASSERT(self
== DOM_PRIVATE_VALUE
);
10246 static bool dom_get_slot(JSContext
* cx
, HandleObject obj
, void* self
,
10247 JSJitGetterCallArgs args
) {
10248 MOZ_ASSERT(JS::GetClass(obj
) == GetDomClass());
10249 MOZ_ASSERT(self
== DOM_PRIVATE_VALUE
);
10251 Value v
= JS::GetReservedSlot(obj
, DOM_OBJECT_SLOT2
);
10252 MOZ_ASSERT(v
.toInt32() == 42);
10253 args
.rval().set(v
);
10257 static bool dom_get_global(JSContext
* cx
, HandleObject obj
, void* self
,
10258 JSJitGetterCallArgs args
) {
10259 MOZ_ASSERT(JS::GetClass(obj
) == GetDomClass());
10260 MOZ_ASSERT(self
== DOM_PRIVATE_VALUE
);
10262 // Return the current global (instead of obj->global()) to test cx->realm
10263 // switching in the JIT.
10264 args
.rval().setObject(*ToWindowProxyIfWindow(cx
->global()));
10269 static bool dom_set_global(JSContext
* cx
, HandleObject obj
, void* self
,
10270 JSJitSetterCallArgs args
) {
10271 MOZ_ASSERT(JS::GetClass(obj
) == GetDomClass());
10272 MOZ_ASSERT(self
== DOM_PRIVATE_VALUE
);
10274 // Throw an exception if our argument is not the current global. This lets
10275 // us test cx->realm switching.
10276 if (!args
[0].isObject() ||
10277 ToWindowIfWindowProxy(&args
[0].toObject()) != cx
->global()) {
10278 JS_ReportErrorASCII(cx
, "Setter not called with matching global argument");
10285 static bool dom_doFoo(JSContext
* cx
, HandleObject obj
, void* self
,
10286 const JSJitMethodCallArgs
& args
) {
10287 MOZ_ASSERT(JS::GetClass(obj
) == GetDomClass());
10288 MOZ_ASSERT(self
== DOM_PRIVATE_VALUE
);
10289 MOZ_ASSERT(cx
->realm() == args
.callee().as
<JSFunction
>().realm());
10291 /* Just return args.length(). */
10292 args
.rval().setInt32(args
.length());
10296 static const JSJitInfo dom_x_getterinfo
= {
10297 {(JSJitGetterOp
)dom_get_x
},
10301 JSJitInfo::AliasNone
, /* aliasSet */
10302 JSVAL_TYPE_UNKNOWN
, /* returnType */
10303 true, /* isInfallible. False in setters. */
10304 true, /* isMovable */
10305 true, /* isEliminatable */
10306 false, /* isAlwaysInSlot */
10307 false, /* isLazilyCachedInSlot */
10308 false, /* isTypedMethod */
10312 static const JSJitInfo dom_x_setterinfo
= {
10313 {(JSJitGetterOp
)dom_set_x
},
10317 JSJitInfo::AliasEverything
, /* aliasSet */
10318 JSVAL_TYPE_UNKNOWN
, /* returnType */
10319 false, /* isInfallible. False in setters. */
10320 false, /* isMovable. */
10321 false, /* isEliminatable. */
10322 false, /* isAlwaysInSlot */
10323 false, /* isLazilyCachedInSlot */
10324 false, /* isTypedMethod */
10328 static const JSJitInfo dom_slot_getterinfo
= {
10329 {(JSJitGetterOp
)dom_get_slot
},
10333 JSJitInfo::AliasNone
, /* aliasSet */
10334 JSVAL_TYPE_INT32
, /* returnType */
10335 false, /* isInfallible. False in setters. */
10336 true, /* isMovable */
10337 true, /* isEliminatable */
10338 true, /* isAlwaysInSlot */
10339 false, /* isLazilyCachedInSlot */
10340 false, /* isTypedMethod */
10341 DOM_OBJECT_SLOT2
/* slotIndex */
10344 // Note: this getter uses AliasEverything and is marked as fallible and
10345 // non-movable (1) to prevent Ion from getting too clever optimizing it and
10346 // (2) it's nice to have a few different kinds of getters in the shell.
10347 static const JSJitInfo dom_global_getterinfo
= {
10348 {(JSJitGetterOp
)dom_get_global
},
10352 JSJitInfo::AliasEverything
, /* aliasSet */
10353 JSVAL_TYPE_OBJECT
, /* returnType */
10354 false, /* isInfallible. False in setters. */
10355 false, /* isMovable */
10356 false, /* isEliminatable */
10357 false, /* isAlwaysInSlot */
10358 false, /* isLazilyCachedInSlot */
10359 false, /* isTypedMethod */
10363 static const JSJitInfo dom_global_setterinfo
= {
10364 {(JSJitGetterOp
)dom_set_global
},
10368 JSJitInfo::AliasEverything
, /* aliasSet */
10369 JSVAL_TYPE_UNKNOWN
, /* returnType */
10370 false, /* isInfallible. False in setters. */
10371 false, /* isMovable. */
10372 false, /* isEliminatable. */
10373 false, /* isAlwaysInSlot */
10374 false, /* isLazilyCachedInSlot */
10375 false, /* isTypedMethod */
10379 static const JSJitInfo doFoo_methodinfo
= {
10380 {(JSJitGetterOp
)dom_doFoo
},
10384 JSJitInfo::AliasEverything
, /* aliasSet */
10385 JSVAL_TYPE_UNKNOWN
, /* returnType */
10386 false, /* isInfallible. False in setters. */
10387 false, /* isMovable */
10388 false, /* isEliminatable */
10389 false, /* isAlwaysInSlot */
10390 false, /* isLazilyCachedInSlot */
10391 false, /* isTypedMethod */
10395 static const JSPropertySpec dom_props
[] = {
10396 JSPropertySpec::nativeAccessors("x", JSPROP_ENUMERATE
, dom_genericGetter
,
10397 &dom_x_getterinfo
, dom_genericSetter
,
10398 &dom_x_setterinfo
),
10399 JSPropertySpec::nativeAccessors("slot", JSPROP_ENUMERATE
, dom_genericGetter
,
10400 &dom_slot_getterinfo
),
10401 JSPropertySpec::nativeAccessors("global", JSPROP_ENUMERATE
,
10402 dom_genericGetter
, &dom_global_getterinfo
,
10403 dom_genericSetter
, &dom_global_setterinfo
),
10406 static const JSFunctionSpec dom_methods
[] = {
10407 JS_FNINFO("doFoo", dom_genericMethod
, &doFoo_methodinfo
, 3,
10411 static const JSClass dom_class
= {
10412 "FakeDOMObject", JSCLASS_IS_DOMJSCLASS
| JSCLASS_HAS_RESERVED_SLOTS(2)};
10414 static const JSClass
* GetDomClass() { return &dom_class
; }
10416 static bool dom_genericGetter(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
10417 CallArgs args
= CallArgsFromVp(argc
, vp
);
10419 if (!args
.thisv().isObject()) {
10420 args
.rval().setUndefined();
10424 RootedObject
obj(cx
, &args
.thisv().toObject());
10425 if (JS::GetClass(obj
) != &dom_class
) {
10426 args
.rval().set(UndefinedValue());
10430 JS::Value val
= JS::GetReservedSlot(obj
, DOM_OBJECT_SLOT
);
10432 const JSJitInfo
* info
= FUNCTION_VALUE_TO_JITINFO(args
.calleev());
10433 MOZ_ASSERT(info
->type() == JSJitInfo::Getter
);
10434 JSJitGetterOp getter
= info
->getter
;
10435 return getter(cx
, obj
, val
.toPrivate(), JSJitGetterCallArgs(args
));
10438 static bool dom_genericSetter(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
10439 CallArgs args
= CallArgsFromVp(argc
, vp
);
10441 if (args
.length() < 1 || !args
.thisv().isObject()) {
10442 args
.rval().setUndefined();
10446 RootedObject
obj(cx
, &args
.thisv().toObject());
10447 if (JS::GetClass(obj
) != &dom_class
) {
10448 args
.rval().set(UndefinedValue());
10452 JS::Value val
= JS::GetReservedSlot(obj
, DOM_OBJECT_SLOT
);
10454 const JSJitInfo
* info
= FUNCTION_VALUE_TO_JITINFO(args
.calleev());
10455 MOZ_ASSERT(info
->type() == JSJitInfo::Setter
);
10456 JSJitSetterOp setter
= info
->setter
;
10457 if (!setter(cx
, obj
, val
.toPrivate(), JSJitSetterCallArgs(args
))) {
10460 args
.rval().set(UndefinedValue());
10464 static bool dom_genericMethod(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
10465 CallArgs args
= CallArgsFromVp(argc
, vp
);
10467 if (!args
.thisv().isObject()) {
10468 args
.rval().setUndefined();
10472 RootedObject
obj(cx
, &args
.thisv().toObject());
10473 if (JS::GetClass(obj
) != &dom_class
) {
10474 args
.rval().set(UndefinedValue());
10478 JS::Value val
= JS::GetReservedSlot(obj
, DOM_OBJECT_SLOT
);
10480 const JSJitInfo
* info
= FUNCTION_VALUE_TO_JITINFO(args
.calleev());
10481 MOZ_ASSERT(info
->type() == JSJitInfo::Method
);
10482 JSJitMethodOp method
= info
->method
;
10483 return method(cx
, obj
, val
.toPrivate(), JSJitMethodCallArgs(args
));
10486 static void InitDOMObject(HandleObject obj
) {
10487 JS::SetReservedSlot(obj
, DOM_OBJECT_SLOT
,
10488 PrivateValue(const_cast<void*>(DOM_PRIVATE_VALUE
)));
10489 JS::SetReservedSlot(obj
, DOM_OBJECT_SLOT2
, Int32Value(42));
10492 static JSObject
* GetDOMPrototype(JSContext
* cx
, JSObject
* global
) {
10493 MOZ_ASSERT(JS_IsGlobalObject(global
));
10494 if (JS::GetClass(global
) != &global_class
) {
10495 JS_ReportErrorASCII(cx
, "Can't get FakeDOMObject prototype in sandbox");
10499 const JS::Value
& slot
= JS::GetReservedSlot(global
, DOM_PROTOTYPE_SLOT
);
10500 MOZ_ASSERT(slot
.isObject());
10501 return &slot
.toObject();
10504 static bool dom_constructor(JSContext
* cx
, unsigned argc
, JS::Value
* vp
) {
10505 CallArgs args
= CallArgsFromVp(argc
, vp
);
10507 RootedObject
callee(cx
, &args
.callee());
10508 RootedValue
protov(cx
);
10509 if (!GetProperty(cx
, callee
, callee
, cx
->names().prototype
, &protov
)) {
10513 if (!protov
.isObject()) {
10514 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr, JSMSG_BAD_PROTOTYPE
,
10519 RootedObject
proto(cx
, &protov
.toObject());
10520 RootedObject
domObj(cx
, JS_NewObjectWithGivenProto(cx
, &dom_class
, proto
));
10525 InitDOMObject(domObj
);
10527 args
.rval().setObject(*domObj
);
10531 static bool InstanceClassHasProtoAtDepth(const JSClass
* clasp
, uint32_t protoID
,
10533 // Only the (fake) DOM object supports any JIT optimizations.
10534 return clasp
== GetDomClass();
10537 static bool ShellBuildId(JS::BuildIdCharVector
* buildId
) {
10538 // The browser embeds the date into the buildid and the buildid is embedded
10539 // in the binary, so every 'make' necessarily builds a new firefox binary.
10540 // Fortunately, the actual firefox executable is tiny -- all the code is in
10541 // libxul.so and other shared modules -- so this isn't a big deal. Not so
10542 // for the statically-linked JS shell. To avoid recompiling js.cpp and
10543 // re-linking 'js' on every 'make', we use a constant buildid and rely on
10544 // the shell user to manually clear any caches between cache-breaking updates.
10545 const char buildid
[] = "JS-shell";
10546 return buildId
->append(buildid
, sizeof(buildid
));
10549 static bool TimesAccessed(JSContext
* cx
, unsigned argc
, Value
* vp
) {
10550 static int32_t accessed
= 0;
10551 CallArgs args
= CallArgsFromVp(argc
, vp
);
10552 args
.rval().setInt32(++accessed
);
10556 static const JSPropertySpec TestingProperties
[] = {
10557 JS_PSG("timesAccessed", TimesAccessed
, 0), JS_PS_END
};
10559 static JSObject
* NewGlobalObject(JSContext
* cx
, JS::RealmOptions
& options
,
10560 JSPrincipals
* principals
, ShellGlobalKind kind
,
10561 bool immutablePrototype
) {
10562 RootedObject
glob(cx
,
10563 JS_NewGlobalObject(cx
, &global_class
, principals
,
10564 JS::DontFireOnNewGlobalHook
, options
));
10570 JSAutoRealm
ar(cx
, glob
);
10572 if (kind
== ShellGlobalKind::WindowProxy
) {
10573 RootedObject
proxy(cx
, NewShellWindowProxy(cx
, glob
));
10577 js::SetWindowProxy(cx
, glob
, proxy
);
10580 #ifndef LAZY_STANDARD_CLASSES
10581 if (!JS::InitRealmStandardClasses(cx
)) {
10586 if (immutablePrototype
) {
10588 if (!JS_SetImmutablePrototype(cx
, glob
, &succeeded
)) {
10591 MOZ_ASSERT(succeeded
,
10592 "a fresh, unexposed global object is always capable of "
10593 "having its [[Prototype]] be immutable");
10596 #ifdef JS_HAS_CTYPES
10597 if (!fuzzingSafe
&& !JS::InitCTypesClass(cx
, glob
)) {
10601 if (!JS_InitReflectParse(cx
, glob
)) {
10604 if (!JS_DefineDebuggerObject(cx
, glob
)) {
10607 if (!JS_DefineFunctionsWithHelp(cx
, glob
, shell_functions
) ||
10608 !JS_DefineProfilingFunctions(cx
, glob
)) {
10611 #ifdef FUZZING_JS_FUZZILLI
10612 if (!JS_DefineFunctions(cx
, glob
, shell_function_fuzzilli_hash
)) {
10616 if (!js::DefineTestingFunctions(cx
, glob
, fuzzingSafe
,
10617 disableOOMFunctions
)) {
10620 if (!JS_DefineProperties(cx
, glob
, TestingProperties
)) {
10624 if (!fuzzingSafe
) {
10625 if (!JS_DefineFunctionsWithHelp(cx
, glob
, fuzzing_unsafe_functions
)) {
10628 if (!DefineConsole(cx
, glob
)) {
10633 if (!DefineOS(cx
, glob
, fuzzingSafe
, &gOutFile
, &gErrFile
)) {
10637 if (!js::SupportDifferentialTesting()) {
10638 if (!JS_DefineFunctionsWithHelp(cx
, glob
,
10639 diff_testing_unsafe_functions
)) {
10643 RootedObject
performanceObj(cx
, JS_NewObject(cx
, nullptr));
10644 if (!performanceObj
) {
10647 if (!JS_DefineFunctionsWithHelp(cx
, performanceObj
,
10648 performance_functions
)) {
10651 RootedObject
mozMemoryObj(cx
, JS_NewObject(cx
, nullptr));
10652 if (!mozMemoryObj
) {
10655 RootedObject
gcObj(cx
, gc::NewMemoryInfoObject(cx
));
10659 if (!JS_DefineProperty(cx
, glob
, "performance", performanceObj
,
10660 JSPROP_ENUMERATE
)) {
10663 if (!JS_DefineProperty(cx
, performanceObj
, "mozMemory", mozMemoryObj
,
10664 JSPROP_ENUMERATE
)) {
10667 if (!JS_DefineProperty(cx
, mozMemoryObj
, "gc", gcObj
, JSPROP_ENUMERATE
)) {
10672 /* Initialize FakeDOMObject. */
10673 static const js::DOMCallbacks DOMcallbacks
= {InstanceClassHasProtoAtDepth
};
10674 SetDOMCallbacks(cx
, &DOMcallbacks
);
10676 RootedObject
domProto(
10677 cx
, JS_InitClass(cx
, glob
, &dom_class
, nullptr, "FakeDOMObject",
10678 dom_constructor
, 0, dom_props
, dom_methods
, nullptr,
10684 // FakeDOMObject.prototype is the only DOM object which needs to retrieved
10685 // in the shell; store it directly instead of creating a separate layer
10686 // (ProtoAndIfaceCache) as done in the browser.
10687 JS::SetReservedSlot(glob
, DOM_PROTOTYPE_SLOT
, ObjectValue(*domProto
));
10689 /* Initialize FakeDOMObject.prototype */
10690 InitDOMObject(domProto
);
10692 if (!DefineToStringTag(cx
, glob
, cx
->names().global
)) {
10696 JS_FireOnNewGlobalObject(cx
, glob
);
10702 static bool BindScriptArgs(JSContext
* cx
, OptionParser
* op
) {
10703 AutoReportException
are(cx
);
10705 MultiStringRange msr
= op
->getMultiStringArg("scriptArgs");
10706 RootedObject
scriptArgs(cx
);
10707 scriptArgs
= JS::NewArrayObject(cx
, 0);
10712 if (!JS_DefineProperty(cx
, cx
->global(), "scriptArgs", scriptArgs
, 0)) {
10716 for (size_t i
= 0; !msr
.empty(); msr
.popFront(), ++i
) {
10717 const char* scriptArg
= msr
.front();
10718 UniqueChars scriptArgUtf8
= JS::EncodeNarrowToUtf8(cx
, scriptArg
);
10719 if (!scriptArgUtf8
) {
10722 RootedString
str(cx
, NewStringCopyUTF8(cx
, scriptArgUtf8
.get()));
10723 if (!str
|| !JS_DefineElement(cx
, scriptArgs
, i
, str
, JSPROP_ENUMERATE
)) {
10728 RootedValue
scriptPathValue(cx
);
10729 if (const char* scriptPath
= op
->getStringArg("script")) {
10730 UniqueChars scriptPathUtf8
= JS::EncodeNarrowToUtf8(cx
, scriptPath
);
10731 if (!scriptPathUtf8
) {
10734 RootedString
scriptPathString(cx
,
10735 NewStringCopyUTF8(cx
, scriptPathUtf8
.get()));
10736 if (!scriptPathString
) {
10739 scriptPathValue
= StringValue(scriptPathString
);
10741 scriptPathValue
= UndefinedValue();
10744 if (!JS_DefineProperty(cx
, cx
->global(), "scriptPath", scriptPathValue
, 0)) {
10751 static bool OptionFailure(const char* option
, const char* str
) {
10752 fprintf(stderr
, "Unrecognized option for %s: %s\n", option
, str
);
10756 template <typename
... Ts
>
10757 auto minVal(Ts
... args
);
10758 template <typename T
>
10763 template <typename T
, typename
... Ts
>
10764 auto minVal(T a
, Ts
... args
) {
10765 return std::min(a
, minVal(args
...));
10768 [[nodiscard
]] static bool ProcessArgs(JSContext
* cx
, OptionParser
* op
) {
10769 ShellContext
* sc
= GetShellContext(cx
);
10771 /* |scriptArgs| gets bound on the global before any code is run. */
10772 if (!BindScriptArgs(cx
, op
)) {
10776 MultiStringRange filePaths
= op
->getMultiStringOption('f');
10777 MultiStringRange utf16FilePaths
= op
->getMultiStringOption('u');
10778 MultiStringRange preludePaths
= op
->getMultiStringOption('p');
10779 MultiStringRange codeChunks
= op
->getMultiStringOption('e');
10780 MultiStringRange modulePaths
= op
->getMultiStringOption('m');
10782 #ifdef FUZZING_JS_FUZZILLI
10783 // Check for REPRL file source
10784 if (op
->getBoolOption("reprl")) {
10785 return FuzzilliReprlGetAndRun(cx
);
10787 #endif /* FUZZING_JS_FUZZILLI */
10789 if (filePaths
.empty() && utf16FilePaths
.empty() && codeChunks
.empty() &&
10790 modulePaths
.empty() && !op
->getStringArg("script")) {
10791 // Always use the interactive shell when -i is used. Without -i we let
10792 // Process figure it out based on isatty.
10793 bool forceTTY
= op
->getBoolOption('i');
10794 return Process(cx
, nullptr, forceTTY
, FileScript
);
10797 while (!preludePaths
.empty() || !filePaths
.empty() ||
10798 !utf16FilePaths
.empty() || !codeChunks
.empty() ||
10799 !modulePaths
.empty()) {
10800 size_t ppArgno
= preludePaths
.empty() ? SIZE_MAX
: preludePaths
.argno();
10801 size_t fpArgno
= filePaths
.empty() ? SIZE_MAX
: filePaths
.argno();
10803 utf16FilePaths
.empty() ? SIZE_MAX
: utf16FilePaths
.argno();
10804 size_t ccArgno
= codeChunks
.empty() ? SIZE_MAX
: codeChunks
.argno();
10805 size_t mpArgno
= modulePaths
.empty() ? SIZE_MAX
: modulePaths
.argno();
10806 size_t minArgno
= minVal(ppArgno
, fpArgno
, ufpArgno
, ccArgno
, mpArgno
);
10808 if (ppArgno
== minArgno
) {
10809 UniqueChars path
= JS::EncodeNarrowToUtf8(cx
, preludePaths
.front());
10813 if (!Process(cx
, path
.get(), false, PreludeScript
)) {
10817 preludePaths
.popFront();
10821 if (fpArgno
== minArgno
) {
10822 UniqueChars path
= JS::EncodeNarrowToUtf8(cx
, filePaths
.front());
10826 if (!Process(cx
, path
.get(), false, FileScript
)) {
10830 filePaths
.popFront();
10834 if (ufpArgno
== minArgno
) {
10835 UniqueChars path
= JS::EncodeNarrowToUtf8(cx
, utf16FilePaths
.front());
10839 if (!Process(cx
, path
.get(), false, FileScriptUtf16
)) {
10843 utf16FilePaths
.popFront();
10847 if (ccArgno
== minArgno
) {
10848 UniqueChars code
= JS::EncodeNarrowToUtf8(cx
, codeChunks
.front());
10853 // Command line scripts are always parsed with full-parse to evaluate
10854 // conditions which might filter code coverage conditions.
10855 JS::CompileOptions
opts(cx
);
10856 opts
.setFileAndLine("-e", 1).setForceFullParse();
10858 JS::SourceText
<Utf8Unit
> srcBuf
;
10859 if (!srcBuf
.init(cx
, code
.get(), strlen(code
.get()),
10860 JS::SourceOwnership::Borrowed
)) {
10864 RootedValue
rval(cx
);
10865 if (!JS::Evaluate(cx
, opts
, srcBuf
, &rval
)) {
10869 codeChunks
.popFront();
10870 if (sc
->quitting
) {
10877 MOZ_ASSERT(mpArgno
== minArgno
);
10879 UniqueChars path
= JS::EncodeNarrowToUtf8(cx
, modulePaths
.front());
10883 if (!Process(cx
, path
.get(), false, FileModule
)) {
10887 modulePaths
.popFront();
10890 if (sc
->quitting
) {
10894 /* The |script| argument is processed after all options. */
10895 if (const char* path
= op
->getStringArg("script")) {
10896 UniqueChars pathUtf8
= JS::EncodeNarrowToUtf8(cx
, path
);
10900 if (!Process(cx
, pathUtf8
.get(), false, FileScript
)) {
10905 if (op
->getBoolOption('i')) {
10906 if (!Process(cx
, nullptr, true, FileScript
)) {
10914 static void SetWorkerContextOptions(JSContext
* cx
) {
10915 // Copy option values from the main thread.
10916 JS::ContextOptionsRef(cx
)
10917 .setAsmJS(enableAsmJS
)
10918 .setWasm(enableWasm
)
10919 .setWasmBaseline(enableWasmBaseline
)
10920 .setWasmIon(enableWasmOptimizing
)
10921 #define WASM_FEATURE(NAME, ...) .setWasm##NAME(enableWasm##NAME)
10922 JS_FOR_WASM_FEATURES(WASM_FEATURE
)
10923 #undef WASM_FEATURE
10925 .setWasmVerbose(enableWasmVerbose
)
10926 .setTestWasmAwaitTier2(enableTestWasmAwaitTier2
)
10927 .setSourcePragmas(enableSourcePragmas
);
10929 cx
->runtime()->setOffthreadIonCompilationEnabled(offthreadCompilation
);
10930 cx
->runtime()->profilingScripts
=
10931 enableCodeCoverage
|| enableDisassemblyDumps
;
10934 if (gZealBits
&& gZealFrequency
) {
10935 for (size_t i
= 0; i
< size_t(gc::ZealMode::Count
); i
++) {
10936 if (gZealBits
& (1 << i
)) {
10937 cx
->runtime()->gc
.setZeal(i
, gZealFrequency
);
10943 JS_SetNativeStackQuota(cx
, gWorkerStackSize
);
10946 [[nodiscard
]] static bool PrintUnhandledRejection(
10947 JSContext
* cx
, Handle
<PromiseObject
*> promise
) {
10948 RootedValue
reason(cx
, promise
->reason());
10949 RootedObject
site(cx
, promise
->resolutionSite());
10951 RootedString
str(cx
, JS_ValueToSource(cx
, reason
));
10956 UniqueChars utf8chars
= JS_EncodeStringToUTF8(cx
, str
);
10961 FILE* fp
= ErrorFilePointer();
10962 fprintf(fp
, "Unhandled rejection: %s\n", utf8chars
.get());
10965 fputs("(no stack trace available)\n", stderr
);
10969 JSPrincipals
* principals
= cx
->realm()->principals();
10970 RootedString
stackStr(cx
);
10971 if (!BuildStackString(cx
, principals
, site
, &stackStr
, 2)) {
10975 UniqueChars stack
= JS_EncodeStringToUTF8(cx
, stackStr
);
10980 fputs("Stack:\n", fp
);
10981 fputs(stack
.get(), fp
);
10986 [[nodiscard
]] static bool ReportUnhandledRejections(JSContext
* cx
) {
10987 ShellContext
* sc
= GetShellContext(cx
);
10988 if (!sc
->trackUnhandledRejections
) {
10992 if (!sc
->unhandledRejectedPromises
) {
10996 AutoRealm
ar(cx
, sc
->unhandledRejectedPromises
);
10998 if (!SetObject::size(cx
, sc
->unhandledRejectedPromises
)) {
11002 sc
->exitCode
= EXITCODE_RUNTIME_ERROR
;
11004 RootedValue
iter(cx
);
11005 if (!SetObject::iterator(cx
, SetObject::IteratorKind::Values
,
11006 sc
->unhandledRejectedPromises
, &iter
)) {
11010 Rooted
<SetIteratorObject
*> iterObj(cx
,
11011 &iter
.toObject().as
<SetIteratorObject
>());
11012 JSObject
* obj
= SetIteratorObject::createResult(cx
);
11017 Rooted
<ArrayObject
*> resultObj(cx
, &obj
->as
<ArrayObject
>());
11019 bool done
= SetIteratorObject::next(iterObj
, resultObj
);
11024 RootedObject
obj(cx
, &resultObj
->getDenseElement(0).toObject());
11025 Rooted
<PromiseObject
*> promise(cx
, obj
->maybeUnwrapIf
<PromiseObject
>());
11027 FILE* fp
= ErrorFilePointer();
11029 "Unhandled rejection: dead proxy found in unhandled "
11030 "rejections set\n",
11035 AutoRealm
ar2(cx
, promise
);
11037 if (!PrintUnhandledRejection(cx
, promise
)) {
11042 sc
->unhandledRejectedPromises
= nullptr;
11047 bool ShellContext::registerWithCx(JSContext
* cx
) {
11049 JS_SetContextPrivate(cx
, this);
11052 SetWorkerContextOptions(cx
);
11055 JS::SetWarningReporter(cx
, WarningReporter
);
11056 JS_SetFutexCanWait(cx
);
11057 JS_InitDestroyPrincipalsCallback(cx
, ShellPrincipals::destroy
);
11058 JS_SetDestroyCompartmentCallback(cx
, DestroyShellCompartmentPrivate
);
11059 js::SetWindowProxyClass(cx
, &ShellWindowProxyClass
);
11061 js::UseInternalJobQueues(cx
);
11063 js::SetPreserveWrapperCallbacks(cx
, DummyPreserveWrapperCallback
,
11064 DummyHasReleasedWrapperCallback
);
11066 JS::SetHostCleanupFinalizationRegistryCallback(
11067 cx
, ShellCleanupFinalizationRegistryCallback
, this);
11068 JS_AddExtraGCRootsTracer(cx
, TraceBlackRoots
, nullptr);
11069 JS_SetGrayGCRootsTracer(cx
, TraceGrayRoots
, nullptr);
11074 ShellContext::~ShellContext() {
11075 markObservers
.reset();
11077 JS_SetContextPrivate(cx_
, nullptr);
11078 JS::SetHostCleanupFinalizationRegistryCallback(cx_
, nullptr, nullptr);
11079 JS_SetGrayGCRootsTracer(cx_
, nullptr, nullptr);
11080 JS_RemoveExtraGCRootsTracer(cx_
, TraceBlackRoots
, nullptr);
11082 MOZ_ASSERT(offThreadJobs
.empty());
11085 static int Shell(JSContext
* cx
, OptionParser
* op
) {
11086 #ifdef JS_STRUCTURED_SPEW
11087 cx
->spewer().enableSpewing();
11090 auto exitShell
= MakeScopeExit([&] {
11091 #ifdef JS_STRUCTURED_SPEW
11092 cx
->spewer().disableSpewing();
11096 #ifdef MOZ_CODE_COVERAGE
11097 InstallCoverageSignalHandlers();
11100 Maybe
<JS::AutoDisableGenerationalGC
> noggc
;
11101 if (op
->getBoolOption("no-ggc")) {
11105 Maybe
<AutoDisableCompactingGC
> nocgc
;
11106 if (op
->getBoolOption("no-cgc")) {
11110 if (op
->getBoolOption("fuzzing-safe")) {
11111 fuzzingSafe
= true;
11114 (getenv("MOZ_FUZZING_SAFE") && getenv("MOZ_FUZZING_SAFE")[0] != '0');
11118 if (op
->getBoolOption("differential-testing")) {
11119 JS::SetSupportDifferentialTesting(true);
11123 if (op
->getBoolOption("disable-oom-functions")) {
11124 disableOOMFunctions
= true;
11127 if (op
->getBoolOption("more-compartments")) {
11128 defaultToSameCompartment
= false;
11131 bool reprl_mode
= FuzzilliUseReprlMode(op
);
11133 // Begin REPRL Loop
11134 int result
= EXIT_SUCCESS
;
11136 JS::RealmOptions options
;
11137 SetStandardRealmOptions(options
);
11139 cx
, NewGlobalObject(cx
, options
, nullptr, ShellGlobalKind::WindowProxy
,
11140 /* immutablePrototype = */ true));
11145 JSAutoRealm
ar(cx
, glob
);
11147 ShellContext
* sc
= GetShellContext(cx
);
11148 if (!sc
->moduleLoader
&& !InitModuleLoader(cx
, *op
)) {
11149 return EXIT_FAILURE
;
11152 #ifdef FUZZING_INTERFACES
11153 if (fuzzHaveModule
) {
11154 return FuzzJSRuntimeStart(cx
, &sArgc
, &sArgv
);
11159 result
= EXIT_SUCCESS
;
11161 AutoReportException
are(cx
);
11162 if (!ProcessArgs(cx
, op
) && !sc
->quitting
) {
11163 result
= EXITCODE_RUNTIME_ERROR
;
11168 * The job queue must be drained even on error to finish outstanding async
11169 * tasks before the main thread JSRuntime is torn down. Drain after
11170 * uncaught exceptions have been reported since draining runs callbacks.
11174 // Only if there's no other error, report unhandled rejections.
11175 if (!result
&& !sc
->exitCode
) {
11176 AutoReportException
are(cx
);
11177 if (!ReportUnhandledRejections(cx
)) {
11178 FILE* fp
= ErrorFilePointer();
11179 fputs("Error while printing unhandled rejection\n", fp
);
11183 if (sc
->exitCode
) {
11184 result
= sc
->exitCode
;
11187 #ifdef FUZZING_JS_FUZZILLI
11191 // Send return code to parent and reset edge counters.
11195 uint32_t execHashInputs
;
11197 s
.status
= (result
& 0xff) << 8;
11198 s
.execHash
= cx
->executionHash
;
11199 s
.execHashInputs
= cx
->executionHashInputs
;
11200 MOZ_RELEASE_ASSERT(write(REPRL_CWFD
, &s
, 12) == 12);
11201 __sanitizer_cov_reset_edgeguards();
11202 cx
->executionHash
= 1;
11203 cx
->executionHashInputs
= 0;
11207 if (enableDisassemblyDumps
) {
11208 AutoReportException
are(cx
);
11209 if (!js::DumpRealmPCCounts(cx
)) {
11210 result
= EXITCODE_OUT_OF_MEMORY
;
11215 } while (reprl_mode
);
11220 // Used to allocate memory when jemalloc isn't yet initialized.
11221 JS_DECLARE_NEW_METHODS(SystemAlloc_New
, malloc
, static)
11223 static void SetOutputFile(const char* const envVar
, RCFile
* defaultOut
,
11224 RCFile
** outFileP
) {
11227 const char* outPath
= getenv(envVar
);
11229 if (outPath
&& *outPath
&& (newfp
= fopen(outPath
, "w"))) {
11230 outFile
= SystemAlloc_New
<RCFile
>(newfp
);
11232 outFile
= defaultOut
;
11236 MOZ_CRASH("Failed to allocate output file");
11239 outFile
->acquire();
11240 *outFileP
= outFile
;
11243 static void PreInit() {
11245 const char* crash_option
= getenv("XRE_NO_WINDOWS_CRASH_DIALOG");
11246 if (crash_option
&& crash_option
[0] == '1') {
11247 // Disable the segfault dialog. We want to fail the tests immediately
11248 // instead of hanging automation.
11249 UINT newMode
= SEM_NOGPFAULTERRORBOX
| SEM_NOOPENFILEERRORBOX
;
11250 UINT prevMode
= SetErrorMode(newMode
);
11251 SetErrorMode(prevMode
| newMode
);
11256 #ifndef JS_WITHOUT_NSPR
11257 class AutoLibraryLoader
{
11258 Vector
<PRLibrary
*, 4, SystemAllocPolicy
> libraries
;
11261 ~AutoLibraryLoader() {
11262 for (auto dll
: libraries
) {
11263 PR_UnloadLibrary(dll
);
11267 PRLibrary
* load(const char* path
) {
11269 libSpec
.type
= PR_LibSpec_Pathname
;
11270 libSpec
.value
.pathname
= path
;
11271 PRLibrary
* dll
= PR_LoadLibraryWithFlags(libSpec
, PR_LD_NOW
| PR_LD_GLOBAL
);
11273 fprintf(stderr
, "LoadLibrary '%s' failed with code %d\n", path
,
11275 MOZ_CRASH("Failed to load library");
11278 MOZ_ALWAYS_TRUE(libraries
.append(dll
));
11284 static bool ReadSelfHostedXDRFile(JSContext
* cx
, FileContents
& buf
) {
11285 FILE* file
= fopen(selfHostedXDRPath
, "rb");
11287 fprintf(stderr
, "Can't open self-hosted stencil XDR file.\n");
11290 AutoCloseFile
autoClose(file
);
11293 if (fstat(fileno(file
), &st
) < 0) {
11294 fprintf(stderr
, "Unable to stat self-hosted stencil XDR file.\n");
11298 if (st
.st_size
>= INT32_MAX
) {
11299 fprintf(stderr
, "self-hosted stencil XDR file too large.\n");
11302 uint32_t filesize
= uint32_t(st
.st_size
);
11304 if (!buf
.growBy(filesize
)) {
11307 size_t cc
= fread(buf
.begin(), 1, filesize
, file
);
11308 if (cc
!= filesize
) {
11309 fprintf(stderr
, "Short read on self-hosted stencil XDR file.\n");
11316 static bool WriteSelfHostedXDRFile(JSContext
* cx
, JS::SelfHostedCache buffer
) {
11317 FILE* file
= fopen(selfHostedXDRPath
, "wb");
11319 JS_ReportErrorUTF8(cx
, "Can't open self-hosted stencil XDR file.");
11322 AutoCloseFile
autoClose(file
);
11324 size_t cc
= fwrite(buffer
.Elements(), 1, buffer
.LengthBytes(), file
);
11325 if (cc
!= buffer
.LengthBytes()) {
11326 JS_ReportErrorUTF8(cx
, "Short write on self-hosted stencil XDR file.");
11333 static bool SetGCParameterFromArg(JSContext
* cx
, char* arg
) {
11334 char* c
= strchr(arg
, '=');
11337 "Error: --gc-param argument '%s' must be of the form "
11338 "name=decimalValue\n",
11344 const char* name
= arg
;
11345 const char* valueStr
= c
+ 1;
11349 if (!GetGCParameterInfo(name
, &key
, &writable
)) {
11350 fprintf(stderr
, "Error: Unknown GC parameter name '%s'\n", name
);
11351 fprintf(stderr
, "Writable GC parameter names are:\n");
11352 #define PRINT_WRITABLE_PARAM_NAME(name, _, writable) \
11354 fprintf(stderr, " %s\n", name); \
11356 FOR_EACH_GC_PARAM(PRINT_WRITABLE_PARAM_NAME
)
11357 #undef PRINT_WRITABLE_PARAM_NAME
11362 fprintf(stderr
, "Error: GC parameter '%s' is not writable\n", name
);
11366 char* end
= nullptr;
11367 unsigned long int value
= strtoul(valueStr
, &end
, 10);
11368 if (end
== valueStr
|| *end
) {
11370 "Error: Could not parse '%s' as decimal for GC parameter '%s'\n",
11375 uint32_t paramValue
= uint32_t(value
);
11376 if (value
== ULONG_MAX
|| value
!= paramValue
||
11377 !cx
->runtime()->gc
.setParameter(cx
, key
, paramValue
)) {
11378 fprintf(stderr
, "Error: Value %s is out of range for GC parameter '%s'\n",
11386 int main(int argc
, char** argv
) {
11394 setlocale(LC_ALL
, "");
11396 // Special-case stdout and stderr. We bump their refcounts to prevent them
11397 // from getting closed and then having some printf fail somewhere.
11398 RCFile
rcStdout(stdout
);
11399 rcStdout
.acquire();
11400 RCFile
rcStderr(stderr
);
11401 rcStderr
.acquire();
11403 SetOutputFile("JS_STDOUT", &rcStdout
, &gOutFile
);
11404 SetOutputFile("JS_STDERR", &rcStderr
, &gErrFile
);
11406 // Use a larger jemalloc page cache. This should match the value for browser
11407 // foreground processes in ContentChild::RecvNotifyProcessPriorityChanged.
11408 moz_set_max_dirty_page_modifier(4);
11410 OptionParser
op("Usage: {progname} [options] [[script] scriptArgs*]");
11411 if (!InitOptionParser(op
)) {
11412 return EXIT_FAILURE
;
11415 switch (op
.parseArgs(argc
, argv
)) {
11416 case OptionParser::EarlyExit
:
11417 return EXIT_SUCCESS
;
11418 case OptionParser::ParseError
:
11419 op
.printHelp(argv
[0]);
11420 return EXIT_FAILURE
;
11421 case OptionParser::Fail
:
11422 return EXIT_FAILURE
;
11423 case OptionParser::Okay
:
11427 if (op
.getHelpOption()) {
11428 return EXIT_SUCCESS
;
11431 if (!SetGlobalOptionsPreJSInit(op
)) {
11432 return EXIT_FAILURE
;
11435 // Start the engine.
11436 if (const char* message
= JS_InitWithFailureDiagnostic()) {
11437 fprintf(gErrFile
->fp
, "JS_Init failed: %s\n", message
);
11441 // `selfHostedXDRBuffer` contains XDR buffer of the self-hosted JS.
11442 // A part of it is borrowed by ImmutableScriptData of the self-hosted scripts.
11444 // This buffer should outlive JS_Shutdown.
11445 Maybe
<FileContents
> selfHostedXDRBuffer
;
11447 auto shutdownEngine
= MakeScopeExit([] { JS_ShutDown(); });
11449 if (!SetGlobalOptionsPostJSInit(op
)) {
11450 return EXIT_FAILURE
;
11453 // Record aggregated telemetry data on disk. Do this as early as possible such
11454 // that the telemetry is recording both before starting the context and after
11456 auto writeTelemetryResults
= MakeScopeExit([&op
] {
11457 if (telemetryLock
) {
11458 const char* dir
= op
.getStringOption("telemetry-dir");
11459 WriteTelemetryDataToDisk(dir
);
11460 js_free(telemetryLock
);
11461 telemetryLock
= nullptr;
11465 if (!InitSharedObjectMailbox()) {
11466 return EXIT_FAILURE
;
11469 JS::SetProcessBuildIdOp(ShellBuildId
);
11471 /* Use the same parameters as the browser in xpcjsruntime.cpp. */
11472 JSContext
* const cx
= JS_NewContext(JS::DefaultHeapMaxBytes
);
11477 // Register telemetry callbacks, if needed.
11478 if (telemetryLock
) {
11479 JS_SetAccumulateTelemetryCallback(cx
, AccumulateTelemetryDataCallback
);
11482 auto destroyCx
= MakeScopeExit([cx
] { JS_DestroyContext(cx
); });
11484 UniquePtr
<ShellContext
> sc
=
11485 MakeUnique
<ShellContext
>(cx
, ShellContext::MainThread
);
11486 if (!sc
|| !sc
->registerWithCx(cx
)) {
11490 if (!SetContextOptions(cx
, op
)) {
11494 JS_SetTrustedPrincipals(cx
, &ShellPrincipals::fullyTrusted
);
11495 JS_SetSecurityCallbacks(cx
, &ShellPrincipals::securityCallbacks
);
11497 JS_AddInterruptCallback(cx
, ShellInterruptCallback
);
11499 JS::SetGCSliceCallback(cx
, GCSliceCallback
);
11501 bufferStreamState
= js_new
<ExclusiveWaitableData
<BufferStreamState
>>(
11502 mutexid::BufferStreamState
);
11503 if (!bufferStreamState
) {
11506 auto shutdownBufferStreams
= MakeScopeExit([] {
11507 ShutdownBufferStreams();
11508 js_delete(bufferStreamState
);
11510 JS::InitConsumeStreamCallback(cx
, ConsumeBufferSource
, ReportStreamError
);
11512 JS::SetPromiseRejectionTrackerCallback(
11513 cx
, ForwardingPromiseRejectionTrackerCallback
);
11515 JS::dbg::SetDebuggerMallocSizeOf(cx
, moz_malloc_size_of
);
11517 auto shutdownShellThreads
= MakeScopeExit([cx
] {
11519 KillWorkerThreads(cx
);
11520 DestructSharedObjectMailbox();
11521 CancelOffThreadJobsForRuntime(cx
);
11524 // The file content should stay alive as long as Worker thread can be
11526 JS::SelfHostedCache xdrSpan
= nullptr;
11527 JS::SelfHostedWriter xdrWriter
= nullptr;
11528 if (selfHostedXDRPath
) {
11529 if (encodeSelfHostedCode
) {
11530 xdrWriter
= WriteSelfHostedXDRFile
;
11532 selfHostedXDRBuffer
.emplace(cx
);
11533 if (ReadSelfHostedXDRFile(cx
, *selfHostedXDRBuffer
)) {
11534 MOZ_ASSERT(selfHostedXDRBuffer
->length() > 0);
11535 JS::SelfHostedCache
span(selfHostedXDRBuffer
->begin(),
11536 selfHostedXDRBuffer
->end());
11539 fprintf(stderr
, "Falling back on parsing source.\n");
11540 selfHostedXDRPath
= nullptr;
11545 if (!JS::InitSelfHostedCode(cx
, xdrSpan
, xdrWriter
)) {
11549 EnvironmentPreparer
environmentPreparer(cx
);
11551 JS::SetProcessLargeAllocationFailureCallback(my_LargeAllocFailCallback
);
11553 if (op
.getBoolOption("wasm-compile-and-serialize")) {
11555 MOZ_CRASH("WASI doesn't support wasm");
11557 if (!WasmCompileAndSerialize(cx
)) {
11558 // Errors have been printed directly to stderr.
11559 MOZ_ASSERT(!cx
->isExceptionPending());
11560 return EXIT_FAILURE
;
11563 return EXIT_SUCCESS
;
11566 result
= Shell(cx
, &op
);
11569 if (OOM_printAllocationCount
) {
11570 printf("OOM max count: %" PRIu64
"\n", js::oom::simulator
.counter());
11577 bool InitOptionParser(OptionParser
& op
) {
11579 "The SpiderMonkey shell provides a command line interface to the "
11580 "JavaScript engine. Code and file options provided via the command line "
11582 "run left to right. If provided, the optional script argument is run "
11584 "all options have been processed. Just-In-Time compilation modes may be "
11586 "command line options.");
11587 op
.setDescriptionWidth(72);
11588 op
.setHelpWidth(80);
11589 op
.setVersion(JS_GetImplementationVersion());
11591 if (!op
.addMultiStringOption(
11592 'f', "file", "PATH",
11593 "File path to run, parsing file contents as UTF-8") ||
11594 !op
.addMultiStringOption(
11595 'u', "utf16-file", "PATH",
11596 "File path to run, inflating the file's UTF-8 contents to UTF-16 and "
11597 "then parsing that") ||
11598 !op
.addMultiStringOption('m', "module", "PATH", "Module path to run") ||
11599 !op
.addMultiStringOption('p', "prelude", "PATH", "Prelude path to run") ||
11600 !op
.addMultiStringOption('e', "execute", "CODE", "Inline code to run") ||
11601 !op
.addStringOption('\0', "selfhosted-xdr-path", "[filename]",
11602 "Read/Write selfhosted script data from/to the given "
11604 !op
.addStringOption('\0', "selfhosted-xdr-mode", "(encode,decode,off)",
11605 "Whether to encode/decode data of the file provided"
11606 "with --selfhosted-xdr-path.") ||
11607 !op
.addBoolOption('i', "shell", "Enter prompt after running code") ||
11608 !op
.addBoolOption('c', "compileonly",
11609 "Only compile, don't run (syntax checking mode)") ||
11610 !op
.addBoolOption('w', "warnings", "Emit warnings") ||
11611 !op
.addBoolOption('W', "nowarnings", "Don't emit warnings") ||
11612 !op
.addBoolOption('D', "dump-bytecode",
11613 "Dump bytecode with exec count for all scripts") ||
11614 !op
.addBoolOption('b', "print-timing",
11615 "Print sub-ms runtime for each file that's run") ||
11616 !op
.addBoolOption('\0', "code-coverage",
11617 "Enable code coverage instrumentation.") ||
11619 '\0', "disable-parser-deferred-alloc",
11620 "Disable deferred allocation of GC objects until after parser") ||
11622 !op
.addBoolOption('O', "print-alloc",
11623 "Print the number of allocations at exit") ||
11625 !op
.addOptionalStringArg("script",
11626 "A script to execute (after all options)") ||
11627 !op
.addOptionalMultiStringArg(
11629 "String arguments to bind as |scriptArgs| in the "
11630 "shell's global") ||
11632 '\0', "cpu-count", "COUNT",
11633 "Set the number of CPUs (hardware threads) to COUNT, the "
11634 "default is the actual number of CPUs. The total number of "
11635 "background helper threads is the CPU count plus some constant.",
11637 !op
.addIntOption('\0', "thread-count", "COUNT", "Alias for --cpu-count.",
11639 !op
.addBoolOption('\0', "ion", "Enable IonMonkey (default)") ||
11640 !op
.addBoolOption('\0', "no-ion", "Disable IonMonkey") ||
11641 !op
.addBoolOption('\0', "no-ion-for-main-context",
11642 "Disable IonMonkey for the main context only") ||
11643 !op
.addIntOption('\0', "inlining-entry-threshold", "COUNT",
11644 "The minimum stub entry count before trial-inlining a"
11647 !op
.addIntOption('\0', "small-function-length", "COUNT",
11648 "The maximum bytecode length of a 'small function' for "
11649 "the purpose of inlining.",
11651 !op
.addBoolOption('\0', "only-inline-selfhosted",
11652 "Only inline selfhosted functions") ||
11653 !op
.addBoolOption('\0', "no-asmjs", "Disable asm.js compilation") ||
11654 !op
.addStringOption(
11655 '\0', "wasm-compiler", "[option]",
11656 "Choose to enable a subset of the wasm compilers, valid options are "
11657 "'none', 'baseline', 'ion', 'optimizing', "
11658 "'baseline+ion', 'baseline+optimizing'.") ||
11659 !op
.addBoolOption('\0', "wasm-verbose",
11660 "Enable WebAssembly verbose logging") ||
11661 !op
.addBoolOption('\0', "disable-wasm-huge-memory",
11662 "Disable WebAssembly huge memory") ||
11663 !op
.addBoolOption('\0', "test-wasm-await-tier2",
11664 "Forcibly activate tiering and block "
11665 "instantiation on completion of tier2") ||
11666 #define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \
11667 FLAG_PRED, FLAG_FORCE_ON, FLAG_FUZZ_ON, SHELL, ...) \
11668 !op.addBoolOption('\0', "no-wasm-" SHELL, \
11669 STAGE == WasmFeatureStage::Experimental \
11671 : "Disable wasm " SHELL " feature.") || \
11672 !op.addBoolOption('\0', "wasm-" SHELL, \
11673 STAGE == WasmFeatureStage::Experimental \
11674 ? "Enable wasm " SHELL " feature." \
11676 JS_FOR_WASM_FEATURES(WASM_FEATURE
)
11677 #undef WASM_FEATURE
11678 !op
.addBoolOption('\0', "no-native-regexp",
11679 "Disable native regexp compilation") ||
11681 '\0', "regexp-warmup-threshold", "COUNT",
11682 "Wait for COUNT invocations before compiling regexps to native code "
11685 !op
.addBoolOption('\0', "trace-regexp-parser", "Trace regexp parsing") ||
11686 !op
.addBoolOption('\0', "trace-regexp-assembler",
11687 "Trace regexp assembler") ||
11688 !op
.addBoolOption('\0', "trace-regexp-interpreter",
11689 "Trace regexp interpreter") ||
11690 !op
.addBoolOption('\0', "trace-regexp-peephole",
11691 "Trace regexp peephole optimization") ||
11692 !op
.addBoolOption('\0', "less-debug-code",
11693 "Emit less machine code for "
11694 "checking assertions under DEBUG.") ||
11695 !op
.addBoolOption('\0', "disable-weak-refs", "Disable weak references") ||
11696 !op
.addBoolOption('\0', "disable-tosource", "Disable toSource/uneval") ||
11697 !op
.addBoolOption('\0', "disable-property-error-message-fix",
11698 "Disable fix for the error message when accessing "
11699 "property of null or undefined") ||
11700 !op
.addBoolOption('\0', "enable-iterator-helpers",
11701 "Enable iterator helpers") ||
11702 !op
.addBoolOption('\0', "enable-shadow-realms", "Enable ShadowRealms") ||
11703 !op
.addBoolOption('\0', "disable-array-grouping",
11704 "Disable Object.groupBy and Map.groupBy") ||
11705 !op
.addBoolOption('\0', "disable-well-formed-unicode-strings",
11706 "Disable String.prototype.{is,to}WellFormed() methods"
11707 "(Well-Formed Unicode Strings) (default: Enabled)") ||
11708 !op
.addBoolOption('\0', "enable-new-set-methods",
11709 "Enable New Set methods") ||
11710 !op
.addBoolOption('\0', "disable-arraybuffer-transfer",
11711 "Disable ArrayBuffer.prototype.transfer() methods") ||
11712 !op
.addBoolOption('\0', "enable-symbols-as-weakmap-keys",
11713 "Enable Symbols As WeakMap keys") ||
11715 '\0', "enable-arraybuffer-resizable",
11716 "Enable resizable ArrayBuffers and growable SharedArrayBuffers") ||
11717 !op
.addBoolOption('\0', "enable-top-level-await",
11718 "Enable top-level await") ||
11719 !op
.addBoolOption('\0', "enable-class-static-blocks",
11720 "(no-op) Enable class static blocks") ||
11721 !op
.addBoolOption('\0', "enable-import-assertions",
11722 "Enable import attributes with old assert syntax") ||
11723 !op
.addBoolOption('\0', "enable-import-attributes",
11724 "Enable import attributes") ||
11725 !op
.addBoolOption('\0', "disable-destructuring-fuse",
11726 "Disable Destructuring Fuse") ||
11727 !op
.addStringOption('\0', "shared-memory", "on/off",
11728 "SharedArrayBuffer and Atomics "
11729 #if SHARED_MEMORY_DEFAULT
11730 "(default: on, off to disable)"
11732 "(default: off, on to enable)"
11735 !op
.addStringOption('\0', "spectre-mitigations", "on/off",
11736 "Whether Spectre mitigations are enabled (default: "
11737 "off, on to enable)") ||
11738 !op
.addStringOption('\0', "write-protect-code", "on/off",
11739 "Whether the W^X policy is enforced to mark JIT code "
11740 "pages as either writable or executable but never "
11741 "both at the same time (default: on, off to "
11743 !op
.addStringOption('\0', "cache-ir-stubs", "on/off/call",
11744 "Use CacheIR stubs (default: on, off to disable, "
11745 "call to enable work-in-progress call ICs)") ||
11746 !op
.addStringOption('\0', "ion-shared-stubs", "on/off",
11747 "Use shared stubs (default: on, off to disable)") ||
11748 !op
.addStringOption('\0', "ion-scalar-replacement", "on/off",
11749 "Scalar Replacement (default: on, off to disable)") ||
11750 !op
.addStringOption('\0', "ion-gvn", "[mode]",
11751 "Specify Ion global value numbering:\n"
11752 " off: disable GVN\n"
11753 " on: enable GVN (default)\n") ||
11754 !op
.addStringOption(
11755 '\0', "ion-licm", "on/off",
11756 "Loop invariant code motion (default: on, off to disable)") ||
11757 !op
.addStringOption('\0', "ion-edgecase-analysis", "on/off",
11758 "Find edge cases where Ion can avoid bailouts "
11759 "(default: on, off to disable)") ||
11760 !op
.addStringOption('\0', "ion-pruning", "on/off",
11761 "Branch pruning (default: on, off to disable)") ||
11762 !op
.addStringOption('\0', "ion-range-analysis", "on/off",
11763 "Range analysis (default: on, off to disable)") ||
11764 !op
.addStringOption('\0', "ion-sink", "on/off",
11765 "Sink code motion (default: off, on to enable)") ||
11766 !op
.addStringOption('\0', "ion-optimization-levels", "on/off",
11767 "No-op for fuzzing") ||
11768 !op
.addStringOption('\0', "ion-loop-unrolling", "on/off",
11769 "(NOP for fuzzers)") ||
11770 !op
.addStringOption(
11771 '\0', "ion-instruction-reordering", "on/off",
11772 "Instruction reordering (default: off, on to enable)") ||
11773 !op
.addStringOption(
11774 '\0', "ion-optimize-shapeguards", "on/off",
11775 "Eliminate redundant shape guards (default: on, off to disable)") ||
11776 !op
.addStringOption(
11777 '\0', "ion-optimize-gcbarriers", "on/off",
11778 "Eliminate redundant GC barriers (default: on, off to disable)") ||
11779 !op
.addStringOption('\0', "ion-iterator-indices", "on/off",
11780 "Optimize property access in for-in loops "
11781 "(default: on, off to disable)") ||
11782 !op
.addStringOption('\0', "ion-load-keys", "on/off",
11783 "Atomize property loads used as keys "
11784 "(default: on, off to disable)") ||
11785 !op
.addBoolOption('\0', "ion-check-range-analysis",
11786 "Range analysis checking") ||
11787 !op
.addBoolOption('\0', "ion-extra-checks",
11788 "Perform extra dynamic validation checks") ||
11789 !op
.addStringOption(
11790 '\0', "ion-inlining", "on/off",
11791 "Inline methods where possible (default: on, off to disable)") ||
11792 !op
.addStringOption(
11793 '\0', "ion-osr", "on/off",
11794 "On-Stack Replacement (default: on, off to disable)") ||
11795 !op
.addBoolOption('\0', "disable-bailout-loop-check",
11796 "Turn off bailout loop check") ||
11797 !op
.addBoolOption('\0', "enable-ic-frame-pointers",
11798 "Use frame pointers in all IC stubs") ||
11799 !op
.addBoolOption('\0', "scalar-replace-arguments",
11800 "Use scalar replacement to optimize ArgumentsObject") ||
11801 !op
.addStringOption(
11802 '\0', "ion-limit-script-size", "on/off",
11803 "Don't compile very large scripts (default: on, off to disable)") ||
11804 !op
.addIntOption('\0', "ion-warmup-threshold", "COUNT",
11805 "Wait for COUNT calls or iterations before compiling "
11806 "at the normal optimization level (default: 1000)",
11808 !op
.addIntOption('\0', "ion-full-warmup-threshold", "COUNT",
11809 "No-op for fuzzing", -1) ||
11810 !op
.addStringOption(
11811 '\0', "ion-regalloc", "[mode]",
11812 "Specify Ion register allocation:\n"
11813 " backtracking: Priority based backtracking register allocation "
11815 " testbed: Backtracking allocator with experimental features\n"
11816 " stupid: Simple block local register allocation") ||
11819 "Always ion-compile methods (implies --baseline-eager)") ||
11820 !op
.addBoolOption('\0', "fast-warmup",
11821 "Reduce warmup thresholds for each tier.") ||
11822 !op
.addStringOption('\0', "ion-offthread-compile", "on/off",
11823 "Compile scripts off thread (default: on)") ||
11824 !op
.addStringOption('\0', "ion-parallel-compile", "on/off",
11825 "--ion-parallel compile is deprecated. Use "
11826 "--ion-offthread-compile.") ||
11827 !op
.addBoolOption('\0', "baseline",
11828 "Enable baseline compiler (default)") ||
11829 !op
.addBoolOption('\0', "no-baseline", "Disable baseline compiler") ||
11830 !op
.addBoolOption('\0', "baseline-eager",
11831 "Always baseline-compile methods") ||
11832 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
11833 !op
.addBoolOption('\0', "portable-baseline-eager",
11834 "Always use the porbale baseline interpreter") ||
11835 !op
.addBoolOption('\0', "portable-baseline",
11836 "Enable Portable Baseline Interpreter (default)") ||
11837 !op
.addBoolOption('\0', "no-portable-baseline",
11838 "Disable Portable Baseline Interpreter") ||
11841 '\0', "baseline-warmup-threshold", "COUNT",
11842 "Wait for COUNT calls or iterations before baseline-compiling "
11845 !op
.addBoolOption('\0', "blinterp",
11846 "Enable Baseline Interpreter (default)") ||
11847 !op
.addBoolOption('\0', "no-blinterp", "Disable Baseline Interpreter") ||
11848 !op
.addBoolOption('\0', "disable-jithints",
11849 "Disable caching eager baseline compilation hints.") ||
11851 '\0', "emit-interpreter-entry",
11852 "Emit Interpreter entry trampolines (default under --enable-perf)") ||
11854 '\0', "no-emit-interpreter-entry",
11855 "Do not emit Interpreter entry trampolines (default).") ||
11856 !op
.addBoolOption('\0', "blinterp-eager",
11857 "Always Baseline-interpret scripts") ||
11859 '\0', "blinterp-warmup-threshold", "COUNT",
11860 "Wait for COUNT calls or iterations before Baseline-interpreting "
11864 '\0', "trial-inlining-warmup-threshold", "COUNT",
11865 "Wait for COUNT calls or iterations before trial-inlining "
11868 !op
.addStringOption(
11869 '\0', "monomorphic-inlining", "default/always/never",
11870 "Whether monomorphic inlining is used instead of trial inlining "
11871 "always, never, or based on heuristics (default)") ||
11873 '\0', "non-writable-jitcode",
11874 "(NOP for fuzzers) Allocate JIT code as non-writable memory.") ||
11877 "Pretend CPU does not support SSE3 instructions and above "
11878 "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
11881 "Pretend CPU does not support SSSE3 [sic] instructions and above "
11882 "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
11885 "Pretend CPU does not support SSE4.1 instructions "
11886 "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
11887 !op
.addBoolOption('\0', "no-sse4", "Alias for --no-sse41") ||
11890 "Pretend CPU does not support SSE4.2 instructions "
11891 "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
11892 #ifdef ENABLE_WASM_AVX
11893 !op
.addBoolOption('\0', "enable-avx",
11894 "No-op. AVX is enabled by default, if available.") ||
11897 "Pretend CPU does not support AVX or AVX2 instructions "
11898 "to test JIT codegen (no-op on platforms other than x86 and x64).") ||
11900 !op
.addBoolOption('\0', "enable-avx",
11901 "AVX is disabled by default. Enable AVX. "
11902 "(no-op on platforms other than x86 and x64).") ||
11903 !op
.addBoolOption('\0', "no-avx",
11904 "No-op. AVX is currently disabled by default.") ||
11906 !op
.addBoolOption('\0', "more-compartments",
11907 "Make newGlobal default to creating a new "
11909 !op
.addBoolOption('\0', "fuzzing-safe",
11910 "Don't expose functions that aren't safe for "
11911 "fuzzers to call") ||
11913 !op
.addBoolOption('\0', "differential-testing",
11914 "Avoid random/undefined behavior that disturbs "
11915 "differential testing (correctness fuzzing)") ||
11917 !op
.addBoolOption('\0', "disable-oom-functions",
11918 "Disable functions that cause "
11919 "artificial OOMs") ||
11920 !op
.addBoolOption('\0', "no-threads", "Disable helper threads") ||
11922 '\0', "no-jit-backend",
11923 "Disable the JIT backend completely for this process") ||
11925 !op
.addBoolOption('\0', "dump-entrained-variables",
11926 "Print variables which are "
11927 "unnecessarily entrained by inner functions") ||
11929 !op
.addBoolOption('\0', "no-ggc", "Disable Generational GC") ||
11930 !op
.addBoolOption('\0', "no-cgc", "Disable Compacting GC") ||
11931 !op
.addBoolOption('\0', "no-incremental-gc", "Disable Incremental GC") ||
11932 !op
.addBoolOption('\0', "no-parallel-marking",
11933 "Disable GC parallel marking") ||
11934 !op
.addBoolOption('\0', "enable-parallel-marking",
11935 "Enable GC parallel marking") ||
11937 '\0', "marking-threads", "COUNT",
11938 "Set the number of threads used for parallel marking to COUNT.", 0) ||
11939 !op
.addStringOption('\0', "nursery-strings", "on/off",
11940 "Allocate strings in the nursery") ||
11941 !op
.addStringOption('\0', "nursery-bigints", "on/off",
11942 "Allocate BigInts in the nursery") ||
11943 !op
.addIntOption('\0', "available-memory", "SIZE",
11944 "Select GC settings based on available memory (MB)",
11946 !op
.addStringOption('\0', "arm-hwcap", "[features]",
11947 "Specify ARM code generation features, or 'help' to "
11948 "list all features.") ||
11949 !op
.addIntOption('\0', "arm-asm-nop-fill", "SIZE",
11950 "Insert the given number of NOP instructions at all "
11951 "possible pool locations.",
11953 !op
.addIntOption('\0', "asm-pool-max-offset", "OFFSET",
11954 "The maximum pc relative OFFSET permitted in pool "
11955 "reference instructions.",
11957 !op
.addBoolOption('\0', "arm-sim-icache-checks",
11958 "Enable icache flush checks in the ARM "
11960 !op
.addIntOption('\0', "arm-sim-stop-at", "NUMBER",
11961 "Stop the ARM simulator after the given "
11962 "NUMBER of instructions.",
11964 !op
.addBoolOption('\0', "mips-sim-icache-checks",
11965 "Enable icache flush checks in the MIPS "
11967 !op
.addIntOption('\0', "mips-sim-stop-at", "NUMBER",
11968 "Stop the MIPS simulator after the given "
11969 "NUMBER of instructions.",
11971 !op
.addBoolOption('\0', "loong64-sim-icache-checks",
11972 "Enable icache flush checks in the LoongArch64 "
11974 !op
.addIntOption('\0', "loong64-sim-stop-at", "NUMBER",
11975 "Stop the LoongArch64 simulator after the given "
11976 "NUMBER of instructions.",
11978 #ifdef JS_CODEGEN_RISCV64
11979 !op
.addBoolOption('\0', "riscv-debug", "debug print riscv info.") ||
11981 #ifdef JS_SIMULATOR_RISCV64
11982 !op
.addBoolOption('\0', "trace-sim", "print simulator info.") ||
11983 !op
.addBoolOption('\0', "debug-sim", "debug simulator.") ||
11984 !op
.addBoolOption('\0', "riscv-trap-to-simulator-debugger",
11985 "trap into simulator debuggger.") ||
11986 !op
.addIntOption('\0', "riscv-sim-stop-at", "NUMBER",
11987 "Stop the riscv simulator after the given "
11988 "NUMBER of instructions.",
11991 !op
.addIntOption('\0', "nursery-size", "SIZE-MB",
11992 "Set the maximum nursery size in MB",
11993 JS::DefaultNurseryMaxBytes
/ 1024 / 1024) ||
11995 !op
.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]",
11996 gc::ZealModeHelpText
) ||
11998 !op
.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]",
11999 "option ignored in non-gc-zeal builds") ||
12001 !op
.addMultiStringOption('\0', "gc-param", "NAME=VALUE",
12002 "Set a named GC parameter") ||
12003 !op
.addStringOption('\0', "module-load-path", "DIR",
12004 "Set directory to load modules from") ||
12005 !op
.addBoolOption('\0', "no-source-pragmas",
12006 "Disable source(Mapping)URL pragma parsing") ||
12007 !op
.addBoolOption('\0', "no-async-stacks", "Disable async stacks") ||
12008 !op
.addBoolOption('\0', "async-stacks-capture-debuggee-only",
12009 "Limit async stack capture to only debuggees") ||
12010 !op
.addMultiStringOption('\0', "dll", "LIBRARY",
12011 "Dynamically load LIBRARY") ||
12012 !op
.addBoolOption('\0', "suppress-minidump",
12013 "Suppress crash minidumps") ||
12014 #ifdef JS_ENABLE_SMOOSH
12015 !op
.addBoolOption('\0', "smoosh", "Use SmooshMonkey") ||
12016 !op
.addStringOption('\0', "not-implemented-watchfile", "[filename]",
12017 "Track NotImplemented errors in the new frontend") ||
12019 !op
.addBoolOption('\0', "smoosh", "No-op") ||
12021 !op
.addStringOption(
12022 '\0', "delazification-mode", "[option]",
12023 "Select one of the delazification mode for scripts given on the "
12024 "command line, valid options are: "
12025 "'on-demand', 'concurrent-df', 'eager', 'concurrent-df+on-demand'. "
12026 "Choosing 'concurrent-df+on-demand' will run both concurrent-df and "
12027 "on-demand delazification mode, and compare compilation outcome. ") ||
12028 !op
.addBoolOption('\0', "wasm-compile-and-serialize",
12029 "Compile the wasm bytecode from stdin and serialize "
12030 "the results to stdout") ||
12031 #ifdef FUZZING_JS_FUZZILLI
12032 !op
.addBoolOption('\0', "reprl", "Enable REPRL mode for fuzzing") ||
12034 !op
.addStringOption('\0', "telemetry-dir", "[directory]",
12035 "Output telemetry results in a directory") ||
12036 !op
.addBoolOption('\0', "use-fdlibm-for-sin-cos-tan",
12037 "Use fdlibm for Math.sin, Math.cos, and Math.tan")) {
12041 op
.setArgTerminatesOptions("script", true);
12042 op
.setArgCapturesRest("scriptArgs");
12047 bool SetGlobalOptionsPreJSInit(const OptionParser
& op
) {
12048 // Note: DisableJitBackend must be called before JS_InitWithFailureDiagnostic.
12049 if (op
.getBoolOption("no-jit-backend")) {
12050 JS::DisableJitBackend();
12053 #if defined(JS_CODEGEN_ARM)
12054 if (const char* str
= op
.getStringOption("arm-hwcap")) {
12055 jit::SetARMHwCapFlagsString(str
);
12058 int32_t fill
= op
.getIntOption("arm-asm-nop-fill");
12060 jit::Assembler::NopFill
= fill
;
12063 int32_t poolMaxOffset
= op
.getIntOption("asm-pool-max-offset");
12064 if (poolMaxOffset
>= 5 && poolMaxOffset
<= 1024) {
12065 jit::Assembler::AsmPoolMaxOffset
= poolMaxOffset
;
12069 // Fish around in `op` for various important compiler-configuration flags
12070 // and make sure they get handed on to any child processes we might create.
12071 // See bug 1700900. Semantically speaking, this is all rather dubious:
12073 // * What set of flags need to be propagated in order to guarantee that the
12074 // child produces code that is "compatible" (in whatever sense) with that
12075 // produced by the parent? This isn't always easy to determine.
12077 // * There's nothing that ensures that flags given to the child are
12078 // presented in the same order that they exist in the parent's `argv[]`.
12079 // That could be a problem in the case where two flags with contradictory
12080 // meanings are given, and they are presented to the child in the opposite
12081 // order. For example: --wasm-compiler=optimizing --wasm-compiler=baseline.
12083 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
12084 MOZ_ASSERT(!js::jit::CPUFlagsHaveBeenComputed());
12086 if (op
.getBoolOption("no-sse3")) {
12087 js::jit::CPUInfo::SetSSE3Disabled();
12088 if (!sCompilerProcessFlags
.append("--no-sse3")) {
12092 if (op
.getBoolOption("no-ssse3")) {
12093 js::jit::CPUInfo::SetSSSE3Disabled();
12094 if (!sCompilerProcessFlags
.append("--no-ssse3")) {
12098 if (op
.getBoolOption("no-sse4") || op
.getBoolOption("no-sse41")) {
12099 js::jit::CPUInfo::SetSSE41Disabled();
12100 if (!sCompilerProcessFlags
.append("--no-sse41")) {
12104 if (op
.getBoolOption("no-sse42")) {
12105 js::jit::CPUInfo::SetSSE42Disabled();
12106 if (!sCompilerProcessFlags
.append("--no-sse42")) {
12110 if (op
.getBoolOption("no-avx")) {
12111 js::jit::CPUInfo::SetAVXDisabled();
12112 if (!sCompilerProcessFlags
.append("--no-avx")) {
12116 if (op
.getBoolOption("enable-avx")) {
12117 js::jit::CPUInfo::SetAVXEnabled();
12118 if (!sCompilerProcessFlags
.append("--enable-avx")) {
12127 bool SetGlobalOptionsPostJSInit(const OptionParser
& op
) {
12128 if (op
.getStringOption("telemetry-dir")) {
12129 MOZ_ASSERT(!telemetryLock
);
12130 telemetryLock
= js_new
<Mutex
>(mutexid::ShellTelemetry
);
12131 if (!telemetryLock
) {
12136 // Allow dumping on Linux with the fuzzing flag set, even when running with
12137 // the suid/sgid flag set on the shell.
12139 if (op
.getBoolOption("fuzzing-safe")) {
12140 prctl(PR_SET_DUMPABLE
, 1);
12146 * Process OOM options as early as possible so that we can observe as many
12147 * allocations as possible.
12149 OOM_printAllocationCount
= op
.getBoolOption('O');
12152 if (op
.getBoolOption("no-threads")) {
12153 js::DisableExtraThreads();
12156 enableCodeCoverage
= op
.getBoolOption("code-coverage");
12157 if (enableCodeCoverage
) {
12158 js::EnableCodeCoverage();
12161 // If LCov is enabled, then the default delazification mode should be changed
12162 // to parse everything eagerly, such that we know the location of every
12163 // instruction, to report them in the LCov summary, even if there is no uses
12164 // of these instructions.
12166 // Note: code coverage can be enabled either using the --code-coverage command
12167 // line, or the JS_CODE_COVERAGE_OUTPUT_DIR environment variable, which is
12168 // processed by JS_InitWithFailureDiagnostic.
12169 if (coverage::IsLCovEnabled()) {
12170 defaultDelazificationMode
=
12171 JS::DelazificationOption::ParseEverythingEagerly
;
12174 if (const char* xdr
= op
.getStringOption("selfhosted-xdr-path")) {
12175 shell::selfHostedXDRPath
= xdr
;
12177 if (const char* opt
= op
.getStringOption("selfhosted-xdr-mode")) {
12178 if (strcmp(opt
, "encode") == 0) {
12179 shell::encodeSelfHostedCode
= true;
12180 } else if (strcmp(opt
, "decode") == 0) {
12181 shell::encodeSelfHostedCode
= false;
12182 } else if (strcmp(opt
, "off") == 0) {
12183 shell::selfHostedXDRPath
= nullptr;
12186 "invalid option value for --selfhosted-xdr-mode, must be "
12191 #ifdef JS_WITHOUT_NSPR
12192 if (!op
.getMultiStringOption("dll").empty()) {
12193 fprintf(stderr
, "Error: --dll requires NSPR support!\n");
12197 AutoLibraryLoader loader
;
12198 MultiStringRange dllPaths
= op
.getMultiStringOption("dll");
12199 while (!dllPaths
.empty()) {
12200 char* path
= dllPaths
.front();
12202 dllPaths
.popFront();
12206 if (op
.getBoolOption("suppress-minidump")) {
12207 js::NoteIntentionalCrash();
12210 // The fake CPU count must be set before initializing the Runtime,
12211 // which spins up the thread pool.
12212 int32_t cpuCount
= op
.getIntOption("cpu-count"); // What we're really setting
12213 if (cpuCount
< 0) {
12214 cpuCount
= op
.getIntOption("thread-count"); // Legacy name
12216 if (cpuCount
>= 0 && !SetFakeCPUCount(cpuCount
)) {
12223 bool SetContextOptions(JSContext
* cx
, const OptionParser
& op
) {
12224 if (!SetContextWasmOptions(cx
, op
) || !SetContextJITOptions(cx
, op
) ||
12225 !SetContextGCOptions(cx
, op
)) {
12229 enableSourcePragmas
= !op
.getBoolOption("no-source-pragmas");
12230 enableAsyncStacks
= !op
.getBoolOption("no-async-stacks");
12231 enableAsyncStackCaptureDebuggeeOnly
=
12232 op
.getBoolOption("async-stacks-capture-debuggee-only");
12233 enableWeakRefs
= !op
.getBoolOption("disable-weak-refs");
12234 enableToSource
= !op
.getBoolOption("disable-tosource");
12235 enablePropertyErrorMessageFix
=
12236 !op
.getBoolOption("disable-property-error-message-fix");
12237 enableIteratorHelpers
= op
.getBoolOption("enable-iterator-helpers");
12238 enableShadowRealms
= op
.getBoolOption("enable-shadow-realms");
12239 enableWellFormedUnicodeStrings
=
12240 !op
.getBoolOption("disable-well-formed-unicode-strings");
12241 enableArrayGrouping
= !op
.getBoolOption("disable-array-grouping");
12242 #ifdef NIGHTLY_BUILD
12243 enableNewSetMethods
= op
.getBoolOption("enable-new-set-methods");
12244 enableSymbolsAsWeakMapKeys
=
12245 op
.getBoolOption("enable-symbols-as-weakmap-keys");
12246 enableArrayBufferResizable
= op
.getBoolOption("enable-arraybuffer-resizable");
12248 enableArrayBufferTransfer
= !op
.getBoolOption("disable-arraybuffer-transfer");
12249 enableImportAttributesAssertSyntax
=
12250 op
.getBoolOption("enable-import-assertions");
12251 enableImportAttributes
= op
.getBoolOption("enable-import-attributes") ||
12252 enableImportAttributesAssertSyntax
;
12253 enableDestructuringFuse
= !op
.getBoolOption("disable-destructuring-fuse");
12254 useFdlibmForSinCosTan
= op
.getBoolOption("use-fdlibm-for-sin-cos-tan");
12256 JS::ContextOptionsRef(cx
)
12257 .setSourcePragmas(enableSourcePragmas
)
12258 .setAsyncStack(enableAsyncStacks
)
12259 .setAsyncStackCaptureDebuggeeOnly(enableAsyncStackCaptureDebuggeeOnly
)
12260 .setImportAttributes(enableImportAttributes
)
12261 .setImportAttributesAssertSyntax(enableImportAttributesAssertSyntax
)
12262 .setEnableDestructuringFuse(enableDestructuringFuse
);
12264 JS::SetUseFdlibmForSinCosTan(useFdlibmForSinCosTan
);
12266 if (const char* str
= op
.getStringOption("shared-memory")) {
12267 if (strcmp(str
, "off") == 0) {
12268 enableSharedMemory
= false;
12269 } else if (strcmp(str
, "on") == 0) {
12270 enableSharedMemory
= true;
12272 return OptionFailure("shared-memory", str
);
12276 reportWarnings
= op
.getBoolOption('w');
12277 compileOnly
= op
.getBoolOption('c');
12278 printTiming
= op
.getBoolOption('b');
12279 enableDisassemblyDumps
= op
.getBoolOption('D');
12280 cx
->runtime()->profilingScripts
=
12281 enableCodeCoverage
|| enableDisassemblyDumps
;
12283 #ifdef JS_ENABLE_SMOOSH
12284 if (op
.getBoolOption("smoosh")) {
12285 JS::ContextOptionsRef(cx
).setTrySmoosh(true);
12286 js::frontend::InitSmoosh();
12289 if (const char* filename
= op
.getStringOption("not-implemented-watchfile")) {
12290 FILE* out
= fopen(filename
, "a");
12291 MOZ_RELEASE_ASSERT(out
);
12292 setbuf(out
, nullptr); // Make unbuffered
12293 cx
->runtime()->parserWatcherFile
.init(out
);
12294 JS::ContextOptionsRef(cx
).setTrackNotImplemented(true);
12298 if (const char* mode
= op
.getStringOption("delazification-mode")) {
12299 if (strcmp(mode
, "on-demand") == 0) {
12300 defaultDelazificationMode
= JS::DelazificationOption::OnDemandOnly
;
12301 } else if (strcmp(mode
, "concurrent-df") == 0) {
12302 defaultDelazificationMode
=
12303 JS::DelazificationOption::ConcurrentDepthFirst
;
12304 } else if (strcmp(mode
, "eager") == 0) {
12305 defaultDelazificationMode
=
12306 JS::DelazificationOption::ParseEverythingEagerly
;
12307 } else if (strcmp(mode
, "concurrent-df+on-demand") == 0 ||
12308 strcmp(mode
, "on-demand+concurrent-df") == 0) {
12309 defaultDelazificationMode
=
12310 JS::DelazificationOption::CheckConcurrentWithOnDemand
;
12312 return OptionFailure("delazification-mode", mode
);
12319 bool SetContextWasmOptions(JSContext
* cx
, const OptionParser
& op
) {
12320 enableAsmJS
= !op
.getBoolOption("no-asmjs");
12323 enableWasmBaseline
= true;
12324 enableWasmOptimizing
= true;
12326 if (const char* str
= op
.getStringOption("wasm-compiler")) {
12327 if (strcmp(str
, "none") == 0) {
12328 enableWasm
= false;
12329 } else if (strcmp(str
, "baseline") == 0) {
12330 MOZ_ASSERT(enableWasmBaseline
);
12331 enableWasmOptimizing
= false;
12332 } else if (strcmp(str
, "optimizing") == 0 ||
12333 strcmp(str
, "optimized") == 0) {
12334 enableWasmBaseline
= false;
12335 MOZ_ASSERT(enableWasmOptimizing
);
12336 } else if (strcmp(str
, "baseline+optimizing") == 0 ||
12337 strcmp(str
, "baseline+optimized") == 0) {
12338 MOZ_ASSERT(enableWasmBaseline
);
12339 MOZ_ASSERT(enableWasmOptimizing
);
12340 } else if (strcmp(str
, "ion") == 0) {
12341 enableWasmBaseline
= false;
12342 enableWasmOptimizing
= true;
12343 } else if (strcmp(str
, "baseline+ion") == 0) {
12344 MOZ_ASSERT(enableWasmBaseline
);
12345 enableWasmOptimizing
= true;
12347 return OptionFailure("wasm-compiler", str
);
12351 #define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \
12352 FLAG_PRED, FLAG_FORCE_ON, FLAG_FUZZ_ON, SHELL, ...) \
12353 if (STAGE == WasmFeatureStage::Experimental) { \
12354 enableWasm##NAME = op.getBoolOption("wasm-" SHELL); \
12356 enableWasm##NAME = !op.getBoolOption("no-wasm-" SHELL); \
12359 JS_FOR_WASM_FEATURES(WASM_FEATURE
);
12360 #undef WASM_FEATURE
12362 enableWasmVerbose
= op
.getBoolOption("wasm-verbose");
12363 enableTestWasmAwaitTier2
= op
.getBoolOption("test-wasm-await-tier2");
12365 JS::ContextOptionsRef(cx
)
12366 .setAsmJS(enableAsmJS
)
12367 .setWasm(enableWasm
)
12368 .setWasmForTrustedPrinciples(enableWasm
)
12369 .setWasmBaseline(enableWasmBaseline
)
12370 .setWasmIon(enableWasmOptimizing
)
12371 #define WASM_FEATURE(NAME, ...) .setWasm##NAME(enableWasm##NAME)
12372 JS_FOR_WASM_FEATURES(WASM_FEATURE
)
12373 #undef WASM_FEATURE
12377 // This must be set before self-hosted code is initialized, as self-hosted
12378 // code reads the property and the property may not be changed later.
12379 bool disabledHugeMemory
= false;
12380 if (op
.getBoolOption("disable-wasm-huge-memory")) {
12381 disabledHugeMemory
= JS::DisableWasmHugeMemory();
12382 MOZ_RELEASE_ASSERT(disabledHugeMemory
);
12385 // --disable-wasm-huge-memory needs to be propagated. See bug 1518210.
12386 if (disabledHugeMemory
&&
12387 !sCompilerProcessFlags
.append("--disable-wasm-huge-memory")) {
12391 // Also the following are to be propagated.
12392 const char* to_propagate
[] = {
12393 # define WASM_FEATURE(NAME, LOWER_NAME, STAGE, COMPILE_PRED, COMPILER_PRED, \
12394 FLAG_PRED, FLAG_FORCE_ON, FLAG_FUZZ_ON, SHELL, ...) \
12395 STAGE == WasmFeatureStage::Experimental ? "--wasm-" SHELL \
12396 : "--no-wasm-" SHELL,
12397 JS_FOR_WASM_FEATURES(WASM_FEATURE
)
12398 # undef WASM_FEATURE
12399 // Compiler selection options
12400 "--test-wasm-await-tier2",
12402 for (const char** p
= &to_propagate
[0]; *p
; p
++) {
12403 if (op
.getBoolOption(&(*p
)[2] /* 2 => skip the leading '--' */)) {
12404 if (!sCompilerProcessFlags
.append(*p
)) {
12410 // Also --wasm-compiler= is to be propagated. This is tricky because it is
12411 // necessary to reconstitute the --wasm-compiler=<whatever> string from its
12412 // pieces, without causing a leak. Hence it is copied into a static buffer.
12413 // This is thread-unsafe, but we're in `main()` and on the process' root
12414 // thread. Also, we do this only once -- it wouldn't work properly if we
12415 // handled multiple --wasm-compiler= flags in a loop.
12416 const char* wasm_compiler
= op
.getStringOption("wasm-compiler");
12417 if (wasm_compiler
) {
12419 2 + strlen("wasm-compiler") + 1 + strlen(wasm_compiler
) + 1;
12420 const size_t n_avail
= 128;
12421 static char buf
[n_avail
];
12422 // `n_needed` depends on the compiler name specified. However, it can't
12423 // be arbitrarily long, since previous flag-checking should have limited
12424 // it to a set of known possibilities: "baseline", "ion",
12425 // "baseline+ion", Still, assert this for safety.
12426 MOZ_RELEASE_ASSERT(n_needed
< n_avail
);
12427 memset(buf
, 0, sizeof(buf
));
12428 SprintfBuf(buf
, n_avail
, "--%s=%s", "wasm-compiler", wasm_compiler
);
12429 if (!sCompilerProcessFlags
.append(buf
)) {
12438 bool SetContextJITOptions(JSContext
* cx
, const OptionParser
& op
) {
12439 // Check --fast-warmup first because it sets default warm-up thresholds. These
12440 // thresholds can then be overridden below by --ion-eager and other flags.
12441 if (op
.getBoolOption("fast-warmup")) {
12442 jit::JitOptions
.setFastWarmUp();
12445 if (op
.getBoolOption("no-ion-for-main-context")) {
12446 JS::ContextOptionsRef(cx
).setDisableIon();
12449 if (const char* str
= op
.getStringOption("cache-ir-stubs")) {
12450 if (strcmp(str
, "on") == 0) {
12451 jit::JitOptions
.disableCacheIR
= false;
12452 } else if (strcmp(str
, "off") == 0) {
12453 jit::JitOptions
.disableCacheIR
= true;
12455 return OptionFailure("cache-ir-stubs", str
);
12459 if (const char* str
= op
.getStringOption("spectre-mitigations")) {
12460 if (strcmp(str
, "on") == 0) {
12461 jit::JitOptions
.spectreIndexMasking
= true;
12462 jit::JitOptions
.spectreObjectMitigations
= true;
12463 jit::JitOptions
.spectreStringMitigations
= true;
12464 jit::JitOptions
.spectreValueMasking
= true;
12465 jit::JitOptions
.spectreJitToCxxCalls
= true;
12466 } else if (strcmp(str
, "off") == 0) {
12467 jit::JitOptions
.spectreIndexMasking
= false;
12468 jit::JitOptions
.spectreObjectMitigations
= false;
12469 jit::JitOptions
.spectreStringMitigations
= false;
12470 jit::JitOptions
.spectreValueMasking
= false;
12471 jit::JitOptions
.spectreJitToCxxCalls
= false;
12473 return OptionFailure("spectre-mitigations", str
);
12477 if (const char* str
= op
.getStringOption("write-protect-code")) {
12478 if (strcmp(str
, "on") == 0) {
12479 jit::JitOptions
.maybeSetWriteProtectCode(true);
12480 } else if (strcmp(str
, "off") == 0) {
12481 jit::JitOptions
.maybeSetWriteProtectCode(false);
12483 return OptionFailure("write-protect-code", str
);
12487 if (const char* str
= op
.getStringOption("monomorphic-inlining")) {
12488 if (strcmp(str
, "default") == 0) {
12489 jit::JitOptions
.monomorphicInlining
=
12490 jit::UseMonomorphicInlining::Default
;
12491 } else if (strcmp(str
, "always") == 0) {
12492 jit::JitOptions
.monomorphicInlining
= jit::UseMonomorphicInlining::Always
;
12493 } else if (strcmp(str
, "never") == 0) {
12494 jit::JitOptions
.monomorphicInlining
= jit::UseMonomorphicInlining::Never
;
12496 return OptionFailure("monomorphic-inlining", str
);
12500 if (const char* str
= op
.getStringOption("ion-scalar-replacement")) {
12501 if (strcmp(str
, "on") == 0) {
12502 jit::JitOptions
.disableScalarReplacement
= false;
12503 } else if (strcmp(str
, "off") == 0) {
12504 jit::JitOptions
.disableScalarReplacement
= true;
12506 return OptionFailure("ion-scalar-replacement", str
);
12510 if (op
.getStringOption("ion-shared-stubs")) {
12511 // Dead option, preserved for now for potential fuzzer interaction.
12514 if (const char* str
= op
.getStringOption("ion-gvn")) {
12515 if (strcmp(str
, "off") == 0) {
12516 jit::JitOptions
.disableGvn
= true;
12517 } else if (strcmp(str
, "on") != 0 && strcmp(str
, "optimistic") != 0 &&
12518 strcmp(str
, "pessimistic") != 0) {
12519 // We accept "pessimistic" and "optimistic" as synonyms for "on"
12520 // for backwards compatibility.
12521 return OptionFailure("ion-gvn", str
);
12525 if (const char* str
= op
.getStringOption("ion-licm")) {
12526 if (strcmp(str
, "on") == 0) {
12527 jit::JitOptions
.disableLicm
= false;
12528 } else if (strcmp(str
, "off") == 0) {
12529 jit::JitOptions
.disableLicm
= true;
12531 return OptionFailure("ion-licm", str
);
12535 if (const char* str
= op
.getStringOption("ion-edgecase-analysis")) {
12536 if (strcmp(str
, "on") == 0) {
12537 jit::JitOptions
.disableEdgeCaseAnalysis
= false;
12538 } else if (strcmp(str
, "off") == 0) {
12539 jit::JitOptions
.disableEdgeCaseAnalysis
= true;
12541 return OptionFailure("ion-edgecase-analysis", str
);
12545 if (const char* str
= op
.getStringOption("ion-pruning")) {
12546 if (strcmp(str
, "on") == 0) {
12547 jit::JitOptions
.disablePruning
= false;
12548 } else if (strcmp(str
, "off") == 0) {
12549 jit::JitOptions
.disablePruning
= true;
12551 return OptionFailure("ion-pruning", str
);
12555 if (const char* str
= op
.getStringOption("ion-range-analysis")) {
12556 if (strcmp(str
, "on") == 0) {
12557 jit::JitOptions
.disableRangeAnalysis
= false;
12558 } else if (strcmp(str
, "off") == 0) {
12559 jit::JitOptions
.disableRangeAnalysis
= true;
12561 return OptionFailure("ion-range-analysis", str
);
12565 if (const char* str
= op
.getStringOption("ion-sink")) {
12566 if (strcmp(str
, "on") == 0) {
12567 jit::JitOptions
.disableSink
= false;
12568 } else if (strcmp(str
, "off") == 0) {
12569 jit::JitOptions
.disableSink
= true;
12571 return OptionFailure("ion-sink", str
);
12575 if (const char* str
= op
.getStringOption("ion-optimize-shapeguards")) {
12576 if (strcmp(str
, "on") == 0) {
12577 jit::JitOptions
.disableRedundantShapeGuards
= false;
12578 } else if (strcmp(str
, "off") == 0) {
12579 jit::JitOptions
.disableRedundantShapeGuards
= true;
12581 return OptionFailure("ion-optimize-shapeguards", str
);
12585 if (const char* str
= op
.getStringOption("ion-optimize-gcbarriers")) {
12586 if (strcmp(str
, "on") == 0) {
12587 jit::JitOptions
.disableRedundantGCBarriers
= false;
12588 } else if (strcmp(str
, "off") == 0) {
12589 jit::JitOptions
.disableRedundantGCBarriers
= true;
12591 return OptionFailure("ion-optimize-gcbarriers", str
);
12595 if (const char* str
= op
.getStringOption("ion-instruction-reordering")) {
12596 if (strcmp(str
, "on") == 0) {
12597 jit::JitOptions
.disableInstructionReordering
= false;
12598 } else if (strcmp(str
, "off") == 0) {
12599 jit::JitOptions
.disableInstructionReordering
= true;
12601 return OptionFailure("ion-instruction-reordering", str
);
12605 if (op
.getBoolOption("ion-check-range-analysis")) {
12606 jit::JitOptions
.checkRangeAnalysis
= true;
12609 if (op
.getBoolOption("ion-extra-checks")) {
12610 jit::JitOptions
.runExtraChecks
= true;
12613 if (const char* str
= op
.getStringOption("ion-inlining")) {
12614 if (strcmp(str
, "on") == 0) {
12615 jit::JitOptions
.disableInlining
= false;
12616 } else if (strcmp(str
, "off") == 0) {
12617 jit::JitOptions
.disableInlining
= true;
12619 return OptionFailure("ion-inlining", str
);
12623 if (const char* str
= op
.getStringOption("ion-osr")) {
12624 if (strcmp(str
, "on") == 0) {
12625 jit::JitOptions
.osr
= true;
12626 } else if (strcmp(str
, "off") == 0) {
12627 jit::JitOptions
.osr
= false;
12629 return OptionFailure("ion-osr", str
);
12633 if (const char* str
= op
.getStringOption("ion-limit-script-size")) {
12634 if (strcmp(str
, "on") == 0) {
12635 jit::JitOptions
.limitScriptSize
= true;
12636 } else if (strcmp(str
, "off") == 0) {
12637 jit::JitOptions
.limitScriptSize
= false;
12639 return OptionFailure("ion-limit-script-size", str
);
12643 int32_t warmUpThreshold
= op
.getIntOption("ion-warmup-threshold");
12644 if (warmUpThreshold
>= 0) {
12645 jit::JitOptions
.setNormalIonWarmUpThreshold(warmUpThreshold
);
12648 warmUpThreshold
= op
.getIntOption("baseline-warmup-threshold");
12649 if (warmUpThreshold
>= 0) {
12650 jit::JitOptions
.baselineJitWarmUpThreshold
= warmUpThreshold
;
12653 warmUpThreshold
= op
.getIntOption("trial-inlining-warmup-threshold");
12654 if (warmUpThreshold
>= 0) {
12655 jit::JitOptions
.trialInliningWarmUpThreshold
= warmUpThreshold
;
12658 warmUpThreshold
= op
.getIntOption("regexp-warmup-threshold");
12659 if (warmUpThreshold
>= 0) {
12660 jit::JitOptions
.regexpWarmUpThreshold
= warmUpThreshold
;
12663 if (op
.getBoolOption("baseline-eager")) {
12664 jit::JitOptions
.setEagerBaselineCompilation();
12667 #ifdef ENABLE_PORTABLE_BASELINE_INTERP
12668 if (op
.getBoolOption("portable-baseline-eager")) {
12669 jit::JitOptions
.setEagerPortableBaselineInterpreter();
12671 if (op
.getBoolOption("portable-baseline")) {
12672 jit::JitOptions
.portableBaselineInterpreter
= true;
12674 if (op
.getBoolOption("no-portable-baseline")) {
12675 jit::JitOptions
.portableBaselineInterpreter
= false;
12679 if (op
.getBoolOption("blinterp")) {
12680 jit::JitOptions
.baselineInterpreter
= true;
12683 if (op
.getBoolOption("no-blinterp")) {
12684 jit::JitOptions
.baselineInterpreter
= false;
12687 if (op
.getBoolOption("disable-jithints")) {
12688 jit::JitOptions
.disableJitHints
= true;
12691 if (op
.getBoolOption("emit-interpreter-entry")) {
12692 jit::JitOptions
.emitInterpreterEntryTrampoline
= true;
12695 if (op
.getBoolOption("no-emit-interpreter-entry")) {
12696 jit::JitOptions
.emitInterpreterEntryTrampoline
= false;
12699 warmUpThreshold
= op
.getIntOption("blinterp-warmup-threshold");
12700 if (warmUpThreshold
>= 0) {
12701 jit::JitOptions
.baselineInterpreterWarmUpThreshold
= warmUpThreshold
;
12704 if (op
.getBoolOption("blinterp-eager")) {
12705 jit::JitOptions
.baselineInterpreterWarmUpThreshold
= 0;
12708 if (op
.getBoolOption("no-baseline")) {
12709 jit::JitOptions
.baselineJit
= false;
12712 if (op
.getBoolOption("no-ion")) {
12713 jit::JitOptions
.ion
= false;
12716 if (op
.getBoolOption("no-native-regexp")) {
12717 jit::JitOptions
.nativeRegExp
= false;
12720 if (op
.getBoolOption("trace-regexp-parser")) {
12721 jit::JitOptions
.trace_regexp_parser
= true;
12723 if (op
.getBoolOption("trace-regexp-assembler")) {
12724 jit::JitOptions
.trace_regexp_assembler
= true;
12726 if (op
.getBoolOption("trace-regexp-interpreter")) {
12727 jit::JitOptions
.trace_regexp_bytecodes
= true;
12729 if (op
.getBoolOption("trace-regexp-peephole")) {
12730 jit::JitOptions
.trace_regexp_peephole_optimization
= true;
12733 if (op
.getBoolOption("less-debug-code")) {
12734 jit::JitOptions
.lessDebugCode
= true;
12737 int32_t inliningEntryThreshold
= op
.getIntOption("inlining-entry-threshold");
12738 if (inliningEntryThreshold
> 0) {
12739 jit::JitOptions
.inliningEntryThreshold
= inliningEntryThreshold
;
12742 int32_t smallFunctionLength
= op
.getIntOption("small-function-length");
12743 if (smallFunctionLength
> 0) {
12744 jit::JitOptions
.smallFunctionMaxBytecodeLength
= smallFunctionLength
;
12747 if (const char* str
= op
.getStringOption("ion-regalloc")) {
12748 jit::JitOptions
.forcedRegisterAllocator
= jit::LookupRegisterAllocator(str
);
12749 if (!jit::JitOptions
.forcedRegisterAllocator
.isSome()) {
12750 return OptionFailure("ion-regalloc", str
);
12754 if (op
.getBoolOption("ion-eager")) {
12755 jit::JitOptions
.setEagerIonCompilation();
12758 offthreadCompilation
= true;
12759 if (const char* str
= op
.getStringOption("ion-offthread-compile")) {
12760 if (strcmp(str
, "off") == 0) {
12761 offthreadCompilation
= false;
12762 } else if (strcmp(str
, "on") != 0) {
12763 return OptionFailure("ion-offthread-compile", str
);
12766 cx
->runtime()->setOffthreadIonCompilationEnabled(offthreadCompilation
);
12768 if (op
.getStringOption("ion-parallel-compile")) {
12770 "--ion-parallel-compile is deprecated. Please use "
12771 "--ion-offthread-compile instead.\n");
12775 if (op
.getBoolOption("disable-bailout-loop-check")) {
12776 jit::JitOptions
.disableBailoutLoopCheck
= true;
12779 if (op
.getBoolOption("only-inline-selfhosted")) {
12780 jit::JitOptions
.onlyInlineSelfHosted
= true;
12783 if (op
.getBoolOption("enable-ic-frame-pointers")) {
12784 jit::JitOptions
.enableICFramePointers
= true;
12787 if (const char* str
= op
.getStringOption("ion-iterator-indices")) {
12788 if (strcmp(str
, "on") == 0) {
12789 jit::JitOptions
.disableIteratorIndices
= false;
12790 } else if (strcmp(str
, "off") == 0) {
12791 jit::JitOptions
.disableIteratorIndices
= true;
12793 return OptionFailure("ion-iterator-indices", str
);
12797 if (const char* str
= op
.getStringOption("ion-load-keys")) {
12798 if (strcmp(str
, "on") == 0) {
12799 jit::JitOptions
.disableMarkLoadsUsedAsPropertyKeys
= false;
12800 } else if (strcmp(str
, "off") == 0) {
12801 jit::JitOptions
.disableMarkLoadsUsedAsPropertyKeys
= true;
12803 return OptionFailure("ion-load-keys", str
);
12807 #if defined(JS_SIMULATOR_ARM)
12808 if (op
.getBoolOption("arm-sim-icache-checks")) {
12809 jit::SimulatorProcess::ICacheCheckingDisableCount
= 0;
12812 int32_t stopAt
= op
.getIntOption("arm-sim-stop-at");
12814 jit::Simulator::StopSimAt
= stopAt
;
12816 #elif defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
12817 if (op
.getBoolOption("mips-sim-icache-checks")) {
12818 jit::SimulatorProcess::ICacheCheckingDisableCount
= 0;
12821 int32_t stopAt
= op
.getIntOption("mips-sim-stop-at");
12823 jit::Simulator::StopSimAt
= stopAt
;
12825 #elif defined(JS_SIMULATOR_LOONG64)
12826 if (op
.getBoolOption("loong64-sim-icache-checks")) {
12827 jit::SimulatorProcess::ICacheCheckingDisableCount
= 0;
12830 int32_t stopAt
= op
.getIntOption("loong64-sim-stop-at");
12832 jit::Simulator::StopSimAt
= stopAt
;
12837 # ifdef JS_CODEGEN_RISCV64
12838 if (op
.getBoolOption("riscv-debug")) {
12839 jit::Assembler::FLAG_riscv_debug
= true;
12842 # ifdef JS_SIMULATOR_RISCV64
12843 if (op
.getBoolOption("trace-sim")) {
12844 jit::Simulator::FLAG_trace_sim
= true;
12846 if (op
.getBoolOption("debug-sim")) {
12847 jit::Simulator::FLAG_debug_sim
= true;
12849 if (op
.getBoolOption("riscv-trap-to-simulator-debugger")) {
12850 jit::Simulator::FLAG_riscv_trap_to_simulator_debugger
= true;
12852 int32_t stopAt
= op
.getIntOption("riscv-sim-stop-at");
12854 jit::Simulator::StopSimAt
= stopAt
;
12862 bool SetContextGCOptions(JSContext
* cx
, const OptionParser
& op
) {
12863 JS_SetGCParameter(cx
, JSGC_MAX_BYTES
, 0xffffffff);
12865 size_t nurseryBytes
= op
.getIntOption("nursery-size") * 1024L * 1024L;
12866 if (nurseryBytes
== 0) {
12867 fprintf(stderr
, "Error: --nursery-size parameter must be non-zero.\n");
12869 "The nursery can be disabled by passing the --no-ggc option.\n");
12872 JS_SetGCParameter(cx
, JSGC_MAX_NURSERY_BYTES
, nurseryBytes
);
12874 size_t availMemMB
= op
.getIntOption("available-memory");
12875 if (availMemMB
> 0) {
12876 JS_SetGCParametersBasedOnAvailableMemory(cx
, availMemMB
);
12879 if (const char* opt
= op
.getStringOption("nursery-strings")) {
12880 if (strcmp(opt
, "on") == 0) {
12881 cx
->runtime()->gc
.nursery().enableStrings();
12882 } else if (strcmp(opt
, "off") == 0) {
12883 cx
->runtime()->gc
.nursery().disableStrings();
12885 MOZ_CRASH("invalid option value for --nursery-strings, must be on/off");
12889 if (const char* opt
= op
.getStringOption("nursery-bigints")) {
12890 if (strcmp(opt
, "on") == 0) {
12891 cx
->runtime()->gc
.nursery().enableBigInts();
12892 } else if (strcmp(opt
, "off") == 0) {
12893 cx
->runtime()->gc
.nursery().disableBigInts();
12895 MOZ_CRASH("invalid option value for --nursery-bigints, must be on/off");
12899 bool incrementalGC
= !op
.getBoolOption("no-incremental-gc");
12900 JS_SetGCParameter(cx
, JSGC_INCREMENTAL_GC_ENABLED
, incrementalGC
);
12903 bool parallelMarking
= true;
12905 bool parallelMarking
= false;
12907 if (op
.getBoolOption("enable-parallel-marking")) {
12908 parallelMarking
= true;
12910 if (op
.getBoolOption("no-parallel-marking")) {
12911 parallelMarking
= false;
12913 JS_SetGCParameter(cx
, JSGC_PARALLEL_MARKING_ENABLED
, parallelMarking
);
12915 int32_t markingThreads
= op
.getIntOption("marking-threads");
12916 if (markingThreads
> 0) {
12917 JS_SetGCParameter(cx
, JSGC_MARKING_THREAD_COUNT
, markingThreads
);
12920 JS_SetGCParameter(cx
, JSGC_SLICE_TIME_BUDGET_MS
, 5);
12922 JS_SetGCParameter(cx
, JSGC_PER_ZONE_GC_ENABLED
, true);
12924 for (MultiStringRange args
= op
.getMultiStringOption("gc-param");
12925 !args
.empty(); args
.popFront()) {
12926 if (!SetGCParameterFromArg(cx
, args
.front())) {
12932 dumpEntrainedVariables
= op
.getBoolOption("dump-entrained-variables");
12936 const char* zealStr
= op
.getStringOption("gc-zeal");
12938 if (!cx
->runtime()->gc
.parseAndSetZeal(zealStr
)) {
12941 uint32_t nextScheduled
;
12942 cx
->runtime()->gc
.getZealBits(&gZealBits
, &gZealFrequency
, &nextScheduled
);
12949 bool InitModuleLoader(JSContext
* cx
, const OptionParser
& op
) {
12950 RootedString
moduleLoadPath(cx
);
12951 if (const char* option
= op
.getStringOption("module-load-path")) {
12952 UniqueChars pathUtf8
= JS::EncodeNarrowToUtf8(cx
, option
);
12957 Rooted
<JSString
*> jspath(cx
, NewStringCopyUTF8(cx
, pathUtf8
.get()));
12962 moduleLoadPath
= js::shell::ResolvePath(cx
, jspath
, RootRelative
);
12964 processWideModuleLoadPath
= JS_EncodeStringToUTF8(cx
, moduleLoadPath
);
12965 if (!processWideModuleLoadPath
) {
12969 processWideModuleLoadPath
= js::shell::GetCWD(cx
);
12970 if (!processWideModuleLoadPath
) {
12974 moduleLoadPath
= NewStringCopyUTF8(cx
, processWideModuleLoadPath
.get());
12975 if (!moduleLoadPath
) {
12980 ShellContext
* sc
= GetShellContext(cx
);
12981 sc
->moduleLoader
= js::MakeUnique
<ModuleLoader
>();
12982 if (!sc
->moduleLoader
|| !sc
->moduleLoader
->init(cx
, moduleLoadPath
)) {